1.首先我们知道SpringBoot的核心注解@SpringBootApplication包含@SpringBootConfiguration
、@EnableAutoConfiguration、@ComponentScan
其中@EnableAutoConfiguration是实现自动配置的核心。
点进@EnableAutoConfiguration,代码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
可以看到核心注解为
@Import({AutoConfigurationImportSelector.class})
2.AutoConfigurationImportSelector.class
点进{AutoConfigurationImportSelector.class,可以看到
接下来主要围绕着loadMetadata和getAutoConfigurationEntry展开
3.loadMetadata和getAutoConfigurationEntry
(1)进入loadMetadata,可以看到主要是读取配置文件META-INF/spring-autoconfigure-metadata.properties,把里面的数据放到AutoConfigurationMetadata对象中,这个对象怎么用的呢,我们接着往下看。
(2)搜索AutoConfigurationMetadata对象,首先在getAutoConfigurationEntry方法中调用,最终在filter方法中使用,filter里面传入的两个参数,另一个参数来自getAutoConfigurationEntry的getCandidateConfigurations
(3)getCandidateConfigurations
点进getCandidateConfigurations,可以看到最终也是返回一个配置,这个配置来自哪个文件我们从提示大概能猜到是META-INF/spring.factories
(4)loadFactoryNames
点进loadFactoryNames,发现果然是META-INF/spring.factories
(5)filter
了解了所有参数之后,我们可以开始看filter了,从名字就能猜到这个方法的作用–过滤器,它过滤了什么呢,接着往下看,可以看到,用AutoConfigurationMetadata和configuration匹配,匹配不上AutoConfigurationMetadata的数据会被过滤掉,具体匹配逻辑现在还不知道,后面会解释
(6)autoconfigure-metadata.properties和spring.factories
①现在我们知道filter的参数主要来自两个文件autoconfigure-metadata.properties和spring.factories
,在autoconfigure的jar包下找到了这俩文件
②打开看看,这是autoconfigure-metadata.properties,可以看到里面有些常见的注解,这些是SpringBoot特有的条件注解
③然后我们再看看spring.factories,存储了很多键值对,org.springframework.boot.autoconfigure.EnableAutoConfiguration这里存了很多逗号隔开的值
(7)filter匹配
①关于匹配逻辑这一块,一开始我没看懂,于是打了个断点,挑了几个例子
SpringApplicationAdminJmxAutoConfiguration、AopAutoConfiguration、BatchAutoConfiguration匹配结果分别是true、true、false
②比如spring.factories中的org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
在autoconfigure-metadata.properties中搜索,结果如下:
SpringApplicationAdminJmxAutoConfiguration有两个匹配项,一个带AutoConfigureAfter且后面跟了值,一个不带;
AopAutoConfiguration在autoconfigure-metadata.properties搜不到;
BatchAutoConfiguration有两个匹配项,一个带ConditionalOnBean且后面跟了值,一个不带;
最终只有BatchAutoConfiguration被过滤了
!也就是说匹配的上的也只有部分会被过滤掉。
然后我又跟了下代码,进入OnClassCondition.class有这么一个方法,里面对ConditionalOnBean的类进行了过滤,当ConditionalOnBean后面跟的值,比如BatchAutoConfiguration.ConditionalOnBean = A
,当A不存在时,最终BatchAutoConfiguration不会被自动装配,会被过滤掉。
最早没打算做这个的,因为发现部分源码还没理解的很清楚,所以决定自己写一个试试。
1.创建一个自定义starter启动器
new一个maven项目,根据starter的命名规范最好是xxx-spring-boot-starter,我这个叫demo-spring-boot-starter
2.项目目录结构如下
3.pom文件配置
加入如下配置,可以使用@Configuration等注解
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-autoconfigureartifactId>
<version>2.1.5.RELEASEversion>
dependency>
dependencies>
4.spring.factories
按照项目目录创建文件,加入下面代码
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.starter.configuration.AutoTestConfiguration
5.AutoTestProperties.java和AutoTestConfiguration.java
package com.starter.configuration;
import com.starter.dao.HealthCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//@Configuration相当于xml中的标签,注入容器
@Configuration
@EnableConfigurationProperties(value = AutoTestProperties.class)
public class AutoTestConfiguration {
@Autowired
private AutoTestProperties autoTestProperties;
//@Bean相当于xml中的标签,注册并管理bean的生命周期
@Bean
public HealthCode autowired(){
HealthCode healthCode = new HealthCode();
healthCode.setHealthStatus(autoTestProperties.getHealthStatus());
healthCode.setIdCard(autoTestProperties.getIdCard());
healthCode.setUsername(autoTestProperties.getUsername());
System.out.println("自动配置成功");
return healthCode;
}
}
package com.starter.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
//prefix在application.yml中使用,用来区分变量值
@ConfigurationProperties(prefix = "healthcode")
public class AutoTestProperties {
private String username;
private String idCard;
private String healthStatus;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public String getHealthStatus() {
return healthStatus;
}
public void setHealthStatus(String healthStatus) {
this.healthStatus = healthStatus;
}
}
6.HealthCode.java
package com.starter.dao;
public class HealthCode {
private String username;
private String idCard;
private String healthStatus;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public String getHealthStatus() {
return healthStatus;
}
public void setHealthStatus(String healthStatus) {
this.healthStatus = healthStatus;
}
}
7.将sarter项目打包,install即可
8.将starter引入已有的web项目中,在pom文件中加入下面代码,并更新一下maven
<dependency>
<groupId>org.examplegroupId>
<artifactId>demo-spring-boot-starterartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
9.在application.yml文件中加入下面配置,注意前面不要有空格
# 自定义starter配置
healthcode.username: caocao
healthcode.idCard: 362302198001013366
healthcode.healthStatus: 1
10.新建一个controller作为入口测试
package com.example.springb_web.controller;
import com.starter.dao.HealthCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestController {
@Autowired
private HealthCode healthCode;
@RequestMapping(value="/autoTest")
@ResponseBody
public HealthCode autoTest(){
return healthCode;
}
}
11.编译启动项目,测试
测试网址:http://localhost:8080/autoTest
12.结果如下
SpringBoot的自动配置使用@EnableAutoConfiguration注释实现,是通过找到spring.factories配置文件中的所有XXXAutoConfiguration的自动配置类,并将autoconfigure-metadata.properties中含@ConditionalOnClass@AutoConfigureAfter的配置类过滤掉,然后通过@bean注册,@Configuration加载到容器,实现这些类的自动配置。