简介
Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、RabbitMQ等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。
前置知识
批量注册bean的方式
- 通过@Bean标注方法的方式,一个个来注册
- @CompontentScan的方式:默认的@CompontentScan是无能为力的,默认情况下只会注册@Compontent标注的类
- @Import
前两种方式,不适用于频繁变化的场景,且有局限性,所以SpringBoot作为脚手架,自动装配第三方类显然第三种方式更适合;
因此我们首先来了解下@Import注解;
@Import注解
作用:@Import可以用来批量导入需要注册的各种类,如普通的类、配置类,然后完成普通类和配置类中所有bean的注册。
支持如下三种方式:
1、直接导入普通类
- 创建一个普通类
kotlin
package com.doudou.imports;
public class DouDouBean {
public String print() {
return "return doudou bean";
}
}
- 创建一个配置类,导入刚创建的类
less
@Configuration
@Import({DouDouBean.class})
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
DouDouBean douDouBean = context.getBean(DouDouBean.class);
System.out.println(douDouBean.print());
}
}
- 控制台输出结果

2、配合自定义的 ImportSelector 使用
ImportSelector是一个接口,该接口提供了一个selectImports方法,用来返回全类名数组;可以通过这种方式,动态导入N个bean
- 创建普通类
kotlin
@Configuration
public class DouDouConfig {
@Bean
public String RedBeanBun() {
return "Red Bean Bun";
}
@Bean
public String OatBag() {
return "Oat Bag";
}
}
typescript
public class DouDouBean {
public String print() {
return "return doudou bean";
}
}
- 实现ImportSelector接口
typescript
public class DouDouImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {
DouDouBean.class.getName(),
DouDouConfig.class.getName()
};
}
}
- 创建一个配置类,导入实现了ImportSelector接口的类
less
@Configuration
@Import({DouDouImportSelector.class})
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(String.format("%s=%s", name, context.getBean(name)));
}
}
}
- 控制台输出结果

2.1 重点:DeferredImportSelector
装个杯,如果让你实现一个框架,比如希望用户如果实现了自己的配置类,跟SpringBoot自动加载的配置类相同,希望以用户的为准,你会怎么做?
SpringBoot实现上述需要就是使用了DeferredImportSelector;
SpringBoot中的核心功能@EnableAutoConfiguration就是靠DeferredImportSelector来实现的;
DeferredImportSelector是ImportSelector的子接口,所以也能够通过@Import导入,跟ImportSelector不同点在于:
- 分组
- 延时导入
- 创建需要延迟导入的配置类
typescript
@Configuration
public class DeferredConfig {
@Bean
public String dog() {
return "dog";
}
}
- 实现DeferredImportSelector接口
typescript
public class DouDeferredImportSelect implements DeferredImportSelector {
@Override
public Class<? extends Group> getImportGroup() {
return DeferredImportSelector.super.getImportGroup();
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
DeferredConfig.class.getName()
};
}
}
- 创建一个配置类,导入实现了DeferredImportSelector接口的类
less
@Configuration
@Import({
DouDeferredImportSelect.class,
DouDouImportSelector.class})
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(String.format("%s=%s", name, context.getBean(name)));
}
}
}
- 控制台输出结果,可以发现DeferredConfig最后输出,跟Import导入顺序不一致

3、配合 ImportBeanDefinitionRegistrar 使用
ImportBeanDefinitionRegistrar也是一个接口,支持手动的注册bean到容器当中
- 创建需要被手动注册到bean容器当中的类
typescript
public class DouDefinitionRegistrarBean {
public String pig() {
return "pig";
}
}
- 实现ImportBeanDefinitionRegistrar接口
java
public class DouDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition definition = new RootBeanDefinition(DouDefinitionRegistrarBean.class);
registry.registerBeanDefinition("douDefinitionRegistrarBean", definition);
}
}
- 创建配置类,导入实现了ImportBeanDefinitionRegistrar接口的类
less
@Configuration
@Import({
DouDeferredImportSelect.class,
DouDouImportSelector.class,
DouDefinitionRegistrar.class})
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(String.format("%s=%s", name, context.getBean(name)));
}
}
}
- 控制台输出结果,可以发现DouDefinitionRegistrarBean类被注入

@Condition注解
@Condition注解能够实现在满足特定条件下配置类才生效
- 创建一个配置类,实现Condition接口,改接口仅提供了一个matches方法,返回值为boolean类型,该方法返回true,代表配置类生效;反之,不生效;
typescript
@Configuration
public class DouDouConditionBean implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
- 创建另一个配置类,依赖上述配置类matches方法来判断是否需要生效
less
@Configuration
@Conditional({DouDouConditionBean.class})
public class ConditionBeanConfig {
}
- 创建配置类,测试ConditionBeanConfig配置类是否被注入
less
@Configuration
@ComponentScan(basePackages = {"com.doudou.condition"})
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(String.format("%s=%s", name, context.getBean(name)));
}
}
}
- 控制台结果输出,ConditionBeanConfig类生效,被成功注入到IOC容器

拓展注解
@Conditional扩展注解 | (判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
1、SpringBoot自动配置分析
注意:本篇博文分析,基于SpringBoot 2.5.2版本
自动配置原理流程图
上述识别到需要自动配置的配置类,被生成BeanDefinitionMap,通过BeanFactory生成具体的Bean,该部分内容由Spring IOC完成,非SpringBoot做的事,所以一笔带过,主要让大家别太懵逼;
具体源码分析,先从启动类入手:
@SpringBootApplication注解
该注解标注该类是SpringBoot的主配置类
less
@Target(ElementType.TYPE) // 设置该注解可以标注在什么地方
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited // 是否会被继承
@SpringBootConfiguration // 表示这是SpringBoot的主配置类
@EnableAutoConfiguration // 开启自动配置
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
由此可见,最核心的注解还是 @EnableAutoConfiguration,展开分析下这个注解都由什么部分组成
less
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
可以发现,核心的就两个注解 @AutoConfigurationPackage 、 @Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage
首先分析下 @AutoConfigurationPackage
less
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
看到 @Import(AutoConfigurationPackages.Registrar.class) 这个东西,是不是很熟悉了,就是帮我们指定哪些bean需要被注册到Spring IOC容器中;

通过调试,我们可以看到,他将我们启动类所在的目录注册到Spring IOC容器中去了,换句话说,如果没有 @AutoConfigurationPackage注解,我们自己项目的类不会自动被扫描到SpringIOC容器中的;所以,总结一下,该注解主要起到了将启动类所在目录下的包扫描到SpringIOC容器中;
@Import(AutoConfigurationImportSelector.class)
再分析下 @Import(AutoConfigurationImportSelector.class)
kotlin
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
该类实现了DeferredImportSelector接口,若getImportGroup()方法返回值不为null,将会调用getImportGroup()方法返回值的process()方法;
具体堆栈如下:
makefile
process:429, AutoConfigurationImportSelector$AutoConfigurationGroup (org.springframework.boot.autoconfigure)
getImports:879, ConfigurationClassParser$DeferredImportSelectorGrouping (org.springframework.context.annotation)
processGroupImports:809, ConfigurationClassParser$DeferredImportSelectorGroupingHandler (org.springframework.context.annotation)
process:780, ConfigurationClassParser$DeferredImportSelectorHandler (org.springframework.context.annotation)
parse:193, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:331, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:247, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:311, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:112, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:746, AbstractApplicationContext (org.springframework.context.support)
refresh:564, AbstractApplicationContext (org.springframework.context.support)
refresh:145, ServletWebServerApplicationContext (org.springframework.boot.web.servlet.context)
refresh:754, SpringApplication (org.springframework.boot)
refreshContext:434, SpringApplication (org.springframework.boot)
run:338, SpringApplication (org.springframework.boot)
run:1343, SpringApplication (org.springframework.boot)
run:1332, SpringApplication (org.springframework.boot)
main:10, SpringbootApplication (com.doudou)
详细分析下org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process方法
kotlin
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static class AutoConfigurationGroup
implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 核心代码:获取需要自动加载的配置类
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
}
}
深度分析下org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry方法
scss
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取所有jar包 META-INF/spring.factories 下的key为EnableAutoConfiguration的配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 配置类去重
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 过滤出满足条件的配置类,过滤规则:META-INF/spring.factories key为AutoConfigurationImportFilter的value值
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
深度分析下核心方法org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
typescript
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// getSpringFactoriesLoaderFactoryClass方法比较简单,就是返回EnableAutoConfiguration.class
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
return configurations;
}
分析下org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
less
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 这里获取到的内容为EnableAutoConfiguration
String factoryTypeName = factoryType.getName();
// 过滤出key为EnableAutoConfiguration的配置类
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
分析下org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
typescript
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
Map<String, List<String>> result = new HashMap();
try {
// 可以看到,通过类加载机制,获取到所有jar包下META-INF/spring.factories的配置信息
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
...
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
上述多次提到key EnableAutoConfiguration、AutoConfigurationImportFilter,你可能没概念,去到具体文件,截图一下,你就明白了;

至此,需要自动加载到Spring IOC的配置类均找到了,接下来交给Spring IOC即可;
2、如何查看哪些配置类被自动加载至Spring容器中
在项目appilication.yml追加以下一条配置项,这样就能够在控制台输出哪些配置类被自动配置;
lua
debug: true
控制台输出结果(内容较多,我拣选了核心的出来):
sql
positive matches: // 以下代表被自动配置的配置类信息
-----------------
AopAutoConfiguration matched:
- @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
AopAutoConfiguration.ClassProxyingConfiguration matched:
- @ConditionalOnMissingClass did not find unwanted class 'org.aspectj.weaver.Advice' (OnClassCondition)
- @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)
DispatcherServletAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet' (OnClassCondition)
- found 'session' scope (OnWebApplicationCondition)
DispatcherServletAutoConfiguration.DispatcherServletConfiguration matched:
- @ConditionalOnClass found required class 'javax.servlet.ServletRegistration' (OnClassCondition)
- Default DispatcherServlet did not find dispatcher servlet beans (DispatcherServletAutoConfiguration.DefaultDispatcherServletCondition)
Negative matches: // 以下代表条件缺失,没有被自动配置的配置类
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
AopAutoConfiguration.AspectJAutoProxyingConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.aspectj.weaver.Advice' (OnClassCondition)
ArtemisAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
BatchAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'org.springframework.batch.core.launch.JobLauncher' (OnClassCondition)
Exclusions: // 哪些类被排除
-----------
None
3、单独分析一个满足自动装配条件的类
以HttpEncodingAutoConfiguration类为例
less
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final Encoding properties;
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
return filter;
}
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class) 该注解表示 该配置类对应的配置文件对应的class类,并且将ServerProperties对应的javaBean加入到Spring IOC容器中
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) web环境下生效
@ConditionalOnClass(CharacterEncodingFilter.class) CharacterEncodingFilter类存在,配置类则生效
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
配置中是否存在某个配置项为server.servlet.encoding,如果不存在,则能够匹配;如果matchIfMissing为false,则需要存在才能够匹配;
4、分析下配置项和配置类如何互相绑定
以ServerProperties为例
swift
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
}
ServerProperties是通过@ConfigurationProperties将配置文件与该类进行绑定;
因此你在application.properties或application.yml中给服务绑定端口号时,实际上就给ServerProperties属性赋值;
yaml
server:
port: 8080
5、自定义starter
简介
SpringBoot最强大的功能就是把我们常用的场景抽取成了一个个starter(场景启动器),我们引入SpringBoot为我们提供的这些场景启动器,我们再进行少量的配置就能完成我们的功能。即使是这样,SpringBoot也不能囊括我们所有的场景,往往我们需要自定义starter,来满足我们需要的功能;
如何编写starter
我们参考spring-boot-starter

发现是一个空的jar包,分析下他的pom.xml文件
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.5.2</version>
<scope>compile</scope>
</dependency>
发现它依赖了spring-boot-autoconfigure,而spring-boot-autoconfigure包含了starter的配置及代码

因此,梳理出规则如下:

- 启动器(starter)是一个空的jar文件,仅仅提供辅助性依赖管理,这些依赖可能用于自动装配或其他类库。
- 需要专门写一个类似spring-boot-autoconfigure的配置模块
- 用的时候只需要引入启动器starter,就可以使用自动配置了
命名规范
官方命名空间
- 前缀:spring-boot-starter-
- 模式:spring-boot-starter-模块名
- 举例:spring-boot-starter-web、spring-boot-starter-jdbc
自定义命名空间
- 后缀:-spring-boot-starter
- 模式:模块-spring-boot-starter
- 举例:mybatis-spring-boot-starter
开始编写自己的starter
makefile
创建一个父工程,和2个Module:
1、doudou-parent (父工程)
2、doudou-spring-boot-starter (子模块,依赖doudou-spring-boot-starter-autoconfigure)
3、doudou-spring-boot-starter-autoconfigure
项目结构概览:

doudou-parent:
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.doudou</groupId>
<artifactId>doudou-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>doudou-parent</name>
<description>doudou-parent</description>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
</dependencies>
<modules>
<module>doudou-spring-boot-starter</module>
<module>doudou-spring-boot-starter-autoconfigure</module>
</modules>
</project>
doudou-spring-boot-starter:
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>
<parent>
<groupId>com.doudou</groupId>
<artifactId>doudou-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>doudou-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>doudou-spring-boot-starter</name>
<description>doudou-spring-boot-starter</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.doudou</groupId>
<artifactId>doudou-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
doudou-spring-boot-starter-autoconfigure:
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>
<parent>
<groupId>com.doudou</groupId>
<artifactId>doudou-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>doudou-spring-boot-starter-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>doudou-spring-boot-starter-autoconfigure</name>
<description>doudou-spring-boot-starter-autoconfigure</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
DoudouAutoConfiguration:
less
@Configuration
@EnableConfigurationProperties(DouDouProperties.class)
@ConditionalOnProperty(value = "doudou.name")
public class DoudouAutoConfiguration {
}
DouDouProperties:
typescript
@ConfigurationProperties("doudou")
public class DouDouProperties {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
DouDouController:
kotlin
@RestController
public class DouDouController {
@Autowired
private DouDouProperties douDouProperties;
@RequestMapping("/test/doudou/custom/starter")
public String indexController() {
return douDouProperties.getName();
}
}
spring.factories:
ini
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.doudou.configuration.DoudouAutoConfiguration
外部程序依赖自定义的starter
额外搭建一个SpringBoot工程,依赖doudou-spring-boot-starter
备注:这个搭建比较简单,我主要展示效果
第一步:增加pom依赖
xml
<dependency>
<groupId>com.doudou</groupId>
<artifactId>doudou-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
其次在application.yml增加配置:
yaml
doudou:
name: doudou
启动服务,访问starter提供的接口

6、总结
如何在面试中回答SpringBoot自动配置原理?
我会从核心注解入手,分析原理:
1、@SpringBootApplication注解标注该工程是一个SpringBoot工程,该注解包含两个核心注解@SpringBootConfiguration、@EnableAutoConfiguration;
2、@SpringBootConfiguration本质就是@Configuration,表示是一个配置类;
3、@EnableAutoConfiguration注解,包含两个核心注解@AutoConfigurationPackage、@Import(AutoConfigurationImportSelector.class);
4、@AutoConfigurationPackage注解核心在于@Import(AutoConfigurationPackages.Register.class),该Register类实现了ImportBeanDefinitionRegister接口,我们知道实现该接口,实际上就是手动注册BeanDefinition至Spring IOC容器中,SpringBoot通过@Import(AutoConfigurationPackages.Register.class)实现,将启动类所在目录注册到Spring IOC容器中;这样,我们项目定义的class就能够被Spring IOC注册成bean;
5、@Import(AutoConfigurationImportSelector.class)注解,其中AutoConfigurationImportSelector类实现了DeferredImportSelector接口,DeferredImportSelector接口是ImportSelector的子接口,我们知道实现了ImportSelector接口,主要目的在于手动导入类将其注册成为bean,而DefeeredImportSelector多了一个延时导入的特性,主要目的在于延时导入各种starter jar包下META-INF/spring.factories里面key为EnableAutoConfiuration的配置类,再按照一定过滤规则,过滤出最终需要自动配置的配置类;为什么要延时导入,因为如果外部自定义的配置类跟SpringBoot自动配置的一样,那么注册外部自定义的配置类至Spring IOC容器中,SpringBoot自动配置的无效;
参考博客
1、mp.weixin.qq.com/s?__biz=Mzg...
3、blog.csdn.net/K_520_W/art...
源码下载地址