1. Spring Aware
Spring的依赖注入的最大亮点就是你所有的Bean对Spring容器的存在是没有意识的。即你可以将你的容器替换成别的容器。
实际项目中,不可避免地会用到Spring容器本身的功能资源,这时的Bean必须意识到Spring容器的存在,才能调用Spring所提供的资源,这就是所谓的Spring Aware。
Spring提供的Aware接口如下:
BeanNameAware | 获取到容器中Bean的名称 |
BeanFactoryAware | 获得当前bean factory,这样可以调用容器的服务 |
ApplicationContextAware | 当前的Applicaion context, 这样可以调用容器的服务 |
MessageSourceAware | 获得message source,这样可以获得文本信息 |
ApplicationEventPublisher | 应用事件发布器,可以发布事件 |
ResourceLoaderAware | 获得资源加载器,可以获得外部资源文件 |
Spring Aware的目的是为了让Bean获得Spring容器的服务。
示例:
1) 创建一个test.txt,内容随意
2) Spring Aware演示Bean
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.aware; 2 3 import java.io.IOException; 4 5 import org.apache.commons.io.IOUtils; 6 import org.springframework.beans.factory.BeanNameAware; 7 import org.springframework.context.ResourceLoaderAware; 8 import org.springframework.core.io.Resource; 9 import org.springframework.core.io.ResourceLoader; 10 import org.springframework.stereotype.Service; 11 12 // 实现BeanNameAware、ResourceLoaderAware接口,获得Bean名称和资源加载的服务 13 @Service 14 public class AwareService implements BeanNameAware, ResourceLoaderAware{ 15 16 private String beanName; 17 private ResourceLoader loader; 18 19 // 实现ResourceLoaderAware需要重写setResourceLoader 20 public void setResourceLoader(ResourceLoader resourceLoader) { 21 this.loader = resourceLoader; 22 } 23 24 // 实现BeanNameAware需重写setBeanName方法 25 public void setBeanName(String name) { 26 this.beanName = name; 27 } 28 29 public void outputResult(){ 30 System.out.println("Bean的名称为:"+beanName); 31 Resource resource = loader.getResource("classpath:com/ws/study/aware/test.txt"); 32 try { 33 System.out.println("ResourceLoader加载的文件内容为:" 34 +IOUtils.toString(resource.getInputStream())); 35 } catch (IOException e) { 36 e.printStackTrace(); 37 } 38 } 39 }
3) 配置类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.aware; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.stereotype.Component; 5 6 @Component 7 @ComponentScan("com.ws.study.aware") 8 public class AwareConfig { 9 } 10
4) 运行类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.aware; 2 3 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 4 5 public class Main { 6 public static void main(String[] args) { 7 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AwareConfig.class); 8 AwareService awareService = context.getBean(AwareService.class); 9 awareService.outputResult(); 10 context.close(); 11 } 12 } 13
5) 运行结果
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 六月 03, 2018 10:56:12 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 2 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@dbe50f: startup date [Sun Jun 03 22:56:12 CST 2018]; root of context hierarchy 3 Bean的名称为:awareService 4 ResourceLoader加载的文件内容为:Hello Spring! 5 六月 03, 2018 10:57:24 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose 6 信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@dbe50f: startup date [Sun Jun 03 22:56:12 CST 2018]; root of context hierarchy 7
2. 多线程
Spring通过任务执行器TaskExecutor来实现多线程和并发编程。使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。实际开发中,任务一般是异步的,所以在配置类中通过@EnableAsync开启对异步任务的支持,并通过在实际执行的Bean的方法中使用@Async注解来声明异步任务。
示例:
1) 配置类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.taskexecutor; 2 3 import java.util.concurrent.Executor; 4 5 import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; 6 import org.springframework.context.annotation.ComponentScan; 7 import org.springframework.scheduling.annotation.AsyncConfigurer; 8 import org.springframework.scheduling.annotation.EnableAsync; 9 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 10 import org.springframework.stereotype.Component; 11 12 @Component 13 @ComponentScan("com.ws.study.taskexecutor") 14 // 利用@EnableAysnc注解开启异步任务支持 15 @EnableAsync 16 public class TaskExecutorConfig implements AsyncConfigurer{ 17 18 // 配置类实现AsyncConfigure接口并重写getAsyncExecutor方法, 19 // 并返回一个ThreadPoolTaskExecutor 20 public Executor getAsyncExecutor() { 21 ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); 22 taskExecutor.setCorePoolSize(5); 23 taskExecutor.setMaxPoolSize(10); 24 taskExecutor.setQueueCapacity(25); 25 taskExecutor.initialize(); 26 return taskExecutor; 27 } 28 29 public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { 30 return null; 31 } 32 33 } 34
2) 任务执行类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.taskexecutor; 2 3 import org.springframework.scheduling.annotation.Async; 4 import org.springframework.stereotype.Service; 5 6 @Service 7 public class AsyncTaskService { 8 9 // 通过@Async注解表明该方法是个异步方法,如果注解在类级别,则表明该类所有的方法都是异步方法 10 // 而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor 11 @Async 12 public void executeAsyncTask(Integer number){ 13 System.out.println("执行异步任务: "+number); 14 } 15 16 @Async 17 public void executeAsyncTaskPlus(Integer number){ 18 System.out.println("异步执行任务+1: "+(number+1)); 19 } 20 } 21
3) 运行类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.taskexecutor; 2 3 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 4 5 public class Main { 6 public static void main(String[] args) { 7 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskExecutorConfig.class); 8 AsyncTaskService asyncTaskService = context.getBean(AsyncTaskService.class); 9 for(int i = 0; i < 10; i++){ 10 asyncTaskService.executeAsyncTask(i); 11 asyncTaskService.executeAsyncTaskPlus(i); 12 } 13 context.close(); 14 } 15 } 16
4) 运行结果:结果是并发执行而不是顺序执行
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 六月 03, 2018 11:17:41 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 2 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3df479: startup date [Sun Jun 03 23:17:41 CST 2018]; root of context hierarchy 3 六月 03, 2018 11:17:41 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization 4 信息: Bean 'taskExecutorConfig' of type [class com.ws.study.taskexecutor.TaskExecutorConfig] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 5 六月 03, 2018 11:17:41 下午 org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor initialize 6 信息: Initializing ExecutorService 7 六月 03, 2018 11:17:41 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization 8 信息: Bean 'org.springframework.scheduling.annotation.ProxyAsyncConfiguration' of type [class org.springframework.scheduling.annotation.ProxyAsyncConfiguration$$EnhancerBySpringCGLIB$$c683b4d7] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 9 六月 03, 2018 11:17:41 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose 10 信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3df479: startup date [Sun Jun 03 23:17:41 CST 2018]; root of context hierarchy 11 执行异步任务: 0 12 异步执行任务+1: 3 13 执行异步任务: 3 14 异步执行任务+1: 4 15 执行异步任务: 4 16 异步执行任务+1: 5 17 执行异步任务: 5 18 异步执行任务+1: 6 19 执行异步任务: 6 20 异步执行任务+1: 7 21 执行异步任务: 7 22 异步执行任务+1: 8 23 执行异步任务: 8 24 异步执行任务+1: 9 25 执行异步任务: 9 26 异步执行任务+1: 10 27 异步执行任务+1: 1 28 异步执行任务+1: 2 29 执行异步任务: 1 30 执行异步任务: 2 31
3. 计划任务
计划任务首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是一个计划任务。通过@Scheduled支持多种类型的计划任务,包含cron, fixDelay, fixRate等
示例:
1) 计划任务执行类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.taskscheduler; 2 3 import java.text.SimpleDateFormat; 4 import java.util.Date; 5 6 import org.springframework.scheduling.annotation.Scheduled; 7 import org.springframework.stereotype.Service; 8 9 @Service 10 public class ScheduledTaskService { 11 private static final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss"); 12 13 // 通过@Scheduled声明该方法是计划任务,使用fixRate属性每隔固定时间执行 14 @Scheduled(fixedRate = 5000) 15 public void reportCurrentTime(){ 16 System.out.println("每隔五秒执行一次 "+format.format(new Date())); 17 } 18 19 // 使用cron属性可按照指定时间执行,本例指定每天22点25分执行, cron是Linux系统下的定时任务 20 @Scheduled(cron = "0 24 22 ? * *") 21 public void fixTimeExecution(){ 22 System.out.println("在指定时间 "+format.format(new Date()) + "执行"); 23 } 24 } 25
2) 配置类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.taskscheduler; 2 3 import org.springframework.context.annotation.ComponentScan; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.scheduling.annotation.EnableScheduling; 6 7 @Configuration 8 @ComponentScan("com.ws.study.taskscheduler") 9 // 通过@EnableScheduling注解开启对计划任务的支持 10 @EnableScheduling 11 public class TaskSchedulerConfig { 12 } 13
3) 运行类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.taskscheduler; 2 3 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 4 5 public class Main { 6 public static void main(String[] args) { 7 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskSchedulerConfig.class); 8 } 9 } 10
4) 执行结果
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 六月 07, 2018 10:23:37 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 2 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Thu Jun 07 22:23:37 CST 2018]; root of context hierarchy 3 六月 07, 2018 10:23:38 下午 org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker postProcessAfterInitialization 4 信息: Bean 'org.springframework.scheduling.annotation.SchedulingConfiguration' of type [class org.springframework.scheduling.annotation.SchedulingConfiguration$$EnhancerBySpringCGLIB$$83a8b643] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 5 每隔五秒执行一次 22:23:38 6 每隔五秒执行一次 22:23:43 7 每隔五秒执行一次 22:23:48 8 每隔五秒执行一次 22:23:53 9 每隔五秒执行一次 22:23:58 10 在指定时间 22:24:00执行 11 每隔五秒执行一次 22:24:03 12 每隔五秒执行一次 22:24:08 13
4. 条件注解@Conditional
@Conditional根据满足某一个特定条件创建一个特定的Bean。即根据特定条件来控制Bean的创建行为,这样可以利用这个特性进行一些自动的配置。
示例:
以不同的OS为例,通过实现Condition接口,并重写matcher方法来构造判断条件,若在windows系统下,则输出dir,若在linux下,则输出ls
1) 判断条件定义之判定Windows的条件
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.conditional; 2 3 import org.springframework.context.annotation.Condition; 4 import org.springframework.context.annotation.ConditionContext; 5 import org.springframework.core.type.AnnotatedTypeMetadata; 6 7 public class WindowsCondition implements Condition{ 8 9 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 10 return context.getEnvironment().getProperty("os.name").contains("Windows"); 11 } 12 13 } 14
2) 判定Linux条件之判定Linux的条件
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.conditional; 2 3 import org.springframework.context.annotation.Condition; 4 import org.springframework.context.annotation.ConditionContext; 5 import org.springframework.core.type.AnnotatedTypeMetadata; 6 7 public class LinuxCondition implements Condition{ 8 9 public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 10 return context.getEnvironment().getProperty("os.name").contains("Linux"); 11 } 12 }
3) 不同OS下Bean类之接口
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.conditional; 2 3 public interface ListService { 4 String showListCmd(); 5 } 6
4) Windows下创建的Bean类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.conditional; 2 3 public class WindowsListService implements ListService{ 4 public String showListCmd() { 5 return "dir"; 6 } 7 } 8
5) Linux下创建的Bean类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.conditional; 2 3 public class LinuxListService implements ListService{ 4 5 public String showListCmd() { 6 return "ls"; 7 } 8 } 9
6) 配置类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.conditional; 2 3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Conditional; 6 import org.springframework.context.annotation.Configuration; 7 8 @Configuration 9 @ComponentScan("com.ws.study.conditional") 10 public class ConditionConfig { 11 12 @Bean 13 // 通过@Conditional注解,符合Windows条件则实例化windowsListService 14 @Conditional(WindowsCondition.class) 15 public ListService windowsListService(){ 16 return new WindowsListService(); 17 } 18 19 @Bean 20 // 通过@Conditional注解,符合Linux条件则实例化linuxListService 21 @Conditional(LinuxCondition.class) 22 public ListService linuxListService(){ 23 return new LinuxListService(); 24 } 25 } 26
7) 运行类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.conditional; 2 3 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 4 5 public class Main { 6 public static void main(String[] args) { 7 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionConfig.class); 8 9 ListService listService = context.getBean(ListService.class); 10 11 System.out.println(context.getEnvironment().getProperty("os.name") 12 + "系统下的命令为: "+listService.showListCmd()); 13 14 context.close(); 15 } 16 } 17
8) 运行结果
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 六月 07, 2018 10:48:27 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 2 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Thu Jun 07 22:48:27 CST 2018]; root of context hierarchy 3 六月 07, 2018 10:48:28 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose 4 信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Thu Jun 07 22:48:27 CST 2018]; root of context hierarchy 5 Windows 7系统下的命令为: dir 6
5. 组合注解与元注解
所谓元注解就是可以注解到别的注解上的注解,被注解的注解称之为组合注解,组合注解具备元注解的功能。Spring本身已经有很多组合注解,如@Configuration就是一个组合@Component注解,表明这个类其实也是一个Bean。
示例:
1) 组合注解示例
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.annotation; 2 3 import java.lang.annotation.Documented; 4 import java.lang.annotation.ElementType; 5 import java.lang.annotation.Retention; 6 import java.lang.annotation.RetentionPolicy; 7 import java.lang.annotation.Target; 8 9 import org.springframework.context.annotation.ComponentScan; 10 import org.springframework.context.annotation.Configuration; 11 12 @Target(ElementType.TYPE) 13 @Retention(RetentionPolicy.RUNTIME) 14 @Documented 15 // 组合@Configuration元注解 16 @Configuration 17 // 组合@ComponentScan元注解 18 @ComponentScan 19 public @interface WiselyConfiguration { 20 // 覆盖value参数 21 String[] value() default {}; 22 }
2) 演示服务Bean
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.annotation; 2 3 import org.springframework.stereotype.Service; 4 5 @Service 6 public class DemoService { 7 public void output(){ 8 System.out.println("从组合注解配置中仍然可以获得Bean"); 9 } 10 } 11
3) 组合注解配置类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.annotation; 2 3 // 使用@WiselyConfiguration组合注解替代@Configuration和@ComponentScan 4 @WiselyConfiguration("com.ws.study.annotation") 5 public class DemoConfig { 6 } 7
4) 运行类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.annotation; 2 3 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 4 5 public class Main { 6 public static void main(String[] args) { 7 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class); 8 DemoService service = context.getBean(DemoService.class); 9 service.output(); 10 context.close(); 11 } 12 } 13
5) 运行结果
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 六月 12, 2018 11:11:37 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh 2 信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Tue Jun 12 23:11:37 CST 2018]; root of context hierarchy 3 六月 12, 2018 11:11:39 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose 4 信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1a71e93: startup date [Tue Jun 12 23:11:37 CST 2018]; root of context hierarchy 5 从组合注解配置中仍然可以获得Bean 6
6. @Enable*注解的工作原理
@EnalbeAspectAutoProxy开启对AspectJ自动代理的支持;@EnableAsync开启异步方法的支持等@Enable*注解可以开启一项功能,从而避免自己配置大量的代码。
所有的@Enable*注解,都包含了一个@Import注解,用于导入配置类,意味着这些自动开启的实现其实是导入了一些自动配置的Bean。这些导入方式主要分为三种类型:
1) 直接导入配置类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Import(SchedulingConfiguration.class) 4 @Documented 5 public @interface EnableScheduling { 6 7 }
@EnableScheduling直接导入配置类SchedulingConfiguration,这个类注解了@Configuration,且注册了ScheduledAnnotationBeanPostProcessor的Bean
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 @Configuration 2 public class SchedulingConfiguration { 3 4 @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) 5 @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 6 public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() { 7 return new ScheduledAnnotationBeanPostProcessor(); 8 } 9 10 }
2) 依据条件选择配置类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Import(AsyncConfigurationSelector.class) 5 public @interface EnableAsync { 6 Class<? extends Annotation> annotation() default Annotation.class; 7 boolean proxyTargetClass() default false; 8 AdviceMode mode() default AdviceMode.PROXY; 9 int order() default Ordered.LOWEST_PRECEDENCE; 10 }
AsyncConfigurationSelector通过条件来选择需要导入的配置类,AsyncConfigurationSelector的根接口为ImportSelector,且重写了selectImports方法,在此方法内进行事先条件判断。此例中,若adviceMode为PORXY,则返回ProxyAsyncConfiguration配置类;若adviceMode为ASPECTJ,则返回AspectAsyncConfiguration配置类。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> { 2 3 private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = 4 "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"; 5 6 @Override 7 public String[] selectImports(AdviceMode adviceMode) { 8 switch (adviceMode) { 9 case PROXY: 10 return new String[] { ProxyAsyncConfiguration.class.getName() }; 11 case ASPECTJ: 12 return new String[] { ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME }; 13 default: 14 return null; 15 } 16 } 17 18 }
3) 动态注册Bean
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Import(AspectJAutoProxyRegistrar.class) 5 public @interface EnableAspectJAutoProxy { 6 7 boolean proxyTargetClass() default false; 8 9 }
AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,该接口的作用是在运行时自动添加Bean到已有的配置类,通过重写方法:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { 2 3 @Override 4 public void registerBeanDefinitions( 5 AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 6 7 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); 8 9 AnnotationAttributes enableAJAutoProxy = 10 AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); 11 if (enableAJAutoProxy.getBoolean("proxyTargetClass")) { 12 AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); 13 } 14 } 15 16 }
其中,AnnotationMetadata参数用于获得当前配置类上的注解,BeanDefinitionRegistry参数用于注册Bean
7. 测试
集成测试提供了一种无须部署或运行程序来完成验证系统各部分是否正常协同工作的能力。Spring提供了一个SpringJunit4ClassRunner类。通过@ContextConfiguration来配置Application Context,通过@ActiveProfiles确定活动的profile。
示例:
1) 增加Spring测试的依赖包
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 <dependency> 2 <groupId>org.springframework</groupId> 3 <artifactId>spring-test</artifactId> 4 <version>${spring-framework.version}</version> 5 </dependency> 6 <dependency> 7 <groupId>junit</groupId> 8 <artifactId>junit</artifactId> 9 <version>4.11</version> 10 <scope>test</scope> 11 </dependency>
2) 业务代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.fortest; 2 3 public class TestBean { 4 private String content; 5 6 7 8 public TestBean(String content) { 9 super(); 10 this.content = content; 11 } 12 13 public String getContent() { 14 return content; 15 } 16 17 public void setContent(String content) { 18 this.content = content; 19 } 20 21 22 } 23
3) 配置类
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study.fortest; 2 3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.Configuration; 5 import org.springframework.context.annotation.Profile; 6 7 @Configuration 8 public class TestConfig { 9 10 @Bean 11 @Profile("dev") 12 public TestBean devTestBean(){ 13 return new TestBean("from development profile"); 14 } 15 16 @Bean 17 @Profile("prod") 18 public TestBean prodTestBean(){ 19 return new TestBean("from production profile"); 20 } 21 } 22
4) 测试类,注意测试类写在src/test/java中
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package com.ws.study1; 2 3 import org.junit.Assert; 4 import org.junit.Test; 5 import org.junit.runner.RunWith; 6 import org.springframework.beans.factory.annotation.Autowired; 7 import org.springframework.test.context.ActiveProfiles; 8 import org.springframework.test.context.ContextConfiguration; 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 10 11 import com.ws.study.fortest.TestBean; 12 import com.ws.study.fortest.TestConfig; 13 14 // SpringJUnit4ClassRunner在JUnit环境下提供Spring Test Context Framework的功能 15 @RunWith(SpringJUnit4ClassRunner.class) 16 // @ContextConfiguration用来加载配置ApplicationContext,其中classes用来加载配置类 17 @ContextConfiguration(classes = {TestConfig.class}) 18 // @ActiveProfiles用于声明活动的profile 19 @ActiveProfiles("prod") 20 public class DemoBeanIntegrationTests { 21 22 // 可使用普通的@Autowired注入Bean 23 @Autowired 24 private TestBean testBean; 25 26 @Test 27 public void prodBeanShouldInject(){ 28 String expected = "from production profile"; 29 String actual = testBean.getContent(); 30 Assert.assertEquals(expected, actual); 31 } 32 } 33