# Springboot 自动装配

# 核心注解 @SpringBootApplication

1
2
3
4
5
6
7
8
9
@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

1
2
3
4
5
6
@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 容器中

1
2
3
4
5
6
7
8
9
10
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断自动装配是否启动
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 获取所有自动装配的bean
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

其中 getAutoConfigurationEntry(annotationMetadata)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 的内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 课程

image-20230228212127676

  1. 准备一个空工程

  2. 新建一个 Maven 模块,命名为 XXX-spring-boot-start 充当启动器

    pom 文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?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>

  3. 再使用 Spring Initializr 创建一个模块,命名为 XXX-spring-boot-start-autoconfigure

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    <?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>

  4. XXX-spring-boot-start-autoconfigure 模块编写代码

  5. 使用 maven 进行 install 将两个模块注入到本地 maven 仓库

    1
    2
    3
    4
    5
    6
    7
    per
    ├─windlinxy
    │ └─windlinxy-spring-boot-starter
    │ └─1.0-SNAPSHOT
    └─windlnxy
    └─windlinxy-spring-boot-starter-autoconfigure
    └─0.0.1-SNAPSHOT

  6. 在另外的项目进行注册

    1
    2
    3
    4
    5
    <dependency>
    <groupId>per.windlinxy</groupId>
    <artifactId>windlinxy-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
    </dependency>

  7. 使用

    配置文件:

    1
    2
    3
    hello:
    prefix: 11
    suffix: 666

    Controller:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @RestController
    public class HelloController {
    @Resource
    private HelloService helloService;

    @GetMapping("/hello")
    public String sayHello() {
    return helloService.sayHello("张三");
    }
    }

HelloServiceAutoConfiguration 自动配置类

1
2
3
4
5
6
7
8
9
10
@Configuration
@ConditionalOnMissingBean(HelloService.class)
// 会将HelloProperties自动注入容器
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
@Bean
public HelloService helloService() {
return new HelloService();
}
}

HelloProperties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@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

1
2
3
4
5
6
7
8
9
// 默认不放入容器
public class HelloService {
@Autowired
private HelloProperties helloProperties;

public String sayHello(String username) {
return helloProperties.getPrefix() + ": " + username + helloProperties.getSuffix();
}
}

resources/META-INF/spring.factiories 文件

1
2
3
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
per.windlnxy.auto.HelloServiceAutoConfiguration

更新于 阅读次数 本文阅读量:

请我喝[茶]~( ̄▽ ̄)~*

Windlinxy 微信支付

微信支付

Windlinxy 支付宝

支付宝