在学习使用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注解实现代码

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

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

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

getCandidateConfigurations实现细节

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

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

定义一个外部项目autoconfigbean

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.sl.autoconfig</groupId>
<artifactId>autoconfig-bean</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
</dependencies>
</project>

定义需要自动注入的类

@Configuration
public class TestConfig { @Bean
public Configdemo configdemo(){
return new Configdemo();
} }
public class Configdemo { }

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

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

在springboot项目中引用autoconfigbean

<dependency>
<groupId>com.sl.autoconfig</groupId>
<artifactId>autoconfig-bean</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>

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

@import注解

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

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

@import源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import { /**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value(); }

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

public interface ImportSelector {

   /**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata); }

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

public interface ImportBeanDefinitionRegistrar {

   /**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry); }

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

// ImportByConfiguration类定义的bean都将注入spring容器
public class ImportByConfiguration { @Bean
public ImportTestClass createImportTestClass(){
return new ImportTestClass();
}
}

定义ImportSelector接口实现类,

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

定义ImportBeanDefinitionRegistrar接口实现类

public class ImportBeanDefinitionRegistrarTest implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// new一个RootBeanDefinition
BeanDefinitionBuilder rootBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(ImportTestClass.class);
//RootBeanDefinition rootBeanDefinition2 = new RootBeanDefinition(ImportTestClass.class);
// 注册一个name为importTestClassInstance的bean
registry.registerBeanDefinition("importTestClassInstance", rootBeanDefinition.getBeanDefinition());
//registry.registerBeanDefinition("importTestClassInstance",rootBeanDefinition2);
}
}

输出到控制台:

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

控制台打印信息如下:

{createImportTestClass=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@2bc0cde,

com.sl.springbootdemo.EnableAnnotation.ImportTestClass=com.sl.springbootdemo.EnableAnnotation.ImportTestClass@ac16e08,

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. git 完善使用中

    GIT 版本库控制: 第一步:Git 的账号注册 url :https://github.com/ 这是git的官网如果第一次打开会这样 中间红色圈内是注册 内容, 第一项是用户名 第二项是邮箱 第三 ...

  2. Python读取 csv文件中文乱码处理

    需求:按行解析读取csv文件存入关系型数据库——主要是中文字体解析:遇到的问题:直接解析出来的数据为list形式,而且编码格式为unicode;解决问题:前提了解: 中文编码的规则 —— GB2312 ...

  3. STM32JTAG口用作普通IO的配置

    使用Jlink向STM32烧录程序时,需要使用6个芯片的引脚(以STM32F103C8T6为例),分别是PB4/JNTRST.PB3/JTDO.PA13/JTMS.PA14/JTCK.PA15/JTD ...

  4. python应用:日期时间

    计算时间差时,注意天数差引发的问题,获取天数差为 (date2-date1).days 此处,需谨记date2>date1,以保证结果的正确性 具体应用如下: # -*-coding:utf8- ...

  5. java的编码格式

    几种常见的编码格式 为什么要编码 不知道大家有没有想过一个问题,那就是为什么要编码?我们能不能不编码?要回答这个问题必须要回到计算机是如何表示我们人类能够理解的符号的,这些符号也就是我们人类使用的语言 ...

  6. Node.js的Formidable模块的使用,方便快捷

    服务用的是express ,如果不是很老的express框架,都有自带formidable  如果没有就下载一个  npm i formidable var formidable = require( ...

  7. c#随机产生颜色

    有时为了满足现实的需要,我们想生成随机的较深的颜色,比如:彩色二维码,为了让手机.二维码识别设备可以正确识别,必须使用较深的颜色.如下图所示:        那么,如何实现呢?以下为源码: //C# ...

  8. Android UI控件:TextView

    TextVIew的属性详解 android:autoLink设置是否当文本为URL链接/email/电话号码/map时,文本显示为可点击的链接.可选值(none/web /email/phone/ma ...

  9. 单例模式之pymysql运用实例

    何为单例? 简单介绍一下下:单例是个什么鬼东西!!!! 单例模式含义] 单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而 ...

  10. springboot 配置二级缓存

    springBoot中配置mybatis的二级缓存 2018年01月22日 11:45:37 Ting.Xue(Martin.Xue) 阅读数:5604更多 个人分类: SSM的Spring框架Myb ...