目录
一、@EnableAutoConfiguration 核心入口源码解析
2. AutoConfigurationImportSelector 核心执行流程
3. SpringFactoriesLoader SPI 加载源码
3. @ConditionalOnMissingBean 源码解析
4. @ConditionalOnProperty 源码解析
2. MybatisAutoConfiguration 全类源码解析
一、@EnableAutoConfiguration 核心入口源码解析
自动配置的核心触发点就是 @EnableAutoConfiguration 注解,它被组合在 @SpringBootApplication 中,是所有自动配置类的总开关。
1. 注解结构源码
// org.springframework.boot.autoconfigure.EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 全局开关配置项:配置spring.boot.enableautoconfiguration=false可关闭所有自动配置
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
// 按类型排除指定自动配置类
Class<?>[] exclude() default {};
// 按全类名字符串排除指定自动配置类
String[] excludeName() default {};
}
解析:
@AutoConfigurationPackage:将主启动类所在的包路径注册到容器中,作为后续组件扫描、自动配置包扫描的根路径。@Import(AutoConfigurationImportSelector.class):这是自动配置的核心,通过 Spring 的ImportSelector机制,批量向容器中导入所有自动配置类。exclude/excludeName:提供手动排除能力,用户可以关闭不需要的自动配置。
2. AutoConfigurationImportSelector 核心执行流程
selectImports() 方法是 ImportSelector 的核心方法,返回的字符串数组就是最终要导入到容器的配置类全限定名,完整执行逻辑如下:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 1. 校验全局开关:spring.boot.enableautoconfiguration=false则直接返回空,不加载任何自动配置
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 2. 加载所有候选自动配置类 + 执行第一阶段条件过滤
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
// 3. 返回最终符合条件的配置类全限定名
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
核心方法 getAutoConfigurationEntry 完整步骤:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 读取注解上的exclude属性,获取用户指定排除的配置类
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 1. SPI加载:从spring.factories读取所有EnableAutoConfiguration对应的配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 2. 去重:移除重复的配置类
configurations = removeDuplicates(configurations);
// 3. 排除:移除用户通过exclude/excludeName指定的配置类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 4. 第一阶段过滤:执行AutoConfigurationImportFilter,按类存在性过滤
configurations = getConfigurationClassFilter().filter(configurations);
// 5. 发布自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
3. SpringFactoriesLoader SPI 加载源码
所有自动配置类的发现都依赖 SpringFactoriesLoader,这是 Spring Boot 扩展机制的底层基石。
// org.springframework.core.io.support.SpringFactoriesLoader
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
// 从缓存中读取,没有则扫描所有spring.factories文件
return loadSpringFactories(classLoader)
.getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
// 先查缓存,避免重复扫描
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
// 扫描类路径下所有Jar包中的META-INF/spring.factories文件
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 读取properties格式的配置文件
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 按key分组,value用逗号拆分多个实现类
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// 存入缓存,整个应用生命周期只扫描一次
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
解析:
- 采用全局缓存机制,类加载器维度缓存扫描结果,应用启动过程中只扫描一次,避免重复 IO。
- 支持多 Jar 包扩展,第三方 Starter 只需要在自己的 Jar 里放
spring.factories文件,就能被自动发现,实现开箱即用。 - 标准的 SPI 思想,面向接口编程,框架定义接口,第三方提供实现,完全解耦。
4. 自动配置全执行流程图

二、Spring Boot 条件注解底层源码解析
条件注解是自动配置的「智能开关」,保证只有符合环境的配置类才会生效,避免无效 Bean 的创建。
1. 条件注解顶层设计
所有条件注解都基于 Spring 原生的 @Conditional 元注解 + Condition 匹配接口,Spring Boot 在此基础上做了大量业务封装。
// Spring原生顶层条件接口
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
Spring Boot 提供了抽象基类 SpringBootCondition,统一了日志输出、匹配记录、异常处理等通用逻辑,所有内置条件注解都继承自它。
2. @ConditionalOnClass 源码解析
作用:类路径下存在指定的类时,配置类才生效,是自动配置中最常用的第一阶段过滤条件。
// 注解定义
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
Class<?>[] value() default {};
String[] name() default {};
}
核心匹配逻辑:
// OnClassCondition核心匹配方法
@Override
protected ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 读取注解中指定的类名
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses == null) {
return ConditionOutcome.match();
}
List<String> missing = new ArrayList<>();
for (String candidate : onClasses) {
// 核心:通过ClassUtils.isPresent判断类是否能被加载
if (!ClassUtils.isPresent(candidate, context.getClassLoader())) {
missing.add(candidate);
}
}
if (!missing.isEmpty()) {
return ConditionOutcome.noMatch("required class not found: " + missing);
}
return ConditionOutcome.match();
}
设计说明:
- 注解同时支持
Class类型和String类型参数,生产环境优先用字符串形式。如果直接写 Class 类型,类不存在时编译就会报错,用字符串可以安全地通过反射判断,这是典型的编译期规避方案。 - 属于第一阶段过滤条件,在自动配置类被注册为 Bean 定义之前就执行,不满足的类直接不会进入容器,性能开销极低。
3. @ConditionalOnMissingBean 源码解析
作用:容器中不存在指定类型的 Bean 时,才创建当前 Bean,是「用户配置优先,自动配置兜底」的核心实现。
// OnBeanCondition核心匹配逻辑
private boolean hasBeanDefinition(ConfigurableListableBeanFactory beanFactory, String beanType) {
// 遍历容器中所有Bean定义,判断是否存在对应类型的Bean
String[] beanNames = beanFactory.getBeanNamesForType(BeanUtils.resolveClassName(beanType, null));
return beanNames.length > 0;
}
源码踩坑与执行时机:
- 执行阶段:属于第二阶段条件,在所有 Bean 定义都注册完成后、Bean 实例化之前执行。
- 顺序依赖:判断结果强依赖配置类的加载顺序。如果自动配置类比用户自定义的 Bean 先加载,判断时用户 Bean 还没注册,会导致自动配置错误生效。
- 解决方案:Spring Boot 通过
@AutoConfigureAfter强制指定加载顺序,比如数据源自动配置必须在连接池自动配置之后执行;所有自动配置类都默认排在用户自定义 Bean 之后。
4. @ConditionalOnProperty 源码解析
作用:根据配置文件中的属性值决定是否生效,常用于功能开关。
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
String[] value() default {}; // 配置项key
String havingValue() default ""; // 匹配的值
boolean matchIfMissing() default false; // 配置不存在时是否默认匹配
}
核心逻辑:从 Environment 中读取指定 key 的配置值,与 havingValue 对比,一致则匹配;如果配置不存在,按 matchIfMissing 的值决定是否通过。
典型应用:spring.aop.auto=true 控制 AOP 自动配置,matchIfMissing=true 表示默认开启。
5. 两阶段条件过滤总结
| 阶段 | 代表注解 | 执行时机 | 作用 |
|---|---|---|---|
| 第一阶段 | @ConditionalOnClass、@ConditionalOnWebApplication | 自动配置类导入前 | 粗筛,不满足的类直接不进入容器,性能开销小 |
| 第二阶段 | @ConditionalOnMissingBean、@ConditionalOnBean | Bean 实例化前 | 细筛,基于容器中已有的 Bean 做判断,保证用户配置优先 |
三、MyBatis 自动配置源码实战解析
MyBatis 的自动配置是学习自动设计思想的最佳实战,完整覆盖了「SPI 注册 + 条件注解 + 属性绑定 + Bean 生成」的全流程。
1. 自动配置 SPI 入口
MyBatis 官方 Starter 在 mybatis-spring-boot-autoconfigure 包的 META-INF/spring.factories 中注册了自动配置类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
Spring Boot 启动时通过 SPI 扫描到这个配置类,纳入自动配置管理。
2. MybatisAutoConfiguration 全类源码解析
@Configuration
// 条件1:类路径下必须存在SqlSessionFactory、SqlSessionFactoryBean,也就是引入了mybatis核心包
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
// 条件2:容器中必须有且只有一个DataSource Bean,也就是数据源配置完成
@ConditionalOnSingleCandidate(DataSource.class)
// 绑定配置文件中mybatis开头的属性
@EnableConfigurationProperties(MybatisProperties.class)
// 加载顺序:必须在数据源自动配置完成之后再执行
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
private final MybatisProperties properties;
private final Interceptor[] interceptors;
// 构造注入配置属性和插件
public MybatisAutoConfiguration(MybatisProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
}
// 创建SqlSessionFactory,Spring整合MyBatis的核心对象
@Bean
@ConditionalOnMissingBean // 容器没有SqlSessionFactory时才创建,用户自定义优先
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
// 读取配置文件中的mapper-locations
Resource[] mapperLocations = this.properties.resolveMapperLocations();
if (mapperLocations != null && mapperLocations.length > 0) {
factory.setMapperLocations(mapperLocations);
}
// 读取type-aliases-package配置
if (StringUtils.hasText(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
// 注册分页等插件
if (this.interceptors != null && this.interceptors.length > 0) {
factory.setPlugins(this.interceptors);
}
return factory.getObject();
}
// 创建SqlSessionTemplate,线程安全的SqlSession实现
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
}
return new SqlSessionTemplate(sqlSessionFactory);
}
}
解析:
- 类上的 4 个注解共同构成生效条件:引入了 MyBatis 依赖 + 数据源已就绪 + 绑定配置 + 顺序控制。
@EnableConfigurationProperties将application.yml中的配置注入到MybatisProperties对象中,实现配置与代码的分离。- 两个
@Bean方法都加了@ConditionalOnMissingBean,用户如果自己定义了 SqlSessionFactory,自动配置的就会失效,完全遵循用户优先的原则。
3. Mapper 扫描自动配置原理
@MapperScan 注解负责扫描 Mapper 接口并注册为 Bean,底层通过 MapperScannerConfigurer 实现:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
String[] value() default {}; // 扫描包路径
}
MapperScannerRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,在 Bean 定义注册阶段,扫描指定包下的接口,为每个 Mapper 接口生成动态代理 Bean,注册到容器中。
4. 属性绑定 MybatisProperties
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {
public static final String MYBATIS_PREFIX = "mybatis";
private String configLocation;
private String[] mapperLocations;
private String typeAliasesPackage;
private ExecutorType executorType;
// getter/setter省略
}
对应 application.yml 中的配置:
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true
@ConfigurationProperties 的底层原理是通过 ConfigurationPropertiesBindingPostProcessor 这个 Bean 后置处理器,从 Environment 中读取对应前缀的配置,通过反射 setter 方法注入到 JavaBean 中。
四、Spring Boot AOP 自动配置源码解析
Spring Boot 对 AOP 做了开箱即用的封装,无需手动添加 @EnableAspectJAutoProxy 注解,引入依赖即可生效。
1. AopAutoConfiguration 自动配置类
@Configuration
// 条件1:类路径存在AspectJ相关类,也就是引入了spring-boot-starter-aop
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class })
// 条件2:配置spring.aop.auto=true,默认缺省时也匹配(默认开启)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
// 分支1:CGLIB代理模式,spring.aop.proxy-target-class=true时生效(默认)
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
static class CglibAutoProxyConfiguration {
@Bean
public static BeanFactoryPostProcessor aopConfigurer() {
return beanFactory -> {
// 注册自动代理创建器,开启CGLIB代理
AopConfigUtils.registerAutoProxyCreatorIfNecessary(beanFactory);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(beanFactory);
};
}
}
// 分支2:JDK动态代理模式,配置为false时生效
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
static class JdkDynamicAutoProxyConfiguration {
@Bean
public static BeanFactoryPostProcessor aopConfigurer() {
return beanFactory -> {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(beanFactory);
AopConfigUtils.forceAutoProxyCreatorToUseInterfaceProxying(beanFactory);
};
}
}
}
解析:
- 采用嵌套配置类 + 条件注解的方式,实现了两种代理模式的无缝切换,默认使用 CGLIB 代理。
matchIfMissing = true是默认生效的关键,用户不配置时就走默认值,体现约定大于配置的思想。- 最终通过
AopConfigUtils向容器中注册AnnotationAwareAspectJAutoProxyCreator,这是 Spring AOP 的核心后置处理器,负责扫描切面、生成动态代理对象。
2. 自动开启原理
原生 Spring 中需要手动加 @EnableAspectJAutoProxy 才能开启 AOP,Spring Boot 自动配置做了两件事替代了这个注解:
- 自动注册了
AnnotationAwareAspectJAutoProxyCreator这个 Bean 后置处理器,和注解的作用完全一致。 - 通过配置项控制代理目标类模式,替代了注解的
proxyTargetClass属性。
五、Spring Boot Jar 包启动底层源码解析
Spring Boot 可以通过 java -jar xxx.jar 直接启动,本质是自定义了类加载器,解决了 JDK 原生不支持嵌套 Jar 加载的问题。
1. 可执行 Fat Jar 结构
Spring Boot 打包生成的 Jar 包叫可执行 Jar(Fat Jar),内部结构和普通 Jar 完全不同:

MANIFEST.MF 核心配置:
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.example.DemoApplication
Main-Class:JVM 执行java -jar时的入口类,是 Spring Boot 自定义的JarLauncher。Start-Class:我们业务代码的主启动类,由 JarLauncher 负责加载启动。
2. JarLauncher 启动流程源码
JarLauncher 是启动入口,核心职责是创建自定义类加载器,加载嵌套 Jar 中的类,然后调用业务主类的 main 方法。
// org.springframework.boot.loader.JarLauncher
public class JarLauncher extends ExecutableArchiveLauncher {
public static void main(String[] args) throws Exception {
// 启动入口:创建JarLauncher实例并执行launch方法
new JarLauncher().launch(args);
}
protected void launch(String[] args) throws Exception {
// 1. 注册自定义URL协议处理器,支持嵌套Jar的URL解析
JarFile.registerUrlProtocolHandler();
// 2. 创建自定义类加载器LaunchedURLClassLoader,加载BOOT-INF下的所有类和Jar
ClassLoader classLoader = createClassLoader(getClassPathArchives());
// 3. 获取Start-Class,调用其main方法,启动Spring Boot应用
launch(args, getMainClass(), classLoader);
}
}
3. 嵌套 Jar 加载原理
JDK 原生的 URLClassLoader 只能加载 Jar 包中的 class 文件,不能加载 Jar 包里面嵌套的 Jar 文件。Spring Boot 的解决方案:
- 自定义 JarFile 实现:重写了 Jar 的解析逻辑,支持读取 Jar 内部的 Jar 条目,把嵌套 Jar 也当成可读取的资源。
- 自定义 URLStreamHandler:注册了自定义的
jar:协议处理器,能够解析jar:file:/xxx.jar!/BOOT-INF/lib/yyy.jar格式的嵌套 Jar URL。 - 自定义类加载器 LaunchedURLClassLoader:继承自 URLClassLoader,把所有嵌套 Jar 的 URL 都加入类路径,重写类加载逻辑,优先加载应用内的类。
4. 类加载机制的双亲委派适配
LaunchedURLClassLoader 遵循双亲委派模型,但做了针对性优化:
- 优先从当前类加载器(应用类加载器)加载业务类和依赖包,避免和系统类加载器的依赖冲突。
BOOT-INF/classes优先级高于BOOT-INF/lib,保证业务类优先于依赖类加载。- 核心系统类(如
java.lang.*)依然委派给启动类加载器,保证 JDK 核心类的安全。
六、面试速记总结
- 自动配置入口:
@EnableAutoConfiguration通过@Import导入AutoConfigurationImportSelector,借助SpringFactoriesLoaderSPI 扫描所有spring.factories中的自动配置类。 - 条件两阶段过滤:第一阶段
@ConditionalOnClass类存在性粗筛,第二阶段@ConditionalOnMissingBeanBean 存在性细筛,用户配置优先,自动配置兜底。 - MyBatis 自动配置:SPI 注册配置类 → 数据源就绪后生效 → 读取配置属性 → 创建 SqlSessionFactory、SqlSessionTemplate → Mapper 扫描生成代理 Bean。
- AOP 自动配置:引入依赖默认生效,嵌套配置类实现 CGLIB/JDK 代理切换,自动注册 AOP 核心后置处理器,替代手动加注解。
- Jar 启动原理:
JarLauncher作为 JVM 入口,自定义类加载器解决嵌套 Jar 加载问题,最终调用业务主类的 main 方法启动 Spring 容器。
最后学习源码的终极目的不是记住代码,而是吸收 Spring 团队的架构设计思想,这些方法论可以直接复用到自己的系统设计中。掌握原理,把通用能力下沉成开箱即用的依赖,培养底层问题排查、架构设计与技术选型的能力。
2355

被折叠的 条评论
为什么被折叠?



