Spring4-@Enable** 注解的实现原理

来源:互联网 发布:怎么购买域名 编辑:程序博客网 时间:2024/05/17 01:13

背景

在前面的工作中使用SpringBoot的时候,我碰到了很多的使用@Enable***注解的地方,使用上也都是加在@Configuration 类注解的类上面,比如:
(1)@EnableAutoConfiguration 开启自动扫描装配Bean

(2)@EnableScheduling 开启计划任务的支持

(3)@EnableTransactionManagement 开启注解式事务的支持。

(4)@EnableCaching开启注解式的缓存支持。

(5)@EnableAspectJAutoProxy 开启对AspectJ自动代理的支持,

还有一些我没用过,但是听过的,比如:

(6) @EnableAsync 开启异步方法的支持

(7) @EnableWebMvc 开启Web MVC的配置支持。

(8) @EnableConfigurationProperties 开启对@ConfigurationProperties注解配置Bean的支持。

(9)@EnableJpaRepositories 开启对Spring Data JPA Repository的支持。

通过简单的加上这些@Enable*注解就可以开启一些功能,避免了在XML时代, 需要自己手动的配置很多的XML代码,大大提升效率降低使用难度。 那么这种简单的操作带来的巨大的便利性是怎么实现的呢?

我研究了一下上面的这些自动开启某些功能的注解,发现了一些公共的特性,下面先举出一个例子:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(CachingConfigurationSelector.class)public @interface EnableCaching {    boolean proxyTargetClass() default false;    AdviceMode mode() default AdviceMode.PROXY;    int order() default Ordered.LOWEST_PRECEDENCE;}

在这些所有的@Enable*注解的定义里面都包含一个 @Import 注解。 @Import注解在4.2之前只支持导入配置类,在4.2,@Import注解支持导入普通的java类,并将其声明成一个bean。这也说明了,自动开启的实现,其实是导入了一些配置类。

在上面各种@Enable**注解的实现中,导入配置类的形式,主要有三种类型:

1. 根据条件选择配置类(用的最多的)

以@EnableTransactionManagement 的定义为例:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(TransactionManagementConfigurationSelector.class)public @interface EnableTransactionManagement {    boolean proxyTargetClass() default false;    AdviceMode mode() default AdviceMode.PROXY;    int order() default Ordered.LOWEST_PRECEDENCE;}

TransactionManagementConfigurationSelector通过条件选择需要导入的配置类, TransactionManagementConfigurationSelector最根接口是ImportSelector, 这个接口重写了selectImports方法,在此方法里面先进行判断。比如在此例中,若AdviceMode为PROXY(默认), 则返回AutoProxyRegistrar 和 ProxyTransactionManagementConfiguration 的配置类。

源码如下:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {    @Override    protected String[] selectImports(AdviceMode adviceMode) {        switch (adviceMode) {            case PROXY:                return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};            case ASPECTJ:                return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};            default:                return null;        }    }}

2. 动态注册的Bean

以@EnableAspectJAutoProxy 为例:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AspectJAutoProxyRegistrar.class)public @interface EnableAspectJAutoProxy {    boolean proxyTargetClass() default false;    boolean exposeProxy() default false;}

AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类,并在Spring容器启动时解析生成bean,通过重写方法:

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)

来实现扫描注册逻辑。其中,AnnotationMetadata参数用来获得当前配置类上的注解,BeanDefinitionRegistry 参数用来注册Bean。源码如下:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {    @Override    public void registerBeanDefinitions(            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);        AnnotationAttributes enableAspectJAutoProxy =                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);        if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);        }        if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);        }    }}

3. 直接导入配置类

以@EnableScheduling 注解为例

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Import(SchedulingConfiguration.class)@Documentedpublic @interface EnableScheduling {}

从上面可以看到,直接导入了入配置类SchedulingConfiguration, 这个类注解了@Configuration,且注册了一个scheduledAnnotationProcessor的Bean,SchedulingConfiguration的源码如下:

@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();    }}

总结: 其实上面的三种方法中,第一类是用的最多的。

阅读全文
0 0
原创粉丝点击