8gu-SpringBoot
Spring Boot自动装配
Spring Boot Starter 和自动装配的底层原理
Spring Boot 的自动装配 (Auto-configuration) 是其“约定优于配置”理念的核心。它能根据你项目中引入的依赖(Starter),自动为你配置好相应的 Bean。
其底层原理可以概括为以下三大核心机制:
1. @SpringBootApplication
注解:启动的入口
我们通常在主类上使用的 @SpringBootApplication
其实是一个复合注解,它包含了三个关键注解:
@Configuration
: 声明这个类是一个配置类。@ComponentScan
: 扫描主类所在包及其子包下的组件(如@Component
,@Service
等)。@EnableAutoConfiguration
: 这是自动装配的“总开关”,是所有魔法的起点。
2. @EnableAutoConfiguration
与 spring.factories
文件:加载自动配置类
@EnableAutoConfiguration
注解本身并不复杂,它通过 @Import(AutoConfigurationImportSelector.class)
导入了一个选择器。这个 AutoConfigurationImportSelector
的核心作用就是去加载所有依赖 JAR 包中 META-INF/spring.factories
文件里定义的自动配置类。
工作流程如下:
- Spring Boot 启动时,
@EnableAutoConfiguration
生效。 AutoConfigurationImportSelector
会被调用,它会去扫描 classpath 下所有starter
和其他依赖包中的META-INF/spring.factories
文件。- 它会寻找
org.springframework.boot.autoconfigure.EnableAutoConfiguration
这个键。 - 这个键对应的值是一个用逗号分隔的全限定类名列表,这些类就是所有潜在的自动配置类(例如
DataSourceAutoConfiguration
,RedisAutoConfiguration
等)。 - Spring Boot 会将这个列表中的所有配置类加载到 Spring 的 IOC 容器中。
例如,spring-boot-autoconfigure.jar
自己的 spring.factories
文件中就定义了大量的自动配置类:
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
...
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
...
3. 条件注解 (@ConditionalOn...
):按需装配,智能决策
虽然 Spring Boot 加载了所有潜在的自动配置类,但并不意味着它们都会生效。每个自动配置类都被大量的条件注解所修饰,只有当满足特定条件时,这个配置类以及它内部定义的 Bean 才会真正被创建和注入。
这正是 Spring Boot 如此智能的关键所在。
常见的条件注解:
@ConditionalOnClass
: 当 classpath 中存在指定的类时,条件满足。- 例子:
RedisAutoConfiguration
上有@ConditionalOnClass(RedisOperations.class)
。这意味着只有当你引入了spring-boot-starter-data-redis
(它包含了spring-data-redis
依赖,里面有RedisOperations
类) 时,Redis 的自动配置才会生效。
- 例子:
@ConditionalOnMissingBean
: 当容器中不存在指定类型的 Bean 时,条件满足。- 例子:Spring Boot 会尝试配置一个
DataSource
Bean,但如果你自己手动配置了一个DataSource
,那么 Spring Boot 的自动配置就不会再生效了。这给了开发者极高的灵活性。
- 例子:Spring Boot 会尝试配置一个
@ConditionalOnProperty
: 当配置文件(application.properties
或yml
)中存在指定的属性并且值匹配时,条件满足。- 例子:
spring.jpa.show-sql=true
可以控制是否打印 SQL。
- 例子:
@ConditionalOnWebApplication
: 判断当前应用是否是一个 Web 应用。
整体流程总结:
- 引入 Starter:你在
pom.xml
中加入spring-boot-starter-web
。 - 依赖传递:Maven 会自动下载
spring-boot-starter-web
所需的所有依赖,如spring-webmvc
,tomcat-embed-core
,spring-boot-autoconfigure
等。 - 启动应用:运行带有
@SpringBootApplication
的主类。 - 开启自动配置:
@EnableAutoConfiguration
开始工作。 - 扫描配置类:
AutoConfigurationImportSelector
扫描所有 JAR 包中的META-INF/spring.factories
文件,找到并加载如ServletWebServerFactoryAutoConfiguration
、DispatcherServletAutoConfiguration
等大量的自动配置类。 - 条件判断:
- Spring Boot 检查
ServletWebServerFactoryAutoConfiguration
,发现@ConditionalOnClass({ ServletRequest.class })
条件满足(因为引入了spring-webmvc
)。 - 它又发现
@ConditionalOnClass(Tomcat.class)
条件也满足(因为引入了tomcat-embed-core
)。 - 于是,它就会自动为你配置一个嵌入式的 Tomcat 服务器 Bean。
- 同理,其他的 Web 相关配置(如
DispatcherServlet
)也会因为满足各自的@Conditional
条件而被自动配置好。
- Spring Boot 检查
通过这套精巧的机制,Spring Boot 实现了“开箱即用”的效果,极大地简化了 Spring 应用的开发和配置过程。
好的,我们来详细梳理 Spring Bean 的整个生命周期,并 pinpoint 动态代理对象的生成时机。
Spring Bean 完整生命周期流程图
这是一个详细的流程图,涵盖了从实例化到销毁的每一个关键步骤。
Bean 生命周期
Bean 生命周期详细说明
整个生命周期可以概括为四个主要阶段:实例化 -> 属性填充 -> 初始化 -> 销毁。
-
** 实例化 (Instantiation)**
- Spring 容器根据 Bean 的定义(XML、注解等),通过反射调用其构造函数来创建一个 Bean 的实例。
- 此时的 Bean 只是一个“裸”对象,其属性都是默认值(如
null
、0等)。
-
** 填充属性 (Populate Properties)**
- Spring 容器根据配置(如
@Autowired
、@Value
等)为 Bean 的属性赋值,也就是进行依赖注入(DI)。 - 关键点:如果此时发生循环依赖,Spring 会利用其三级缓存机制来提前暴露“半成品”的 Bean,从而解决依赖闭环。
- Spring 容器根据配置(如
-
** 调用 Aware 接口方法**
- Spring 会检查该 Bean 是否实现了
Aware
系列接口。如果实现了,就会调用相应的方法,将容器自身的一些资源注入给 Bean。常见的有:BeanNameAware
:注入 Bean 的 ID/name。BeanFactoryAware
:注入 Bean 工厂。ApplicationContextAware
:注入应用上下文。
- Spring 会检查该 Bean 是否实现了
-
**
BeanPostProcessor
前置处理**- 在 Bean 执行任何自定义的初始化逻辑之前,Spring 会调用所有注册的
BeanPostProcessor
的postProcessBeforeInitialization
方法。 - 这提供了一个在 Bean 初始化前对其进行修改或处理的机会。
- 在 Bean 执行任何自定义的初始化逻辑之前,Spring 会调用所有注册的
-
** 初始化 (Initialization)**
- 这是执行 Bean 自定义初始化逻辑的阶段。Spring 会按照以下顺序调用:
- 被
@PostConstruct
注解的方法。 - 如果 Bean 实现了
InitializingBean
接口,则调用其afterPropertiesSet
方法。 - XML 或
@Bean
中定义的init-method
。
- 被
- 这是执行 Bean 自定义初始化逻辑的阶段。Spring 会按照以下顺序调用:
-
**
BeanPostProcessor
后置处理**- 当 Bean 的自定义初始化逻辑执行完毕后,Spring 会调用所有注册的
BeanPostProcessor
的postProcessAfterInitialization
方法。 - 这是整个生命周期中至关重要的一步,也是 AOP 发生的地方。
- 当 Bean 的自定义初始化逻辑执行完毕后,Spring 会调用所有注册的
-
** Bean 创建完成**
- 经过以上所有步骤,Bean 已经是一个完全可用、功能完备的对象了。
- 它被放入 Spring 的单例池(一级缓存
singletonObjects
)中,等待被其他对象注入或通过getBean()
调用。
-
** 销毁 (Destruction)**
- 当 Spring 容器关闭时,会触发销毁流程。
- Spring 会按照以下顺序调用:
- 被
@PreDestroy
注解的方法。 - 如果 Bean 实现了
DisposableBean
接口,则调用其destroy
方法。 - XML 或
@Bean
中定义的destroy-method
。
- 被
动态代理对象在哪个时期生成?
答案非常明确:在生命周期的第 步,即 BeanPostProcessor
的 postProcessAfterInitialization
方法中生成。
为什么是这个时期?
-
保证原始对象的完整性:选择在
postProcessAfterInitialization
这个节点,意味着原始的 Bean 对象已经完成了实例化、属性填充和它自己的所有初始化逻辑 (@PostConstruct
,init-method
等)。它已经是一个完全可用、状态正确的“目标对象”(Target)。只有在这种状态下,为它创建代理才有意义,因为代理最终需要将方法调用委托给这个功能完整的原始对象。 -
AOP 的实现机制:Spring AOP 本身就是通过
BeanPostProcessor
来实现的。具体的实现类是AnnotationAwareAspectJAutoProxyCreator
。它的工作流程如下:- 它作为一个
BeanPostProcessor
在 Spring 容器中注册。 - 在每个 Bean 走完生命周期的第 步(初始化)之后,都会进入第 步,此时
AnnotationAwareAspectJAutoProxyCreator
的postProcessAfterInitialization
方法会被调用。 - 在该方法内部,它会检查当前这个 Bean 是否有任何方法与已定义的切面(Aspect)的切点(Pointcut)相匹配。
- 如果匹配,就意味着这个 Bean 需要被增强。此时,它会使用 JDK 动态代理(如果 Bean 实现了接口)或 CGLIB(如果 Bean 没有实现接口)来为这个原始 Bean 创建一个代理对象。
- 最后,这个方法返回的是创建好的代理对象,而不是原始对象。
- Spring 容器接下来会将这个返回的代理对象放入单例池中,以后任何地方注入或获取该 Bean,得到的都是这个代理对象。
- 它作为一个
总结: Spring 的设计非常精妙,它确保了在对一个对象进行功能增强(如添加事务、日志等 AOP 功能)之前,这个对象本身必须是完全初始化好、功能完备的。postProcessAfterInitialization
正是保证这一点的完美时机。