一. Spring Aware
在实际项目中,你不可避免的要用到spring容器本身的功能资源,这时你的bena必须要意识到spring容器的存在,才能调用spring所提供的资源,这就是所谓的Spring Aware。
BeanNameAware | 获取容器中的bean |
BeanFactoryAware | 获取当前bean factory,这样可以调用容器的服务 |
ApplicationContextAware | 获取当前的application context,这样可以调用容器的服务 |
MessageSourceAware | 获得message source,这样可以获得文本信息 |
ApplicationEventPublisherAware | 应用时间发布器,可以发布事件 |
ResourceLooaderAware | 获取资源加载器,可以获得外部资源文件 |
//实现BeanNameAware,ResourceLoaderAware接口,获取bean名称和资源加载服务
public class AwareService implements BeanNameAware,ResourceLoaderAware{
private String beanName;
pirvate RresourceLoader loader;
//实现ResourceLoaderAware需要重写setResourceLoader方法
@Verride
public void setResourceLoader(ResourceLoader resourceLoader){
this.loader = resourceLoader;
}
//实现BeanNameAware需要重写setBeanName方法
@Verride
public void setBeanName(String name){
this.beanName = name;
}
public void outputResult(){
sout(beanName);
Resource resource = loader.getResource("classpath:test.txt");
sout(IOUtils.toString(resource.getInputStream()));
}
}
二.多线程
spring通过任务执行器(TaskExecutor)来实现多线程和并发编程。使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。而实际开发中任务一般是非阻碍的,即异步的,所以我们要在配置类中通过@EnableAsync开启异步任务的支持,并通过在实际执行的Bean的方法中使用@Async注解来声明其是一个异步任务。
1.配置类:
@Configuration
@ComponentScan("com.spring.demo.ch03.taskexecutor")
@EnableAsync //该注解开启异步任务支持
public class TaskExecutorConfig implements AsyncConfigurer{
//配置类实现AsyncConfigurer接口并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor,这样我们就获得了一个基于线程池TaskExecutor
@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;
}
}
2.任务执行类
@Service
public class AsyncTaskService{
@Async //该注解表明该方法是一个异步方法,如果注解在类级别,则表明该类所欲的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
public void executeAsyncTask(Integer i){
sout("执行异步任务:"+i);
}
@Async
public void executeAsyncTaskPlus(Integer i){
sout("执行异步任务+1:"+(i+1));
}
}
三.计划任务
从spring3.1开始,计划任务在spring中的实现变得异常的简单。首先通过在配置类注解@EnableScheduling来开启对计划任务的支持,然后在要执行计划任务的方法上注解@Scheduled,声明这是个计划任务。
Spring通过@Scheduled支持多种类型的计划任务,包含cron,fixDelay,fixRate等
1.计划任务执行类
@Service
public class ScheduledTaskService{
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
@Scheduled(fixedRate=5000) //通过@Scheduled声明该方法是计划任务,使用fixedRate属性每隔固定时间执行。
public void reportCurrentTime(){
System.out.println("每隔五秒执行一次:"+dateFormat.format(new Date()));
}
@Scheduled(cron="0 15 16 ? * *") //使用cron属性可按照指定时间执行。本例是每天16点15分执行
public void fixTimeExecution(){
System.out.println("在指定实际:"+dateFormat.format(new Date())+"执行");
}
}
2.配置类
@Configuration
@ComponentScan("com.spring.demo.ch03.taskscheduler")
@EnableScheduling //该注解开启对计划任务的支持
public class TaskSchedulerConfig{
}
四.条件注解@Conditonal
根据满足某一个特定条件创建一个特定的Bean。
比如,以不同的操作系统为条件,我们通过实现Condition接口,并重写其mathces方法来构造条件判断。若在Windows系统下运行程序,则输出列表名利为dir;若在linux操作系统下运行程序,则输出列表命令为ls
1.判断条件定义
//判定Windows的条件
public class WindowsCondition implements Condition{
@Override
public boolean mathces(ConditionContext context,AnnotatedTypeMetadata metadata){
return context.getEnvironment.getProperty("os.name").contains("Windows");
}
}
//判定Linux的条件
public class LinuxCondition implementsCondition{
@Override
public boolean mathces(ConditionContext context,AnnotatedTypeMetadata metadata){
return context.getEnvironment.getProperty("os.name").contains("Linux");
}
}
2.不同系统下Bean的类
//接口
public interface ListService{
public String showListCmd();
}
//windows下所要创建的bean类
public class WindowsListService implements ListService{
@Override
public String showListCmd(){
renturn "dir";
}
}
//linux下所要创建的bean类
public class LinuxListService implements ListService{
@Override
public String showListCmd(){
renturn "ls";
}
}
3.配置类
@Configuration
public class ConditionConfig{
@Bean
@Conditional(SeindowsCondition.class) //符合windows条件则实例化WindowsListService
public ListService windowsListService(){
return new WindowsListService();
}
@Bean
@Conditional(SeindowsCondition.class) //服务linux条件则实例化LinuxListService
public ListService linuxListService(){
return new LinuxListService();
}
}
五.组合注解与元注解
所谓的元注解其实就是可以注解到别的注解上的注解,被注解的注解称为组合注解,组合注解具备元注解的功能。
spring有很多注解都可以作为元注解,而且spring本身已经有很多组合租界,如@Configuration就是一个组合@Component注解。
实例:将两个元注解@Configuration和@ComponentScan组成一个组合注解,这样我们就只需要一个注解就可以表示两个注解。
1.实例组合注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration //元注解
@ComponentScan //元注解
public @interface WiselyConfiguration{
String[] value() default{}; //覆盖value参数
}
2.新的配置类
@WiselyConfiguration("com.spring.demo.ch03.annotation")
public class DemoConfig{
}
六.@Enable*注解的工作原理
通过简单的@Enable*来开启一项功能的支持,从而避免自己配置大量的代码,大大减低使用难度。
@EnableAspectJautoProxy | 开启对AspectJ自动代理的支持 |
@EnableAsync | 开启异步方法的支持(多线程) |
@EnableScheduling | 开启计划任务的支持 |
@EnableWebMvc | 开启WebMVC的配置支持 |
@EnableConfigurationProperties | 开启对@ConfigurationProperties注解配置bean的支持 |
@EnableJpaRepositories | 开启对SpringData JPA Repository的支持 |
@EnableTransactionManagement | 开启注解式事务支持 |
@EnableCaching | 开启注解式的缓存支持 |
所有的注解都有要给@Import注解,用来导入配置类的,这就意味着这些自动开启的实现,其实是导入了一些自动配置的bena。这些导入的配置方式组要分为三种类型:
1.第一类:直接导入配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling{
//开启任务计划
}
//直接导入配置类SchedulingConfiguration,这个类注解了@Configration,而且注册了一个scheduledAnnotationProcessor的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();
}
}
2.第二类:依据条件选择配置类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync{ //开启异步方法(多线程)支持
Class<? extends Annotation> annotation() default Annotation.class;
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
/**
*AsyncConfigurationSelector 通过调节来选择需要导入的配置类,
*AsyncConfigurationSelector的根接口为ImportSelector,
*这个接口需要重写selectImports方法,在此方法内进行事先条件判断。
*/
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync>{
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME=
"org.springframework,scheduling.aspectj.AspectJAsyncConfiguration";
@Verride
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;
}
}
}
3.第三类:动态注册bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAsperctJAutoProxy{ //开启对AsperctJ自动代理的支持
boolean proxyTargetClass() default false;
}
/**
*AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,
*ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类
*重写方法registerBeanDefinitions,AnnotationMetadata参数用来获取当前配置类上的注解;
*BeanDefinitinRegistry参数用来注册Bean
*/
public class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar{
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,BeanDefinitinRegistry registry){
AopConfigUtils.registerAspecyJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata,EnableAspectJAutoProxy.class);
if(enableAJAutoProxy .getBoolean("proxyTargetClass")){
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}
}
七.测试
单元测试:针对当前开发的类和方法进行测试,可以简单通过模拟依赖来实现,对运行环境没有依赖;
集成测试:需要来自不同层的不同对象的交互,如数据库,网络连接,IOC容器等。
spring通过Spring TestContext Framework对集成测试提供顶级支持。它不依赖于特定的测试框架,既可以使用Junit,也可以使用TestNG。
Spring提供了一个SpringJUnit4ClassRunner类,它提供了Spring TestContext Framework的功能。通过@ContextConfiguration来配置Application Context,通过@ActivePofiles确定活动的profile
依赖:
<!--spring test 支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
业务代码:
public class DemoBean{
private String content;
public TestBean(String content){
super();
this.content = content;
}
public String getContent(){
return content;
}
public void setContent(String content){
this.content = content;
}
}
配置类
@Configuration
public class DemoConfig{
@Bean
@Profile("dev")
public TestBean devTestBean(){
return new TestBean("from development profile");
}
@Bean
@Profile("prod")
public TestBean prodTestBean(){
return new TestBean("from production profile");
}
}
测试
@RunWith(SpringJUnit4ClassRunner.class) //在JUnit环境下提供spring testContext Framework的功能
@ContextConfiguration(classes={DemoConfig.class}) //用来加载配置ApplictionContext,其中classes属性用来加载配置类。
@ActiveProfiles("prod") //用来声明活动的profile
public class DemoBenaTests{
@Autowired
private DemoBean demoBean;
@Test
public void prodBeanShouldInject(){
String expected = "from production profile";
String actual = demoBean.getContent();
Assert.asserEquals(expected ,actual );
}
}