在学习使用springboot过程中,我们经常碰到以@Enable开头的注解,其实早在Spring3中就已经出现了类似注解,比如@EnableTransactionManagement、@ EnableWebMvc等,本文以@ EnableAutoConfiguration注解为例跟踪一下源码,分析实现过程。

@EnableAutoConfiguration注解

@EnableAutoConfiguration注解主要功能是启用自动配,将classpath中所有包下的META-INF/spring.factories配置文件中Key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的对应的符合自动配置条件的bean定义加载到Spring容器中。

查看源码可以看到@EnableAutoConfiguration是一个复合注解,自身也使用了其他注解,其中关键的是@Import(AutoConfigurationImportSelector.class)

@EnableAutoConfiguration注解实现代码

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Documented
  3. @Inherited
  4. @AutoConfigurationPackage
  5. @Import(AutoConfigurationImportSelector.class)
  6. public @interface EnableAutoConfiguration {
  7.  
  8. //是否启用EnableAutoConfiguration注解,可在spring.factories中配置 spring.boot.enableautoconfiguration=true 或者false控制是否启用,默认为true
  9. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  10.  
  11. //通过class指定需要排除的自动配置类:
  12. Class<?>[] exclude() default {};
  13.  
  14. //通过类名指定需要排除的自动配置类:
  15. String[] excludeName() default {};
  16. }

查看AutoConfigurationImportSelector.class源码发现这个继承接口ImportSelector,所以截取其中最重要的一个方法

  1. //实现接口ImportSelector的selectImports方法 ,该方法返回一个类名数组,数组中的类会被spring容器所托管起来
  1. @Override
  2. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  3. //判断是否启用自动配置
  4. if (!isEnabled(annotationMetadata)) {
  5. return NO_IMPORTS;
  6. }
  7. //加载spring内部自动配置的默认原属性
  8. AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
  9. .loadMetadata(this.beanClassLoader);
  10. AnnotationAttributes attributes = getAttributes(annotationMetadata);
  11. //关键加载配置实现:
  12. // getCandidateConfigurations方法实现加载需要自动配置的类,内部通过使用Spring framework提供的SpringFactoriesLoader类,可以从classpath中搜索所有META-INF/spring.factories配置文件,并读取配置
  13. List<String> configurations = getCandidateConfigurations(annotationMetadata,
  14. attributes);
  15. //去掉重复配置
  16. configurations = removeDuplicates(configurations);
  17. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
  18. //检查需要排除的配置
  19. checkExcludedClasses(configurations, exclusions);
  20. configurations.removeAll(exclusions);
  21. //spring.factories加载一个org.springframework.boot.autoconfigure.AutoConfigurationImportFilter过滤器OnClassCondition //通过该过//滤器判断现有系统是否引入了某个组件,如果有则进行相关配置.
  22. configurations = filter(configurations, autoConfigurationMetadata);
  23. //处理事件监听,具体监听器参见spring.factories中配置
  24. fireAutoConfigurationImportEvents(configurations, exclusions);
  25.  
  26. //返回最终需要托管的类
  27. return StringUtils.toStringArray(configurations);
  28. }

getCandidateConfigurations实现细节

  1. //实现加载META-INF/spring.factories配置文件,具体配置文件有什么类容,可以自行查看spring-boot-autoconfig-{version}.jar
  2. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
  3. AnnotationAttributes attributes) {
  4. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
  5. getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
  6. Assert.notEmpty(configurations,
  7. "No auto configuration classes found in META-INF/spring.factories. If you "
  8. + "are using a custom packaging, make sure that file is correct.");
  9. return configurations;
  10. }

上面分析了EnableAutoConfiguration的实现过程,下面通过一个示例演示如果从外部项目中读取自动配置

定义一个外部项目autoconfigbean

pom.xml

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5.  
  6. <groupId>com.sl.autoconfig</groupId>
  7. <artifactId>autoconfig-bean</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <dependencies>
  10. <dependency>
  11. <groupId>org.springframework</groupId>
  12. <artifactId>spring-context</artifactId>
  13. <version>5.0.7.RELEASE</version>
  14. </dependency>
  15. </dependencies>
  16. </project>

定义需要自动注入的类

  1. @Configuration
  2. public class TestConfig {
  3.  
  4. @Bean
  5. public Configdemo configdemo(){
  6. return new Configdemo();
  7. }
  8.  
  9. }
  10. public class Configdemo {
  11.  
  12. }

添加resources/META-INF/spring.factories配置文件

  1. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  2. configtest.Configdem

在springboot项目中引用autoconfigbean

  1. <dependency>
  2. <groupId>com.sl.autoconfig</groupId>
  3. <artifactId>autoconfig-bean</artifactId>
  4. <version>1.0-SNAPSHOT</version>
  5. </dependency>

启动springboot项目可以通过System.out.println(context.getBeansOfType(Configdemo.class)) 查看是否已经注入到spring容器

@import注解

上面的@EnableAutoConfiguration注解通过@Import(AutoConfigurationImportSelector.class)实现了主要功能, 下面来看一下@import注解的作用

@import是spring framework提供的一个注解,是通过导入的方式把一个或多个bean或者bean的配置类注入到Spring容器中。

@import源码

  1. @Target(ElementType.TYPE)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Import {
  5.  
  6. /**
  7. * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
  8. * or regular component classes to import.
  9. */
  10. Class<?>[] value();
  11.  
  12. }

ImportSelector接口:selectImports方法返回一个class名称数组,该class会被spring容器所托管起来

  1. public interface ImportSelector {
  2.  
  3. /**
  4. * Select and return the names of which class(es) should be imported based on
  5. * the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
  6. */
  7. String[] selectImports(AnnotationMetadata importingClassMetadata);
  8.  
  9. }

ImportBeanDefinitionRegistrar接口:registerBeanDefinitions方法的参数有一个BeanDefinitionRegistry,该register可以用来向spring容器中注入bean

  1. public interface ImportBeanDefinitionRegistrar {
  2.  
  3. /**
  4. * Register bean definitions as necessary based on the given annotation metadata of
  5. * the importing {@code @Configuration} class.
  6. * <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
  7. * registered here, due to lifecycle constraints related to {@code @Configuration}
  8. * class processing.
  9. * @param importingClassMetadata annotation metadata of the importing class
  10. * @param registry current bean definition registry
  11. */
  12. public void registerBeanDefinitions(
  13. AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
  14.  
  15. }

下面通过一个示例简单演示一下Import注解通过{@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}三种方式注入
定义配置类

  1. // ImportByConfiguration类定义的bean都将注入spring容器
  2. public class ImportByConfiguration {
  3.  
  4. @Bean
  5. public ImportTestClass createImportTestClass(){
  6. return new ImportTestClass();
  7. }
  8. }

定义ImportSelector接口实现类,

  1. //selectImports方法返回一个类数据,该数组中的类将会被spring容器所托管起来
  2. public class ImportSelectorTest implements ImportSelector {
  3. @Override
  4. public String[] selectImports(AnnotationMetadata importingClassMetadata) {
  5. /**
  6. * xxxx逻辑
  7. */
  8.  
  9. return new String[]{
  10. "com.sl.springbootdemo.EnableAnnotation.ImportTestClass"
  11. };
  12. }
  13. }

定义ImportBeanDefinitionRegistrar接口实现类

  1. public class ImportBeanDefinitionRegistrarTest implements ImportBeanDefinitionRegistrar {
  2. @Override
  3. public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  4. // new一个RootBeanDefinition
  5. BeanDefinitionBuilder rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ImportTestClass.class);
  6. //RootBeanDefinition rootBeanDefinition2 = new RootBeanDefinition(ImportTestClass.class);
  7. // 注册一个name为importTestClassInstance的bean
  8. registry.registerBeanDefinition("importTestClassInstance", rootBeanDefinition.getBeanDefinition());
  9. //registry.registerBeanDefinition("importTestClassInstance",rootBeanDefinition2);
  10. }
  11. }

输出到控制台:

  1. @Import({ImportByConfiguration.class, //导入bean配置类,则配置类中bean也将注入到spring容器
  2. ImportSelectorTest.class, //ImportSelector接口方式
  3. ImportBeanDefinitionRegistrarTest.class //ImportBeanDefinitionRegistrar接口方式
  4. })
  5. //@ComponentScan
  6. @SpringBootApplication
  7. public class SpringbootdemoApplication2 {
  8.  
  9. public static void main(String[] args) {
  10. ConfigurableApplicationContext context = SpringApplication.run(SpringbootdemoApplication2.class,args);
  11. System.out.println(context.getBeansOfType(ImportTestClass.class));
  12. }
  13. }

控制台打印信息如下:

  1. {createImportTestClass=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@2bc0cde,
  2.  
  3. com.sl.springbootdemo.EnableAnnotation.ImportTestClass=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@ac16e08,
  4.  
  5. importTestClassInstance=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@210a40d6}

@Import 注解并不是SpringBoot的功能,Spring 3.0开始就提供了@Import这个注解,并在后续版本中逐渐完善,主要用来支持在Configuration类中引入其它的配置类,包括Configuration类, ImportSelector和ImportBeanDefinitionRegistrar的实现类。

Spring Boot(四)@EnableXXX注解分析的更多相关文章

  1. Spring Boot 实战与原理分析视频课程

    Spring Boot 实战与原理分析视频课程 链接:https://pan.baidu.com/share/init?surl=PeykcoeqZtd1d9lN9V_F-A 提取码: 关注公众号[G ...

  2. spring boot启动原理步骤分析

    spring boot最重要的三个文件:1.启动类 2.pom.xml 3.application.yml配置文件 一.启动类->main方法 spring boot启动原理步骤分析 1.spr ...

  3. Spring Boot(四) Mybatis-MySql

    Spring Boot(四) Mybatis-MySql 0.准备数据库表 -- ---------------------------- -- Table structure for person ...

  4. Spring Boot 二十个注解

    Spring Boot 二十个注解 占据无力拥有的东西是一种悲哀. Cold on the outside passionate on the insede. 背景:Spring Boot 注解的强大 ...

  5. spring boot @ConditionalOnxxx相关注解总结

    Spring boot @ConditionalOnxxx相关注解总结 下面来介绍如何使用@Condition public class TestCondition implements Condit ...

  6. Spring boot 使用的注解有哪些?

    Spring boot 使用的注解有哪些? 注解 作用 @SpringBootApplication 等价于 @Configuration + @EnableAutoConfiguration + @ ...

  7. (32)Spring Boot使用@SpringBootApplication注解,从零开始学Spring Boot

    [来也匆匆,去也匆匆,在此留下您的脚印吧,转发点赞评论] 如果看了我之前的文章,这个节你就可以忽略了,这个是针对一些刚入门的选手存在的困惑进行写的一篇文章. 很多Spring Boot开发者总是使用 ...

  8. spring boot 中@Autowired注解无法自动注入的错误

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/huihuilovei/article/de ...

  9. Spring Boot中@Scheduled注解的使用方法

    Spring Boot中@Scheduled注解的使用方法 一.定时任务注解为@Scheduled,使用方式举例如下 //定义一个按时间执行的定时任务,在每天16:00执行一次. @Scheduled ...

  10. Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?

    启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解: @SpringBootConfiguration:组合了 ...

随机推荐

  1. 【Spark】Spark2.x版的新特性

    一.API 1. 出现新的上下文接口:SparkSession,统一了SQLContext和HiveContext,并且为SparkSession开发了新的流式调用的configuration API ...

  2. 网站用户行为分析——在Ubuntu下安装MySQL及其常用操作

    安装MySQL 使用以下命令即可进行mysql安装,注意安装前先更新一下软件源以获得最新版本: sudo apt-get update #更新软件源 sudo apt-get install mysq ...

  3. 『Python基础-3』变量、定义变量、变量类型、关键字Python基础-3』变量、定义变量、变量类型、关键字

    『Python基础-3』变量.定义变量.变量类型.关键字 目录: 1.Python变量.变量的命名 2.变量的类型(Python数据类型) 3.Python关键字 1. Python 变量.变量的命名 ...

  4. Python中常见的字典dict处理

    #字典的赋值d = [{"dasda": 123, "gsgsg": 3344}, {"dasdz": 123, "gsksg&q ...

  5. PTA基础编程题目集7-3逆序三位数

    7-3 逆序的三位数 (10 分) 程序每次读入一个正3位数,然后输出按位逆序的数字.注意:当输入的数字含有结尾的0时,输出不应带有前导的0.比如输入700,输出应该是7. 输入格式: 每个测试是一个 ...

  6. tensorflow简单实现卷积前向过程

    卷积,说白了就是对应位置相乘再求和,卷积操作用广泛应用于图像识别,在自然语言处理中也开始应用,用作文本分类问题. 卷积操作最重要的部分就是卷积核或者说过滤器 1.常用过滤器尺寸为3*3或者5*5 2. ...

  7. Luogu P3120 [USACO15FEB]牛跳房子(金)Cow Hopscotch (Gold)

    题目传送门 这是一道典型的记忆化搜索题. f[x][y]表示以x,y为右下角的方案数. code: #include <cstdio> #define mod 1000000007 usi ...

  8. 北京Uber优步司机奖励政策(12月29日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  9. 人脸检测库libfacedetection介绍

    libfacedetection是于仕琪老师放到GitHub上的二进制库,没有源码,它的License是MIT,可以商用.目前只提供了windows 32和64位的release动态库,主页为http ...

  10. 拼接index

    import MySQLdb import sys db = MySQLdb.connect(host="127.0.0.1", # your host, usually loca ...