SpringBoot 自动配置
约定大于配置
SpringBoot 自动配置的关键概念:约定大于配置
“约定大于配置”(Convention over Configuration)是一种软件设计原则,强调减少开发者需要进行的配置工作,而是通过约定的默认设置来使开发变得更加简洁。
旨在通过预先定义合理的默认行为和约定,减少开发者需要手动编写的配置。其核心思想是:“如果你遵循框架的约定,就无需显式配置;只有需要偏离约定时,才需要额外配置”。
主要思想
- 默认约定:制定了一些列默认的规则和约定
- 配置的最小化:尽量减少开发者需要进行的显式配置,将配置的工作交给框架或者工具来完成
- 统一规范:使用广泛的共同约定,使得开发者能够遵循一致的开发模式
如何体现
- 项目结构约定
- 默认目录结构:
src/main/java
存放源代码,src/main/resources
存放资源文件,启动类位于根包下,SpringBoot 会自动扫描其子包中的组件 - 启动类位于根包下是,SpringBoot 会自动扫描其子包中的组件
- 默认目录结构:
- 自动配置
- 条件发 Bean 加载:若引入
spring-boot-starter-web
,自动配置 Tomcat 和 Spring MVC - 覆盖默认配置:通过
application
配置文件来覆盖默认配置
- 条件发 Bean 加载:若引入
- 配置文件约定
- 默认配置文件:配置文件为
application.yaml
,application.properties
等 - 环境差异化配置:通过
application-dev.properties
(开发环境)和application-prod.properties
(生产环境)区分配置,无需代码修改,只需激活对应 Profile
- 默认配置文件:配置文件为
JavaConfig 配置方式
JavaConfig 是 Spring 框架中一种基于 Java 的配置方式,旨在替代传统的 XML 配置。通过使用注解和 Java 类,开发者可以用更简洁、类型安全且面向对象的方式定义 Spring 容器的 Bean 和依赖关系。
核心概念:使用注释来描述 Bean 配置的组件
核心作用
- 取代 XML:用 Java 代码替代 XML,减少配置文件的冗余
- 类型安全:编译时检查配置的正确性,避免 XML 中的字符串拼写错误
- 面向对象优势:支持继承、多态等特性,便于复用和模块化配置
- 与注解驱动开发结合:无缝整合
@Component
、@Autowired
等注解,简化依赖注入
核心注解
@Configuration
:标记一个类为配置类,Spring 会将其视为 Bean 定义的来源@Bean
:在配置类中标记方法,方法的返回值会被注册为 Spring Bean@ComponentScan
:自动扫描指定包下的组件(如@Component
、@Service
等注解标记的类)@Import
:引入其他配置类,实现模块化配置
总结
- 通过
@configuration
注解的配置类创建哪些 Bean - 通过条件注解来满足创建条件
- 通过配置属性来创建 Bean 的属性
自动配置原理
可以根据启动类中的 @SpringBootApplication
核心注解来详细解释自动化配置原理
@SpringBootApplication
public class SpringbootDemoApplication {public static void main(String[] args) {SpringApplication.run(SpringbootDemoApplication.class, args);}}
点击 @SpringBootApplication
注解看到该注解组成,可以发现 @SpringBootApplication
注解是组合注解,其中 @SpringBootConfiguration
,@EnableAutoConfiguration
以及 @ComponentScan
这三个注解最为重要
@SpringBootConfiguration
:继承自 Configuration,支持 JavaConfig 的方式进行配置@EnableAutoConfiguration
:用于开启自动配置@ComponentScan
:自动扫描组件,默认扫描该类所在包及其子包下所有带有指定注解的类,将它们自动装配到bean容器中,会被自动装配的注解包括@Controller、@Service、@Component、@Repository等。也可以指定扫描路径
@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
实际上是一个特殊的 @Configuration
注解,标注在某类上,说明该类为 SpringBoot 的配置类
直接使用 @Configuration
注解并不会有太多与 SpringBoot 特定的功能整合,@SpringBootConfiguration
注解的出现更多的是为了标明这是一个由 SpringBoot 管理的配置类,@Configuration是Spring下的配置类注解,@SpringBootConfiguration是SpringBoot下的配置类注解,但二者的本质相同
@EnableAutoConfiguration
先看 @EnableAutoConfiguration
注解的源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}
注解源码中可以看到 @Import(AutoConfigurationImportSelector.class)
注解,这个注解的作用:
ImportSelector
是 Spring 提供的接口,用于动态选择需要导入的配置类,也就是不需要 new 对象,也不需要给这个对象加上任何注解,直接使用该类皆可- 也就是在
@EnableAutoConfiguration
中将AutoConfigurationImportSelector
配置类注入到了 Spring 容器中,使得可以直接使用这些类 AutoConfigurationImportSelector
实现了DeferredImportSelector
(延迟导入),确保自动配置类在所有其他配置类处理完成后加载
在 AutoConfigurationImportSelector
类中主要分析 selectImports
方法,在启动时会调用 selectImports
方法,返回需要加载的自动配置类列表
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {@Override//作用就是Spring会把这个方法返回的数组中所有全限定名的类注入到Spring容器中public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}// 加载所有候选自动配置类AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);// 返回最终生效的配置类return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
}
在 selectImports
方法中最关键就是 getAutoConfigurationEntry
方法,因此需要重点分析这个方法
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {// 检查自动配置功能是否开启,默认开启if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}AnnotationAttributes attributes = getAttributes(annotationMetadata);// 获取候选配置类List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);// 去除重复的配置类configurations = removeDuplicates(configurations);// 获得注解中被exclude和excludeName排除的类的集合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
方法中时获取候选配置类,根据报错信息中可以看出,配置文件存放在 org.springframework.boot.autoconfigure.AutoConfiguration.imports
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).getCandidates();Assert.notEmpty(configurations,"No auto configuration classes found in "+ "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;}
}
也可以在 ImportCandidates.load
方法的 String.format("META-INF/spring/%s.imports", annotation.getName())
中可以找出自动配置文件的路径
public final class ImportCandidates implements Iterable<String> {public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {Assert.notNull(annotation, "'annotation' must not be null");ClassLoader classLoaderToUse = decideClassloader(classLoader);String location = String.format("META-INF/spring/%s.imports", annotation.getName());Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);List<String> importCandidates = new ArrayList();while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();importCandidates.addAll(readCandidateConfigurations(url));}return new ImportCandidates(importCandidates);}
}
自动配置文件
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
......
参考
- 芋道 Spring Boot 自动配置原理 | 芋道源码 —— 纯源码解析博客
- Spring Boot 自动配置原理懂后轻松写一个自己的 starter - 程序员 xiaozhang - 博客园
- @SpringBootApplication和@SpringBootConfiguration的关系 - 文采杰出 - 博客园