3.3、编写自动配置类,使用容器中的属性类读取配置文件中的属性值。
SpringBoot 的核心原理就是自动配置,其所有的配置类都是由 spring-boot-autoconfiguration这个模块提供的,自动配置类一般是以 ***AutoConfiguration命名,自动配置类需要注册到自动配置文件中。
自动配置文件:
1)、在 springboot 2.7 之前,自动配置文件在 META-INF/spring.factories (在 springboot 2.7 版本中宣布废弃了)
2)、在 springboot 3.0 之前,老的自动配置文件依旧支持,为了向后兼容;在 springboot 3.0 之后,正式废除,采用新的自动配置文件。新的自动配置文件为:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。
1、自动配置原理(基于springboot 3.0 版本)
springboot 自动配置原理使用是 @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 {};
}
1)、@AutoConfigurationPackage:注册需要自动配置的包,如果不指定就是当前注解所在类的包。
2)、@Import({AutoConfigurationImportSelector.class}):导入AutoConfigurationImportSelector配置类。
AutoConfigurationImportSelector配置类实现了ImportSelector 接口
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
// ========================
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
getAutoConfigurationEntry 方法进入 getCandidateConfigurations 方法。
// 自动配置文件路径
private static final String LOCATION = "META-INF/spring/%s.imports";
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).getCandidates();
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
// ImportCandidates.load 方法
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
Assert.notNull(annotation, "'annotation' must not be null");
ClassLoader classLoaderToUse = decideClassloader(classLoader);
String location = String.format("META-INF/spring/%s.imports", annotation.getName());
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> importCandidates = new ArrayList();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
importCandidates.addAll(readCandidateConfigurations(url));
}
return new ImportCandidates(importCandidates); // 返回类型:List<String>
}
最后得到将要自动注册到容器中的 对象列表。
2、自动配置中注解
1)、@AutoConfiguration : 是 @Configuration(proxyBeanMethods = false),@AutoConfigureBefore,@AutoConfigureAfter 三者的组合,带有这个注解就表示这个是个自动配置类。
2)、@ConditionOn* : 这是一种条件注解,表示在满足指定条件时才会进行自动配置。主要有:@ConditionOnClass,@ConditionOnBean,@ConditionOnProperty;@ConditionOnMissingClass,@ConditionOnMissingBean,@ConditionOnMissingProperty。
3)、排除某个自动配置:@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})。
4)、@SpringBootConfiguration:替代 @Configuration 注解。
5)、@EnableAutoConfiguration:开启自动配置。
这些是主要的注解,足以看懂源码。
3、自定义 starter
自定义一个 starter ,并且使用自动配置功能,帮助彻底搞懂自动配置原理。
3.1、定义 pom.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<!-- 第三方starter 定义的方法 -->
<artifactId>test-spring-boot-starter</artifactId>
<version>1.0</version>
<name>autoconfiguration</name>
<description>autoconfiguration</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>3.0.0</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.2、定义属性类
// 属性类
@Data
@ConfigurationProperties(prefix = "weilong.param")
public class TestProperties {
private String name;
private Integer age;
}
3.3、编写自动配置类,使用容器中的属性类读取配置文件中的属性值。
@AutoConfiguration
@ConditionalOnProperty(prefix = "test.enable", name = "isauto", havingValue = "true") // 决定是否被自动注册
@EnableConfigurationProperties({TestProperties.class}) // 将属性类加载到容器
public class TestAutoConfiguration {
@Bean
public TestClass testClass(TestProperties testProperties){
TestClass testClass = new TestClass();
testClass.setAge(testProperties.getAge());
testClass.setName(testProperties.getName());
return testClass;
}
}
3.4、定义实体类
@Data
public class TestClass {
private String name;
private Integer age;
public String test(){
return "hello "+ name + " autoConfiguration: " + age;
}
}
3.5、编写自动配置文件
在 resources 目录下创建 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,内容如下。
com.test.autoconfiguration.test.TestAutoConfiguration
4、引入自定义的 starter ,并且测试自动配置功能
新建一个模块,导入自定义的 starter
4.1、pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>test-spring-boot-starter</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
4.2、配置文件 application.yml
test:
enable:
isauto: true # 决定是否开启自动配置
param:
name: test
age: 1
4.3、主启动类
启动主启动类,发现 TestClass 不必导入就可以直接使用,说明已经被自动配置。
@SpringBootApplication
@Slf4j
public class AutoCOnfigurationtestApplication {
public static void main(String[] args) {
SpringApplication.run(AutoCOnfigurationtestApplication.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(TestClass testClass){
return args -> log.info(testClass.test());
}
}
总结:以上是自动配置原理解读,通过手写 starter,注册自动配置类 ,进一步理解自动配置原理,还有很多内容没有涉及,需要阅读源码进一步深入细节。
本人是一个从小白自学计算机技术,对运维、后端、各种中间件技术、大数据等有一定的学习心得,想获取自学总结资料(pdf版本)或者希望共同学习,关注微信公众号:it自学社团。后台回复相应技术名称/技术点即可获得。(本人学习宗旨:学会了就要免费分享)