# Springboot 自动装配
# 核心注解 @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
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {}
@SpringBootApplication 组合了很多注解:
- @EnableAutoConfiguration :启用 SpringBoot 的自动配置
- @AutoConfigurationPackage:主配置类(@SpringBootApplication 标注的类)的所在包以及下面所有子包里面的所有组件扫描到 Spring 容器。
@Import(AutoConfigurationImportSelector.class):自动装配核心功能的实现
- @ComponentScan:扫描 @Component(@Controller、@Service、@Repository 都包含此注解)并注入容器
- @SpringConfiguration
- Configuration:允许在 ApplicationContext 中注册额外的 Bean 或导入其他配置类
- Indexed: 项目编译打包时,会在自动生成 META-INF/spring.components 文件
# @EnableAutoConfiguration
@EnableAutoConfiguration 注解中导入了 AutoConfigurationImportSelector
类。
AutoConfigurationImportSelector
类实现了 ImportSelector
接口,也就实现了这个接口中的 selectImports
方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IOC 容器中。
@Override | |
public String[] selectImports(AnnotationMetadata annotationMetadata) { | |
// 判断自动装配是否启动 | |
if (!isEnabled(annotationMetadata)) { | |
return NO_IMPORTS; | |
} | |
// 获取所有自动装配的 bean | |
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); | |
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); | |
} |
其中 getAutoConfigurationEntry(annotationMetadata)
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { | |
// 判断是否开启自动装配(spring.boot.enableautoconfiguration 默认为 true) | |
if (!isEnabled(annotationMetadata)) { | |
return EMPTY_ENTRY; | |
} | |
// 获取 EnableAutoConfiguration 注解中的属性 exclude 和 excludeName 排除 | |
AnnotationAttributes attributes = getAttributes(annotationMetadata); | |
// 拿到 starter 包中 Auto 配置模块的 META-INF/spring.factories 文件下的自动配置类列表(条件装配) | |
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); | |
// 去重 | |
configurations = removeDuplicates(configurations); | |
// 获取注解的 exclude 和 excludeName 属性配置的需要排除的自动配置类全限定名的集合 | |
Set<String> exclusions = getExclusions(annotationMetadata, attributes); | |
// 检查并排除类(如果类不在 configurations 中报错此类不是自动配置类) | |
checkExcludedClasses(configurations, exclusions); | |
// 移除所有 exclude 和 excludeName 属性配置的需要排除的自动配置类 | |
configurations.removeAll(exclusions); | |
// 对 configurations 进行过滤,剔除掉条件不成立的配置类,@Conditional 有关 | |
configurations = getConfigurationClassFilter().filter(configurations); | |
// 监听器 import 事件回调 | |
fireAutoConfigurationImportEvents(configurations, exclusions); | |
return new AutoConfigurationEntry(configurations, exclusions); | |
} |
最后方法返回了一个 AutoConfigurationEntry
,它是 AutoConfigurationImportSelector 的内部类
protected static class AutoConfigurationEntry { | |
private final List<String> configurations; | |
private final Set<String> exclusions; | |
private AutoConfigurationEntry() { | |
this.configurations = Collections.emptyList(); | |
this.exclusions = Collections.emptySet(); | |
} | |
/** | |
* Create an entry with the configurations that were contributed and their | |
* exclusions. | |
* @param configurations the configurations that should be imported | |
* @param exclusions the exclusions that were applied to the original list | |
*/ | |
AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) { | |
this.configurations = new ArrayList<>(configurations); | |
this.exclusions = new HashSet<>(exclusions); | |
} | |
public List<String> getConfigurations() { | |
return this.configurations; | |
} | |
public Set<String> getExclusions() { | |
return this.exclusions; | |
} | |
} |
最后 selectImports(AnnotationMetadata annotationMetadata)
方法返回一个配置类的全限定名数组。
# 自定义 Starter
代码参考:尚硅谷 springboot2 课程
准备一个空工程
新建一个 Maven 模块,命名为
XXX-spring-boot-start
充当启动器pom 文件:
<?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>per.windlinxy</groupId>
<artifactId>windlinxy-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>per.windlnxy</groupId>
<artifactId>windlinxy-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
再使用 Spring Initializr 创建一个模块,命名为
XXX-spring-boot-start-autoconfigure
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>per.windlnxy</groupId>
<artifactId>windlinxy-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>windlinxy-spring-boot-starter-autoconfigure</name>
<description>windlinxy-spring-boot-starter-autoconfigure</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
在
XXX-spring-boot-start-autoconfigure
模块编写代码使用 maven 进行 install 将两个模块注入到本地 maven 仓库
per
├─windlinxy
│ └─windlinxy-spring-boot-starter
│ └─1.0-SNAPSHOT
└─windlnxy
└─windlinxy-spring-boot-starter-autoconfigure
└─0.0.1-SNAPSHOT
在另外的项目进行注册
<dependency>
<groupId>per.windlinxy</groupId>
<artifactId>windlinxy-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
使用
配置文件:
hello:
prefix: 11
suffix: 666
Controller:
@RestController
public class HelloController {
@Resource
private HelloService helloService;
@GetMapping("/hello")
public String sayHello() {
return helloService.sayHello("张三");
}
}
HelloServiceAutoConfiguration
自动配置类
@Configuration | |
@ConditionalOnMissingBean(HelloService.class) | |
// 会将 HelloProperties 自动注入容器 | |
@EnableConfigurationProperties(HelloProperties.class) | |
public class HelloServiceAutoConfiguration { | |
@Bean | |
public HelloService helloService() { | |
return new HelloService(); | |
} | |
} |
HelloProperties
@ConfigurationProperties("hello") | |
public class HelloProperties { | |
private String prefix; | |
private String suffix; | |
public String getPrefix() { | |
return prefix; | |
} | |
public void setPrefix(String prefix) { | |
this.prefix = prefix; | |
} | |
public String getSuffix() { | |
return suffix; | |
} | |
public void setSuffix(String suffix) { | |
this.suffix = suffix; | |
} | |
} |
HelloService
// 默认不放入容器 | |
public class HelloService { | |
@Autowired | |
private HelloProperties helloProperties; | |
public String sayHello(String username) { | |
return helloProperties.getPrefix() + ": " + username + helloProperties.getSuffix(); | |
} | |
} |
resources/META-INF/spring.factiories 文件
# Auto Configure | |
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ | |
per.windlnxy.auto.HelloServiceAutoConfiguration |