zoukankan      html  css  js  c++  java
  • [转] Spring Boot 自动配置之@Enable* 与@Import注解

    SpringBoot 的自动配置如此强大,比如我们经常使用的@Enable* 注解来开启对某方面的支持。那么@Enable* 注解的原理是什么呢?

    一、@Enable* 注解与 @Import 注解之间的关系

    @Enable* 举例:

    • @EnableScheduling 开启计划任务的支持
    • @EnableAsync 开启异步方法的支持
    • @EnableAspectJAutoProxy 开启对 AspectJ 代理的支持
    • @EnableTransactionManagement 开启对事务的支持
    • @EnableCaching 开启对注解式缓存的支持

    等等

    我们观察这些@Enable* 的源码可以看出,所有@Enable* 注解都是有@Import的组合注解,@Enable* 自动开启的实现其实就是导入了一些自动配置的Bean

    看下 Spring Boot Reference Guide原文

    You need not put all your @Configuration into a single class. The @Import annotation
    can be used to import additional configuration classes.
    
    您不需要把所有的 @Configuration 放到一个类中。@Import 注解可以导入额外的配置类。

    @Import 注解的最主要功能就是导入额外的配置信息

    二、 @Import 注解的用法

    官方介绍:

    * <p>Provides functionality equivalent to the {@code <import/>} element in Spring XML.
    * Allows for importing {@code @Configuration} classes, {@link ImportSelector} and
    * {@link ImportBeanDefinitionRegistrar} implementations, as well as regular component
    * classes (as of 4.2; analogous to {@link AnnotationConfigApplicationContext#register}).

    有以下三种使用方式

    1、直接导入配置类(@Configuration 类)
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(SchedulingConfiguration.class)
    @Documented
    public @interface EnableScheduling {
    
    }

    可以看到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();
        }
    }
    2、依据条件选择配置类(实现 ImportSelector 接口)

    如果并不确定引入哪个配置类,需要根据@Import注解所标识的类或者另一个注解(通常是注解)里的定义信息选择配置类的话,用这种方式。

    ImportSelector接口只有一个方法

    String[] selectImports(AnnotationMetadata importingClassMetadata);

     AnnotationMetadata:用来获得当前配置类上的注解

    例:

    @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继承AdviceModeImportSelector,AdviceModeImportSelector类实现ImportSelector接口 根据AdviceMode的不同来选择生明不同的Bean
    public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    
        private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
    
        @Override
        @Nullable
        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(实现 ImportBeanDefinitionRegistrar 接口)

    一般只要用户确切知道哪些Bean需要放入容器的话,自己可以通过spring 提供的注解来标识就可以了,比如@Component,@Service,@Repository,@Bean等。 如果是不确定的类,或者不是spring专用的,所以并不想用spring的注解进行侵入式标识,那么就可以通过@Import注解,实现ImportBeanDefinitionRegistrar接口来动态注册Bean。 比如:

    @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到已有的配置类,通过重写方法:
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
    • AnnotationMetadata 参数用来获得当前配置类上的注解
    • BeanDefinitionRegistry 参数用来注册Bean

    源码:

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

    Mybatis 中大名鼎鼎的@MapperScan 也是如此

    三、官方文档

    官方文档


    转自:https://juejin.im/post/5c761c096fb9a049b41d2299

  • 相关阅读:
    poj 3693 Maximum repetition substring 重复次数最多的连续子串
    hdu 3518 Boring counting 后缀数组 height分组
    Codeforces 920G List Of Integers 二分 + 容斥
    Codeforces 920E Connected Components? 补图连通块个数
    Codeforces 920D Tanks
    LeetCode Weekly Contest 70 A B C D
    Educational Codeforces Round 37 A B C D E F
    检查站点SSL证书配置是否正确
    nginx rewrite之后post参数丢失问题
    Linux中如何避免buffers/cached占用过多内存
  • 原文地址:https://www.cnblogs.com/gotodsp/p/12952476.html
Copyright © 2011-2022 走看看