starter自动转配流程以及@Import注解使用
本文主要内容包括三个部分,第一部分主要介绍@Import注解的三种使用方法,第二部分主要介绍自定义starter的两种形式,第三部分主要介绍Springboot自动装配Bean的大致流程,第四部分主要介绍一些starter的一些基本知识。
1:三种使用方法
1.1:导入普通类
1.1.1:直接导入
新建一个测试类TestA.java
public class TestA {
public String funA(){
return "TestA";
}
}
在创建一个配置类
@Import(TestA.class)
public class Config {
}
测试
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
TestA testA = context.getBean(TestA.class);
System.out.println(testA.funA());
}
}
1.1.2:导入带有@Configuration的配置类
再见一个测试类TestB.java
public class TestB {
public String funB(){
return "TestB";
}
}
在创建一个配置类创建一个配置类ConfigOther.class
@Configuration
public class ConfigOther {
@Bean
public TestB testB(){
return new TestB();
}
}
修改Config.java
@Import({TestA.class, ConfigOther.class})
public class Config {
}
测试
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
TestA testA = context.getBean(TestA.class);
System.out.println(testA.funA());
TestB testB = context.getBean(TestB.class);
System.out.println(testB.funB());
}
}
1.2:通过ImportSelector导入
创建TestC.class
public class TestC {
public String funC(){
return "TestC";
}
}
创建ClassConfig并实现org.springframework.context.annotation.ImportSelector接口
@Configuration
public class ClassConfig implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{TestC.class.getName()};
}
}
修改Config.class
@Import({TestA.class, ConfigOther.class,ClassConfig.class})
public class Config {
}
测试
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
TestA testA = context.getBean(TestA.class);
System.out.println(testA.funA());
TestB testB = context.getBean(TestB.class);
System.out.println(testB.funB());
TestC testC = context.getBean(TestC.class);
System.out.println(testC.funC());
}
}
1.3通过 ImportBeanDefinitionRegistrar 方式导入的类
创建测试类TestD.class
public class TestD {
public String funD(){
return "TestD";
}
}
创建CustomImportBeanDefinitionRegistrar.class并实现org.springframework.context.annotation.ImportBeanDefinitionRegistrar接口
public class CustomImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
RootBeanDefinition testD = new RootBeanDefinition(TestD.class);
registry.registerBeanDefinition("testD", testD);
}
}
修改Config.class
@Import({TestA.class, ConfigOther.class,ClassConfig.class,CustomImportBeanDefinitionRegistrar.class})
public class Config {
}
测试
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
TestA testA = context.getBean(TestA.class);
System.out.println(testA.funA());
TestB testB = context.getBean(TestB.class);
System.out.println(testB.funB());
TestC testC = context.getBean(TestC.class);
System.out.println(testC.funC());
TestD testD = context.getBean(TestD.class);
System.out.println(testD.funD());
}
}
至此@Import的使用已完毕,下面我们看一下starter是如何实现自动配置的。
自定义starter的时候有两种方式,
第一种是引入了依赖之后需要在启动类上加上@EnableXXX注解的方式,
第二种是在META-INF中加入spring.factories文件然后引入需要配置的类这种就什么也不需要配置了。
2:自定义starter的方式
第一种:采用@EnableXXX注解的形式自定义starter
新建一个注解@EnableAppConfig
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.TYPE)
@Import(ClassConfig.class)
public @interface EnableAppConfig {
}
然后使用该starter的时候只需在pom.xml文件中加入依赖,在启动类上加上@EnableAppConfig,bean就会自动装配了
第二种:使用spring.factories文件形式自定义starter
在META-INF中加入spring.factories文件添加一下内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.liekkas.sql.testimport.Config
在Config.class类上加上@Configuration注解
@Import({TestA.class, ConfigOther.class,ClassConfig.class,CustomImportBeanDefinitionRegistrar.class})
@Configuration
public class Config {
}
然后使用该starter的时候只需要在pom.xml文件中加入依赖bean就会自动装配了。
两种形式自定义starter无本质区别,只是采用@EnableXXX注解的形式需要在启动类上加注解,而如果配置了spring.factories文件则只需要引入依赖就可以了。当然本文只是大致介绍了如何自定义starter的简单使用,如有复杂的逻辑配置等需求请参考https://www.cnblogs.com/wkynf/p/14256565.html
3:Springboot是如何自动配置Bean的
查看org.springframework.boot.autoconfigure.AutoConfigurationImportSelector的selectImports方法可知道
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//获取所有需要自动装配的Bean的全限定名
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry获取所有的配置类的全限定类名
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取所有的Spring.factories里面的内容
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}

获取到所有需要加载的Bean的全限定类名之后就可以放到Spring的容器中了。
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;
}
SpringBoot使用了SPI机制可以读取所有classpath路径下的spring.factories文件的内容,然后根据我们@Import注解中的内容把Bean加入Spring的容器中。至于如何解析@Import注解的内容应该就是工作量的事情了,当我们把Springboot自动装配Bean的答题逻辑了解清楚了之后,其他的就是内容的实现了,如有需要了解具体逻辑的同学,请自行参考Springboot的代码。
4:starter的一些注意事项
starter主要分为两种,一种是官方定义的starter,另一种是非官方的starter比如我们自定义的starter。
一些命名的注意事项,一般官方的命名为spring-boot-starter-xxx,而非官方的starter命名为xxx-spring-boot-starter
官方定义starter可以参考redis的starter,非官方的可以参考mybatis的starter,仔细观察就会发现官方的starter中没有spring.factories文件,而非官方的starter中有spring.factories文件,就是因为官方的starter中的spring.factories文件内容已经被内置到Springboot项目中了,所以不需要spring.factories文件了,而非官方的就需要我们自定义配置了。
starter自动转配流程以及@Import注解使用的更多相关文章
- Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件
本章内容 自定义属性快速入门 外化配置 自动配置 自定义创建 Starter 组件 摘录:读书是读完这些文字还要好好用心去想想,写书也一样,做任何事也一样 图 2 第二章目录结构图 第 2 章 Spr ...
- Spring 自动转配类 在类中使用@Bean 注解进行转配但是需要排除该类说明
在spring中可以使用 @Component @Configuration @Bean(实例化后返回该bean)进行类实例的自动装配. 需求: 排除指定需要自动转配的类. 说明: 1.在以上注解中 ...
- SpringBoot自动配置(装配)流程
源码分析 SpringBoot自动配置流程 首先,我们要了解在@SpringBootApplication注解的内部,还具有@EnableAutoConfiguration,@SpringBo ...
- @enable跟@import注解
参考文章: 讲@import的相关内容:https://blog.csdn.net/u012437781/article/details/78626134 讲为什么registrar没有注入:http ...
- Spring5源码深度分析(二)之理解@Conditional,@Import注解
代码地址: 1.源码分析二主要分析的内容 1.使用@Condition多条件注册bean对象2.@Import注解快速注入第三方bean对象3.@EnableXXXX 开启原理4.基于ImportBe ...
- spring注解之@Import注解的三种使用方式
目录 1.@Import注解须知 2.@Import的三种用法 3.@Import注解的三种使用方式总结 @ 1.@Import注解须知 1.@Import只能用在类上 ,@Import通过快速导入的 ...
- EnableAutoConfiguration注解 Spring中@Import注解的作用和使用
EnableAutoConfiguration注解 http://www.51gjie.com/javaweb/1046.html springboot@EnableAutoConfiguration ...
- Spring中@Import注解的使用
Spring中@Import注解的使用 @Import注解算是SpringBoot自动配置原理中一个很重要的注解 认识@Import注解 先看一下源码 @Target(ElementType.TYPE ...
- JAVA_eclipse 保留Java文件时自动格式化代码和优化Import
Eclipse 保存Java文件时自动格式化代码和优化Import Eclipse中format代码的快捷方式是ctrl+shift+F,如果大家想保存 java文件的时候 自动就格式化代码+消除不必 ...
随机推荐
- Codeforces Round #687 (Div. 2, based on Technocup 2021 Elimination Round 2)
A. Prison Break 题意:就是在一个n*m的矩阵中,以(1,1)为起点(n,m)为终点,每个点以每个单位1s的速度移动,问总共至少需要多少秒,所有的矩阵点就能够全部移动到(r,c)中 思路 ...
- ES系列(五):获取单条数据get处理过程实现
前面讲的都是些比较大的东西,即框架层面的东西.今天咱们来个轻松点的,只讲一个点:如题,get单条记录的es查询实现. 1. get语义说明 get是用于搜索单条es的数据,是根据主键id查询数据方式. ...
- Elastic Stack(ElasticSearch 、 Kibana 和 Logstash) 实现日志的自动采集、搜索和分析
Elastic Stack 包括 Elasticsearch.Kibana.Beats 和 Logstash(也称为 ELK Stack).能够安全可靠地获取任何来源.任何格式的数据,然后实时地对数据 ...
- Docker Swarm(九)资源限制
资源限制 docker run 針對限制容器資源有許多設置選項,但Swarm中的 docker service 是另一回事,目前只有cpu和memory的選項可以操作. 如果 docker 找不到足夠 ...
- jmeter中beanshell postprocessor结合fastjson库提取不确定个数的json参数
在项目实践中,遇到了这样一个问题.用jmeter作http接口测试,需要的接口参数个数是不确定的.也就是说,在每次测试中,根据情况不同,可能页面中的列表中所含的参数个数是不确定的,那么要提取的参数个数 ...
- shell基础之EOF的用法
一.EOF的用法 EOF是(END Of File)的缩写,表示自定义终止符.既然自定义,那么EOF就不是固定的,可以随意设置别名,在linux按ctrl-d 就代表EOF. EOF一般会配合cat能 ...
- Lua _G
1.全局变量的原形 在Lua中,要声明全局变量很简单,那就是定义变量的时候,前面不要加上local. 这个神秘的全局变量,其实本质上也是一个table,它把我们创建的全局变量都保存到一个table里了 ...
- 4.3 Python3进阶-函数嵌套和嵌套调用
>>返回主目录 源码 # 函数嵌套 def func1(): print("这是外部函数") def func2(): print("这是内部函数1" ...
- 《Matlab实用案例》系列Matlab从入门到精通实用100例案例教程目录(持续更新)
目录 1. 专栏简介 2. 专栏地址 3. 专栏目录 1. 专栏简介 2. 专栏地址 「 刘一哥与GIS的故事 」之<Matlab使用案例> 3. 专栏目录 [MATLAB统计分析与应用1 ...
- python字典转bytes类型字典
python字典转bytes类型字典import base64 import json 1. a={"Vod":{"userData":"{}&quo ...