zoukankan      html  css  js  c++  java
  • Spring4-@Enable** 注解的实现原理

    背景
    在前面的工作中使用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)
    @Documented
    public @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();
    }
    }
    总结: 其实上面的三种方法中,第一类是用的最多的。

  • 相关阅读:
    命令拷屏之网络工具
    PHP 设计模式 笔记与总结(1)命名空间 与 类的自动载入
    Java实现 计蒜客 1251 仙岛求药
    Java实现 计蒜客 1251 仙岛求药
    Java实现 计蒜客 1251 仙岛求药
    Java实现 蓝桥杯 算法训练 字符串合并
    Java实现 蓝桥杯 算法训练 字符串合并
    Java实现 蓝桥杯 算法训练 字符串合并
    Java实现 LeetCode 143 重排链表
    Java实现 LeetCode 143 重排链表
  • 原文地址:https://www.cnblogs.com/moxiaotao/p/10107072.html
Copyright © 2011-2022 走看看