8gu-SpringBoot

Spring Boot自动装配

Spring Boot Starter 和自动装配的底层原理

Spring Boot 的自动装配 (Auto-configuration) 是其“约定优于配置”理念的核心。它能根据你项目中引入的依赖(Starter),自动为你配置好相应的 Bean。

其底层原理可以概括为以下三大核心机制:

1. @SpringBootApplication 注解:启动的入口

我们通常在主类上使用的 @SpringBootApplication 其实是一个复合注解,它包含了三个关键注解:

  • @Configuration: 声明这个类是一个配置类。
  • @ComponentScan: 扫描主类所在包及其子包下的组件(如 @Component, @Service 等)。
  • @EnableAutoConfiguration: 这是自动装配的“总开关”,是所有魔法的起点。

2. @EnableAutoConfigurationspring.factories 文件:加载自动配置类

@EnableAutoConfiguration 注解本身并不复杂,它通过 @Import(AutoConfigurationImportSelector.class) 导入了一个选择器。这个 AutoConfigurationImportSelector 的核心作用就是去加载所有依赖 JAR 包META-INF/spring.factories 文件里定义的自动配置类。

工作流程如下:

  1. Spring Boot 启动时,@EnableAutoConfiguration 生效。
  2. AutoConfigurationImportSelector 会被调用,它会去扫描 classpath 下所有 starter 和其他依赖包中的 META-INF/spring.factories 文件。
  3. 它会寻找 org.springframework.boot.autoconfigure.EnableAutoConfiguration 这个键。
  4. 这个键对应的值是一个用逗号分隔的全限定类名列表,这些类就是所有潜在的自动配置类(例如 DataSourceAutoConfiguration, RedisAutoConfiguration 等)。
  5. 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 的自动配置就不会再生效了。这给了开发者极高的灵活性。
  • @ConditionalOnProperty: 当配置文件(application.propertiesyml)中存在指定的属性并且值匹配时,条件满足。
    • 例子spring.jpa.show-sql=true 可以控制是否打印 SQL。
  • @ConditionalOnWebApplication: 判断当前应用是否是一个 Web 应用。

整体流程总结:

  1. 引入 Starter:你在 pom.xml 中加入 spring-boot-starter-web
  2. 依赖传递:Maven 会自动下载 spring-boot-starter-web 所需的所有依赖,如 spring-webmvc, tomcat-embed-core, spring-boot-autoconfigure 等。
  3. 启动应用:运行带有 @SpringBootApplication 的主类。
  4. 开启自动配置@EnableAutoConfiguration 开始工作。
  5. 扫描配置类AutoConfigurationImportSelector 扫描所有 JAR 包中的 META-INF/spring.factories 文件,找到并加载如 ServletWebServerFactoryAutoConfigurationDispatcherServletAutoConfiguration 等大量的自动配置类。
  6. 条件判断
    • Spring Boot 检查 ServletWebServerFactoryAutoConfiguration,发现 @ConditionalOnClass({ ServletRequest.class }) 条件满足(因为引入了 spring-webmvc)。
    • 它又发现 @ConditionalOnClass(Tomcat.class) 条件也满足(因为引入了 tomcat-embed-core)。
    • 于是,它就会自动为你配置一个嵌入式的 Tomcat 服务器 Bean。
    • 同理,其他的 Web 相关配置(如 DispatcherServlet)也会因为满足各自的 @Conditional 条件而被自动配置好。

通过这套精巧的机制,Spring Boot 实现了“开箱即用”的效果,极大地简化了 Spring 应用的开发和配置过程。
image

自动装配的流程图

好的,我们来详细梳理 Spring Bean 的整个生命周期,并 pinpoint 动态代理对象的生成时机。

Spring Bean 完整生命周期流程图

这是一个详细的流程图,涵盖了从实例化到销毁的每一个关键步骤。

image
bean生命周期

Bean 生命周期

Bean 生命周期详细说明

整个生命周期可以概括为四个主要阶段:实例化 -> 属性填充 -> 初始化 -> 销毁

  1. ** 实例化 (Instantiation)**

    • Spring 容器根据 Bean 的定义(XML、注解等),通过反射调用其构造函数来创建一个 Bean 的实例。
    • 此时的 Bean 只是一个“裸”对象,其属性都是默认值(如 null、0等)。
  2. ** 填充属性 (Populate Properties)**

    • Spring 容器根据配置(如 @Autowired@Value 等)为 Bean 的属性赋值,也就是进行依赖注入(DI)。
    • 关键点:如果此时发生循环依赖,Spring 会利用其三级缓存机制来提前暴露“半成品”的 Bean,从而解决依赖闭环。
  3. ** 调用 Aware 接口方法**

    • Spring 会检查该 Bean 是否实现了 Aware 系列接口。如果实现了,就会调用相应的方法,将容器自身的一些资源注入给 Bean。常见的有:
      • BeanNameAware:注入 Bean 的 ID/name。
      • BeanFactoryAware:注入 Bean 工厂。
      • ApplicationContextAware:注入应用上下文。
  4. ** BeanPostProcessor 前置处理**

    • 在 Bean 执行任何自定义的初始化逻辑之前,Spring 会调用所有注册的 BeanPostProcessorpostProcessBeforeInitialization 方法。
    • 这提供了一个在 Bean 初始化前对其进行修改或处理的机会。
  5. ** 初始化 (Initialization)**

    • 这是执行 Bean 自定义初始化逻辑的阶段。Spring 会按照以下顺序调用:
      1. @PostConstruct 注解的方法。
      2. 如果 Bean 实现了 InitializingBean 接口,则调用其 afterPropertiesSet 方法。
      3. XML 或 @Bean 中定义的 init-method
  6. ** BeanPostProcessor 后置处理**

    • 当 Bean 的自定义初始化逻辑执行完毕后,Spring 会调用所有注册的 BeanPostProcessorpostProcessAfterInitialization 方法。
    • 这是整个生命周期中至关重要的一步,也是 AOP 发生的地方。
  7. ** Bean 创建完成**

    • 经过以上所有步骤,Bean 已经是一个完全可用、功能完备的对象了。
    • 它被放入 Spring 的单例池(一级缓存 singletonObjects)中,等待被其他对象注入或通过 getBean() 调用。
  8. ** 销毁 (Destruction)**

    • 当 Spring 容器关闭时,会触发销毁流程。
    • Spring 会按照以下顺序调用:
      1. @PreDestroy 注解的方法。
      2. 如果 Bean 实现了 DisposableBean 接口,则调用其 destroy 方法。
      3. XML 或 @Bean 中定义的 destroy-method

动态代理对象在哪个时期生成?

答案非常明确:在生命周期的第 步,即 BeanPostProcessorpostProcessAfterInitialization 方法中生成。

为什么是这个时期?

  1. 保证原始对象的完整性:选择在 postProcessAfterInitialization 这个节点,意味着原始的 Bean 对象已经完成了实例化、属性填充和它自己的所有初始化逻辑 (@PostConstruct, init-method 等)。它已经是一个完全可用、状态正确的“目标对象”(Target)。只有在这种状态下,为它创建代理才有意义,因为代理最终需要将方法调用委托给这个功能完整的原始对象。

  2. AOP 的实现机制:Spring AOP 本身就是通过 BeanPostProcessor 来实现的。具体的实现类是 AnnotationAwareAspectJAutoProxyCreator。它的工作流程如下:

    • 它作为一个 BeanPostProcessor 在 Spring 容器中注册。
    • 在每个 Bean 走完生命周期的第 步(初始化)之后,都会进入第 步,此时 AnnotationAwareAspectJAutoProxyCreatorpostProcessAfterInitialization 方法会被调用。
    • 在该方法内部,它会检查当前这个 Bean 是否有任何方法与已定义的切面(Aspect)的切点(Pointcut)相匹配。
    • 如果匹配,就意味着这个 Bean 需要被增强。此时,它会使用 JDK 动态代理(如果 Bean 实现了接口)或 CGLIB(如果 Bean 没有实现接口)来为这个原始 Bean 创建一个代理对象。
    • 最后,这个方法返回的是创建好的代理对象,而不是原始对象。
    • Spring 容器接下来会将这个返回的代理对象放入单例池中,以后任何地方注入或获取该 Bean,得到的都是这个代理对象。

总结: Spring 的设计非常精妙,它确保了在对一个对象进行功能增强(如添加事务、日志等 AOP 功能)之前,这个对象本身必须是完全初始化好、功能完备的。postProcessAfterInitialization 正是保证这一点的完美时机。

posted @ 2025-09-04 10:11  tokirin994  阅读(4)  评论(0)    收藏  举报