Spring Aware
通常@Component標識的Bean由容器所管理,但它自身是不能和容器交互的(解耦),你只能通過@Autowired引入並使用。而Aware接口爲Bean提供了與容器交互的能力。
- BeanNameAware:獲取容器中Bean的名稱
- BeanFactoryAware:獲取當前Bean的Factory,從而調用容器的服務
- MessagerSourceAware:獲取文本信息
- ApplicationEventPublisherAware:應用事件發佈器,也可以通過AbstractApplicationContext的publish方法發佈
- ResourceLoaderAware:獲取資源加載器,可以獲取外部資源
- ApplicationContextAware:獲取上下文,包含了MessagerSource、EventPublisher、ResourceLoader
@Service
public class AwareService implements BeanNameAware, ResourceLoaderAware {
String beanName;
ResourceLoader resourceLoader;
@Override
public void setBeanName(String s) {
this.beanName = s;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
public void outputInfo() {
System.out.println("bean在容器中的名稱是:"+beanName);
Resource resource = resourceLoader.getResource("classpath:aware/test.txt");
try {
System.out.println("資源文件的內容是:"+IOUtils.toString(resource.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
多線程
- 註解需要異步執行(多線程)的方法
@Service
@Async //註解表明該方法是一個異步方法,註解在類上則表示類中所有方法都是異步的.
public class AsyncTaskService {
public void executeAsyncTask(Integer i) {
System.out.println("執行異步任務:" + i);
}
public void executeAsyncTaskPlus(Integer i) {
System.out.println("執行異步任務+1:" + (i + 1));
}
}
- 開啓異步執行的方法,並定義異步執行器
@Configuration
@ComponentScan("demo.springboot.taskexecutor")
@EnableAsync //使用@EnableAsync開啓異步任務,實現異步配置類返回一個基於線程池的執行器
public class ExecutorConfig implements AsyncConfigurer {
//重寫替換系統獲取的執行器
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.initialize();
return taskExecutor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ExecutorConfig.class);
AsyncTaskService asyncTaskService = context.getBean(AsyncTaskService.class);
/**
* 調用異步方法時,Spring會自動獲取內部的Async執行器來執行
* 執行的結果是異步的(通過多線程執行),而不是0-9順序的執行
*/
for (int i=0;i<10;i++){
asyncTaskService.executeAsyncTask(i);
asyncTaskService.executeAsyncTaskPlus(i);
}
context.close();
}
}
結果展示
執行異步任務+1:1
執行異步任務:1
執行異步任務+1:2
執行異步任務:3
執行異步任務+1:3
執行異步任務:0
執行異步任務:2
執行異步任務:5
執行異步任務+1:5
執行異步任務:4
執行異步任務+1:4
執行異步任務:7
執行異步任務+1:7
執行異步任務:6
執行異步任務+1:6
執行異步任務:9
執行異步任務+1:9
執行異步任務:8
執行異步任務+1:8
執行異步任務+1:10
計劃任務
現在使用註解就可以使用Spring的計劃任務模塊, 代替之前複雜繁瑣的quartz整合
@Service
public class SchedulerService {
private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
@Scheduled(fixedRate = 5000)
public void reportCurrentTime(){System.out.println("每5秒執行,當前時間:"+sdf.format(new Date()));}
@Scheduled(cron = "0 08 17 ? * *")
public void fixedTimeExecution(){System.out.println("定時執行:"+sdf.format(new Date()));}
}
@Configuration
@ComponentScan("demo.springboot.taskscheduler")
@EnableScheduling //註解開啓計劃任務
public class SchedulerConfig {
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SchedulerConfig.class);
}
}
條件註解
根據條件操控Bean, 比profile更通用, 可以定義在程序內方便理解
* 設置條件, 實現條件實現的函數
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Windows");
}
}
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return context.getEnvironment().getProperty("os.name").contains("Linux");
}
}
- 設置Bean的不同實現
public class WindowsServiceImpl implements ListService {
@Override
public String showListCmd() { return "dir"; }
}
public class LinuxServiceImpl implements ListService {
@Override
public String showListCmd() { return "ls"; }
}
- 設置Bean的加載條件
@Configuration
public class ConditionalConfig {
@Bean
@Conditional(WindowsCondition.class)
public ListService windowsList() { return new WindowsServiceImpl(); }
@Bean
@Conditional(LinuxCondition.class)
public ListService linuxList() { return new LinuxServiceImpl(); }
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionalConfig.class);
ListService listService = context.getBean(ListService.class);
System.out.println("查看系統列表的命令是:" + listService.showListCmd());
}
}
組合註解和元註解
自定義註解組合常用的註解(類似 繼承/組合)
@Target(ElementType.TYPE)/* Class, interface (including annotation type), or enum declaration */
@Retention(RetentionPolicy.RUNTIME)/* 保留至運行時 ,運行時可以檢測到這個註解 */
@Documented/* 被javadoc記錄 */
/* 組合註解類似繼承 ,可以使用同名函數(屬性) 覆蓋 被組合的註解中的函數(屬性) */
@Configuration
@ComponentScan
public @interface CommonConfiguration {
String[] value() default {};
}
使用組合註解
@CommonConfiguration("demo.springboot.annotation")
public class DemoConfig {
//...
}
@Enable*註解的原理
常用的註解
@org.springframework.context.annotation.EnableAspectJAutoProxy //aspectj支持
@org.springframework.scheduling.annotation.EnableAsync //開啓異步方法
@org.springframework.web.servlet.config.annotation.EnableWebMvc //開啓Mvc配置
@org.springframework.boot.context.properties.EnableConfigurationProperties //開啓註解配置bean
@org.springframework.cache.annotation.EnableCaching //開啓註解式緩存
@org.springframework.scheduling.annotation.EnableScheduling //開啓計劃任務
@EnableJpaRepositories //開啓JPA支持
@org.springframework.transaction.annotation.EnableTransactionManagement //開啓註解事務
原理解析
* 引入配置類@EnableScheduling
//註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)/* 引入配置類 */
@Documented
public @interface EnableScheduling {
}
// 配置類 ,定義了一個bean(等效於 xml中的<bean .../>)
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
- 依據條件選擇配置類@EnableAsync
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
//...
}
//配置類
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
//...
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] { ProxyAsyncConfiguration.class.getName() };
case ASPECTJ:
return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME };
default:
return null;
}
}
//...
}
- 動態註冊Bean@EnableAspectJAutoProxy
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
//...
}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, // 獲取配置類的註解
BeanDefinitionRegistry registry //註冊bean
) {
//...
}
}
測試
手動導入spring-test
或者spring-boot-starter-test
@RunWith(SpringJUnit4ClassRunner.class) //包含需要啓動的spring容器
@ContextConfiguration(classes = {DemoConfig.class}) //需要包含的配置
@ActiveProfiles("dev") //配合profile使用
public class DemoBeansIntegrationTests {
@Autowired
private DemoBean demoBean;
@Test //測試用例
public void contentTest() {
String content = demoBean.getContent();
Assert.assertEquals(content, "register from dev");
}
}