Spring-boot自动配置原理
- 启动类
SpringAutoconfigurationStudyApplication - spring-boot注解
@SpringBootApplication
1.@SpringBootApplication
进入@SpringBootApplication 注解内部
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
}
@SpringBootConfiguration 表名这是一个spring boot 应用,可以被@Configuration 替换
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
}
2.@EnableAutoConfiguration
其中@EnableAutoConfiguration 为自动配置的核心内容
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
@AutoConfigurationPackage 注入启动类所在的包或者自定义的包到容器中
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
进入Registrar.class 可知,注册了当前启动类所在包到容器中
2.1@AutoConfigurationImportSelector
@Import(AutoConfigurationImportSelector.class) 将配置类导入容器当中
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
进入AutoConfigurationImportSelector.class 中,
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
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);
}
其中getCandidateConfigurations 方法用于获取配置类名称,点进去查看
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
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;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
return result;
}
从下图可以看出我们自定义的starter下的META-INF/spring.factories 已经被扫描到
自定义的配置类被加入result中
修改了包名的样子
3.自定义starter
3.1.项目包结构
--src
-main
-java
-org
-test
-CountProperties
-CountServer
-CountServerAutoConfiguration
-resources
-META-INF
-spring.factories
maven 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.test</groupId>
<artifactId>demo-autoconfiguration</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.6.4</version>
</dependency>
</dependencies>
</project>
CountProperties
@ConfigurationProperties(prefix = "count")
public class CountProperties {
private Integer number;
private String type = "订单项";
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
CountServer
public class CountServer {
@Autowired
private CountProperties countProperties;
public Integer getItemNum(){
return countProperties.getNumber();
}
}
CountServerAutoConfiguration
@Configuration
@ConditionalOnClass(CountServer.class)
@EnableConfigurationProperties(CountProperties.class)
@ConditionalOnProperty(prefix = "count.server",name = "enable",havingValue = "true",matchIfMissing = true)
public class CountServerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(CountServer.class)
public CountServer countServer(){
return new CountServer();
}
}
spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.test.CountServerAutoConfiguration
3.2加入本地仓库
mvn clean install
3.3其他spring项目
创建一个项目,依赖之前的项目
<dependency>
<groupId>org.test</groupId>
<artifactId>demo-autoconfiguration</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
在项目中引入代码
@RestController
public class CountWeb {
@Resource
private CountServer countServer;
@GetMapping("/item")
public Integer getCount(){
return countServer.getItemNum();
}
}
在application.properties添加如下内容
count.number=10
#count.server.enable=false
启动成功,说明bean注入容器成功
浏览器访问http://localhost:8080/item
返回10,说明配置成功。
综上,自动配置主要靠@EnableAutoConfiguration 然后解析出spring.factories下配置的配置类,spring在幕后将其注入factory当中。
参考: 1.https://blog.csdn.net/zjcjava/article/details/84028222
|