Spring Boot实战(3) Spring高级话题

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

  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 }
View Code

3) 配置类

  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 
View Code

4) 运行类

  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 
View Code

5) 运行结果

  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 
View Code

2. 多线程

Spring通过任务执行器TaskExecutor来实现多线程和并发编程。使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。实际开发中,任务一般是异步的,所以在配置类中通过@EnableAsync开启对异步任务的支持,并通过在实际执行的Bean的方法中使用@Async注解来声明异步任务。

示例:

1) 配置类

  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 
View Code

2) 任务执行类

  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 
View Code

3) 运行类

  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 
View Code

4) 运行结果:结果是并发执行而不是顺序执行

  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 
View Code

3. 计划任务

计划任务首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是一个计划任务。通过@Scheduled支持多种类型的计划任务,包含cron, fixDelay, fixRate等

示例:

1) 计划任务执行类

  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 
View Code

2)  配置类

  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 
View Code

3) 运行类

  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 
View Code

4) 执行结果

  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 
View Code

4. 条件注解@Conditional

@Conditional根据满足某一个特定条件创建一个特定的Bean。即根据特定条件来控制Bean的创建行为,这样可以利用这个特性进行一些自动的配置。

示例:

以不同的OS为例,通过实现Condition接口,并重写matcher方法来构造判断条件,若在windows系统下,则输出dir,若在linux下,则输出ls

1) 判断条件定义之判定Windows的条件

  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 
View Code

2) 判定Linux条件之判定Linux的条件

  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 }
View Code

3) 不同OS下Bean类之接口

  1 package com.ws.study.conditional;
  2 
  3 public interface ListService {
  4 	String showListCmd();
  5 }
  6 
View Code

4) Windows下创建的Bean类

  1 package com.ws.study.conditional;
  2 
  3 public class WindowsListService implements ListService{
  4 	public String showListCmd() {
  5 		return "dir";
  6 	}
  7 }
  8 
View Code

5) Linux下创建的Bean类

  1 package com.ws.study.conditional;
  2 
  3 public class LinuxListService implements ListService{
  4 
  5 	public String showListCmd() {
  6 		return "ls";
  7 	}
  8 }
  9 
View Code

6) 配置类

  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 
View Code

7) 运行类

  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 
View Code

8) 运行结果

  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 
View Code

5. 组合注解与元注解

所谓元注解就是可以注解到别的注解上的注解,被注解的注解称之为组合注解,组合注解具备元注解的功能。Spring本身已经有很多组合注解,如@Configuration就是一个组合@Component注解,表明这个类其实也是一个Bean。

示例:

1) 组合注解示例

  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 }
View Code

2) 演示服务Bean

  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 
View Code

3) 组合注解配置类

  1 package com.ws.study.annotation;
  2 
  3 // 使用@WiselyConfiguration组合注解替代@Configuration和@ComponentScan
  4 @WiselyConfiguration("com.ws.study.annotation")
  5 public class DemoConfig {
  6 }
  7 
View Code

4) 运行类

  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 
View Code

5) 运行结果

  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 
View Code

6. @Enable*注解的工作原理

@EnalbeAspectAutoProxy开启对AspectJ自动代理的支持;@EnableAsync开启异步方法的支持等@Enable*注解可以开启一项功能,从而避免自己配置大量的代码。

所有的@Enable*注解,都包含了一个@Import注解,用于导入配置类,意味着这些自动开启的实现其实是导入了一些自动配置的Bean。这些导入方式主要分为三种类型:

1) 直接导入配置类

  1 @Target(ElementType.TYPE)
  2 @Retention(RetentionPolicy.RUNTIME)
  3 @Import(SchedulingConfiguration.class)
  4 @Documented
  5 public @interface EnableScheduling {
  6 
  7 }
View Code

@EnableScheduling直接导入配置类SchedulingConfiguration,这个类注解了@Configuration,且注册了ScheduledAnnotationBeanPostProcessor的Bean

  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 }
View Code

2) 依据条件选择配置类

  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 }
View Code

AsyncConfigurationSelector通过条件来选择需要导入的配置类,AsyncConfigurationSelector的根接口为ImportSelector,且重写了selectImports方法,在此方法内进行事先条件判断。此例中,若adviceMode为PORXY,则返回ProxyAsyncConfiguration配置类;若adviceMode为ASPECTJ,则返回AspectAsyncConfiguration配置类。

  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 }
View Code

3) 动态注册Bean

  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 }
View Code

AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,该接口的作用是在运行时自动添加Bean到已有的配置类,通过重写方法:

  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 }
View Code

其中,AnnotationMetadata参数用于获得当前配置类上的注解,BeanDefinitionRegistry参数用于注册Bean

7. 测试

集成测试提供了一种无须部署或运行程序来完成验证系统各部分是否正常协同工作的能力。Spring提供了一个SpringJunit4ClassRunner类。通过@ContextConfiguration来配置Application Context,通过@ActiveProfiles确定活动的profile。

示例:

1) 增加Spring测试的依赖包

  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>
View Code

2) 业务代码

  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 
View Code

3) 配置类

  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 
View Code

4) 测试类,注意测试类写在src/test/java中

  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 
View Code
posted @ 2018-06-19 08:25  mengrennwpu  阅读(744)  评论(0编辑  收藏  举报