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
替换
/**
* Indicates that a class provides Spring Boot application
* {@link Configuration @Configuration}. Can be used as an alternative to the Spring's
* standard {@code @Configuration} annotation so that configuration can be found
* automatically (for example in tests).
*/
@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
注入启动类所在的包或者自定义的包到容器中
/**
* Registers packages with {@link AutoConfigurationPackages}. When no {@link #basePackages
* base packages} or {@link #basePackageClasses base package classes} are specified, the
* package of the annotated class is registered.
*
* @author Phillip Webb
* @since 1.3.0
* @see AutoConfigurationPackages
*/
@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);
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//加载所有的META-INF/spring.factories下键值对的value
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 {
//FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
//类加载器读取spring.factories下的内容
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的value当中
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
/**
* 下方一个条件不满足则不会配置
* @date 2022/3/29
*/
@Configuration //标记为配置类
@ConditionalOnClass(CountServer.class) //当该类存在时才会进行配置
@EnableConfigurationProperties(CountProperties.class) //开启属性类配置
@ConditionalOnProperty(prefix = "count.server",name = "enable",havingValue = "true",matchIfMissing = true)
public class CountServerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(CountServer.class) //CountServer这个bean对象在容器中不存在才会注入
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