概述
简介
实现原理
简介 在使用SpringBoot的时候,我们经常看到一些@Enable*
之类的开启对某些特性的支持。类如下面举的例子
@EnableAspectJAutoProxy 开启对AspectJ自动代理的支持
@EnableAsync 开启异步方法的支持
@EnableScheduling 开启计划任务的支持
@EnableWebMvc 开启Web MVC的配置支持。
@EnableConfigurationProperties开启对@ConfigurationProperties注解配置Bean的支持。
@EnableJpaRepositories 开启对Spring Data JPA Repository的支持。
@EnableTransactionManagement 开启注解式事务的支持。
@EnableCaching开启注解式的缓存支持。
通过简单的@Enable*
来开启一项功能的支持,从而避免自己配置大量的代码,大大降低使用难度。那么这个神奇的功能的实现原理是什么呢?下面来研究一下。
实现原理 通过观察这些@Enable*
注解的源码,发现所有的注解都有一个@Import
注解,@Import
是用来导入配置类的,这也就意味着这些自动开启的实现其实是导入了一些自动配置的Bean。这些导入的配置主要分为以下三种类型:
直接导入配置类
依据条件选择配置类
动态注册Bean
下面我们来分别介绍这个类型
直接导入配置类 比较直接的就是@EnableScheduling
,这个注解的源码如下
1 2 3 4 5 6 7 @Target({java.lang.annotation.ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Import({SchedulingConfiguration.class}) @Documented public @interface EnableScheduling{ }
可以看到EnableScheduling
注解直接导入配置类SchedulingConfiguration
,这个类注解了@Configuration
,且注册了一个scheduledAnnotationProcessor
的Bean,SchedulingConfiguration
的源码如下:
1 2 3 4 5 6 7 8 9 10 @Configuration public class SchedulingConfiguration { @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor () { return new ScheduledAnnotationBeanPostProcessor (); } }
依据条件选择配置类 这里可以看看@EnableAsync
这个注解,源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @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; }
AsyncConfigurationSelectort
通过条件来选择需要导入的配置类。AsyncConfigurationSelector
的根接口为ImportSelector
,这个接口需要重写selectImports
方法,在此方法内进行事先条件判断。此例中,若adviceMode
为PROXY
,则返回ProxyAsyncConfiguration
这个配置类;若adviceMode
为ASPECTJ
,则返回AspectJAsyncConfiguration
配置类,源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class AsyncConfigurationSelector extends AdviceModeImportSelector <EnableAsync> { private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration" ; @Override 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
来举例,源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass () default false ; }
AspectJAutoProxyRegistrar
实现了ImportBeanDefinitionRegistrar
接口,ImportBeanDefinitionRegistrar
的作用是在运行时自动添加Bean到已有的配置类,通过重写方法:
1 public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
其中,AnnotationMetadata
参数用来获得当前配置类上的注解,BeanDefinitionRegistry
参数用来注册Bean。源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions ( AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry); AnnotationAttributes enableAJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class); if (enableAJAutoProxy.getBoolean("proxyTargetClass" )) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } } }
总结 其实就是将原先在xml中配置的一些类,现在统一的转换成对应的类,然后通过以上三种方法,将这些类导入到spring容器中。
参考
Spring4.x高级话题(六):@Enable*注解的工作原理