一、前言
SpringBoot核心精髓就是约定优于配置 ,不用手动XML、不用大量@Bean手动注册组件;核心驱动是 @EnableAutoConfiguration,再配合一大批@Conditional派生条件注解,实现「类存在、Bean不存在、配置项开启时,才自动向IOC容器注入对应组件」。
前置知识点:
- Spring3+注解驱动IOC、
@Configuration、@Bean、@ComponentScan;- SPI机制(Java原生服务发现);
- Spring4条件注解体系。
二、启动类复合注解拆解:@SpringBootApplication
java
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
点开注解源码:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
// ...
}
三大核心合成:
@Configuration:当前启动类是配置类;@ComponentScan:默认扫描启动类所在包及其子包下所有@Component、@Service、@Controller等组件;@EnableAutoConfiguration:自动配置开关,整个自动配置的入口。
三、@EnableAutoConfiguration 底层执行流程
1. 注解源码
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// ...
}
关键:@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector 实现了DeferredImportSelector接口,Spring容器刷新阶段会执行其选择导入配置类的逻辑。
2. 完整加载步骤
AutoConfigurationImportSelector借助SpringBoot内置SPI,读取所有依赖包中
META-INF/spring.factories文件;- 该文件中key为
org.springframework.boot.autoconfigure.EnableAutoConfiguration,value是一大批自动配置类全限定名 (如WebMvcAutoConfiguration、DataSourceAutoConfiguration、RedisAutoConfiguration); - 批量拿到这些自动配置类,加载进Spring上下文;
- 每个自动配置类内部都标注了大量**
@Conditional派生条件注解**; - 条件全部满足:该配置类里的
@Bean才会实例化,注入IOC容器;条件不满足,直接跳过。
3. spring.factories
properties
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
引入对应starter依赖,该配置类才会被扫描到,无依赖则SPI不会加载。
四、核心:@Conditional 派生条件注解体系
1. 顶层父注解:@Conditional
java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Conditional {
Class<? extends Condition>[] value();
}
作用:自定义条件规则,实现Condition接口matches()方法,返回true则生效,false跳过。
SpringBoot在此基础上封装了大量开箱即用的派生注解,无需手写Condition。
2. SpringBoot高频内置派生条件注解(分类)
(1)类存在判定:依赖是否引入
@ConditionalOnClass:classpath中存在指定类,才生效;@ConditionalOnMissingClass:classpath不存在指定类,才生效。
示例(WebMvc自动配置):
java
@Configuration
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
public class WebMvcAutoConfiguration {
// ...自动注册视图解析器、静态资源处理器、转换器等Bean
}
只要引入spring-boot-starter-web,Servlet、DispatcherServlet类存在,WebMvc自动配置才启用。
(2)容器Bean判定:容器内是否已有该组件
@ConditionalOnBean:IOC容器中已经存在指定Bean,才创建;@ConditionalOnMissingBean:容器中不存在该Bean,才自动注册(最常用!)。
典型场景:用户自定义了MessageConverter,SpringBoot就不再自动注入默认的转换器,实现自定义覆盖默认配置。
java
@Bean
@ConditionalOnMissingBean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
return new MappingJackson2HttpMessageConverter();
}
(3)配置文件属性判定:application.yml是否配置对应key
@ConditionalOnProperty:根据配置文件属性值匹配,开启/关闭组件;
java
// 配置项 my.datasource.enabled=true 才生效
@ConditionalOnProperty(prefix = "my.datasource", name = "enabled", havingValue = "true")
(4)Web环境区分
@ConditionalOnWebApplication:仅Web环境生效;@ConditionalOnNotWebApplication:非Web(Jar命令行、定时任务)环境生效。
(5)资源、系统属性等其他
@ConditionalOnResource、@ConditionalOnJava、@ConditionalOnJndi等,日常使用频率低。
五、拿WebMvcAutoConfiguration完整举例串联整条链路
- 项目引入
spring-boot-starter-web,依赖中存在spring.factories,携带WebMvcAutoConfiguration; @EnableAutoConfiguration扫描到该配置类,加载进上下文;- 类上
@ConditionalOnClass(Servlet.class)校验通过; - 内部
@Bean方法标注@ConditionalOnMissingBean;- 开发者没手动注册
DispatcherServlet、视图解析器 → SpringBoot自动创建并注入; - 开发者自己
@Bean重写了组件 → 条件不满足,自动配置跳过,以自定义Bean为准;
- 开发者没手动注册
- 最终SpringMVC全套组件自动装配完成,无需XML。
六、application.yml配置绑定原理(@ConfigurationProperties)
自动配置类不会硬编码写死端口、连接地址,而是绑定配置文件属性:
- 自动配置类内绑定属性实体类标注
@ConfigurationProperties(prefix = "server"); - SpringBoot自动把
server.port、server.servlet.context-path等yml配置注入属性对象; - 再把属性对象传入自动配置的Bean(如Tomcat容器、DispatcherServlet)。
示例:
java
@ConfigurationProperties(prefix = "server")
public class ServerProperties {
private Integer port = 8080;
}
这就是修改yml就能修改内嵌Tomcat端口的底层原因。
七、排除指定自动配置类(禁用自动装配)
方式1:启动类注解排除
java
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class DemoApplication {}
场景:纯Web静态项目,无数据库,排除数据源自动配置,避免报错。
方式2:yml配置排除
yaml
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
八、完整执行时序总结
- 启动类
@SpringBootApplication包含@EnableAutoConfiguration; AutoConfigurationImportSelector通过SPI读取所有starter内spring.factories,拿到候选自动配置类;- 逐个加载配置类,校验类上
@ConditionalOnClass等外层条件; - 条件通过后,执行内部
@Bean方法,再校验方法上@ConditionalOnMissingBean; - 校验全部通过 → Bean实例化,放入IOC容器;任意条件不满足 → 跳过该组件装配;
- 配置文件属性通过
@ConfigurationProperties绑定,修改组件运行参数。
九、高频面试题
1)@EnableAutoConfiguration做了什么?
借助AutoConfigurationImportSelector + Java SPI,扫描所有依赖包META-INF/spring.factories中定义的自动配置类,批量导入到Spring容器,开启自动装配总开关。
2)@ConditionalOnClass和@ConditionalOnMissingBean区别?
@ConditionalOnClass:检测classpath下是否存在对应类,用来判断是否引入了对应starter依赖;@ConditionalOnMissingBean:检测IOC容器有没有用户自定义的Bean,用户自定义优先,框架默认Bean兜底。
3)为什么引入starter不用手动配置组件?
starter内部依赖包含自动配置类,写入spring.factories;EnableAutoConfiguration加载配置类;条件注解校验依赖、Bean、配置项后自动创建组件,实现零XML配置。