Spring 高级用法和 bean初始化过程

Spring的主要特性和用法

InitializingBean

Spring 容器完成 Bean 的属性注入后,执行该 Bean 的自定义初始化逻辑。
使用场景:

  1. 框架 / 组件开发中,需要强制子类实现初始化逻辑
  2. 需要利用 Spring 内部接口的联动机制
    例如:某个 Bean 同时实现了 ApplicationContextAware(获取容器上下文)和 InitializingBean,其初始化逻辑依赖容器上下文,而 afterPropertiesSet 的执行时机在 setApplicationContext 之后(Spring 保证 Aware 接口方法先于 afterPropertiesSet 执行)。

如果是业务代码开发,使用init-method即可

DisposableBean

在 Bean 被容器销毁时执行特定的资源清理或收尾操作。

当应用收到关闭信号(如 kill -15 或 Ctrl+C)并进入优雅停机流程时:
内嵌 Web 容器(如 Tomcat、Netty)首先停止接收新请求
容器会等待预设的超时时间(默认 30 秒,可通过 spring.lifecycle.timeout-per-shutdown-phase 配置),让已进入应用的请求继续处理。
若超时前所有请求处理完成,容器正常关闭。
若超时后仍有未完成的请求,容器会强制中断这些请求(避免无限等待)。

SEATA客户端的关闭做的事情例子
与 TC(事务协调器)的通信:TM/RM 会与 TC 维持网络连接(如 Netty 长连接),并上报事务状态(如分支事务注册、全局事务提交 / 回滚)。如果不主动销毁:JVM 突然退出时,TC 可能无法收到「事务中断」的通知,导致 TC 中残留「悬挂的全局事务」(认为事务仍在进行中),进而占用 TC 的资源(如锁、事务日志),甚至影响其他分布式事务的正常执行。
与数据库的交互:RM 会管理本地数据库的分支事务,并持有数据库锁(如行锁)。如果不主动释放:JVM 退出时,若本地事务未完成,数据库可能因无法收到「回滚」指令而长期持有锁,导致其他事务被阻塞(例如,某行数据被锁,其他请求无法更新)。

继承 ApplicationContextAware

在自定义框架或者组件使用applicationContext, 比如获取有自定义注解的类
Seata 2.3 源码


    private void findBusinessBeanNamesNeededEnhancement() {

 {
        if (applicationContext instanceof ConfigurableApplicationContext) {
            ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;
            ConfigurableListableBeanFactory configurableListableBeanFactory = configurableApplicationContext.getBeanFactory();

            String[] beanNames = applicationContext.getBeanDefinitionNames();
            for (String contextBeanName : beanNames) {
                BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(contextBeanName);
                if (StringUtils.isBlank(beanDefinition.getBeanClassName())) {
                    continue;
                }
                if (IGNORE_ENHANCE_CHECK_SET.contains(beanDefinition.getBeanClassName())) {
                    continue;
                }
                try {
                    // get the class by bean definition class name
                    Class<?> beanClass = Class.forName(beanDefinition.getBeanClassName());
                    // check if it needs enhancement by the class
                    IfNeedEnhanceBean ifNeedEnhanceBean = DefaultInterfaceParser.get().parseIfNeedEnhancement(beanClass);
                    if (!ifNeedEnhanceBean.isIfNeed()) {
                        continue;
                    }
                    if (ifNeedEnhanceBean.getNeedEnhanceEnum().equals(NeedEnhanceEnum.SERVICE_BEAN)) {
                        // the native bean which dubbo, sofa bean service bean referenced
                        PropertyValue propertyValue = beanDefinition.getPropertyValues().getPropertyValue("ref");
                        if (propertyValue == null) {
                            // the native bean which HSF service bean referenced
                            propertyValue = beanDefinition.getPropertyValues().getPropertyValue("target");
                        }
                        if (propertyValue != null) {
                            RuntimeBeanReference r = (RuntimeBeanReference) propertyValue.getValue();
                            if (r != null && StringUtils.isNotBlank(r.getBeanName())) {
                                NEED_ENHANCE_BEAN_NAME_SET.add(r.getBeanName());
                                continue;
                            }
                        }
                        // the native bean which local tcc service bean referenced
                        NEED_ENHANCE_BEAN_NAME_SET.add(contextBeanName);
                    } else if (ifNeedEnhanceBean.getNeedEnhanceEnum().equals(NeedEnhanceEnum.GLOBAL_TRANSACTIONAL_BEAN)) {
                        // global transactional bean
                        NEED_ENHANCE_BEAN_NAME_SET.add(contextBeanName);
                    }
                } catch (ClassNotFoundException e) {
                    LOGGER.warn("check if need enhance bean error, it can be ignore", e);
                }
            }
            LOGGER.info("The needed enhancement business beans are : {}", NEED_ENHANCE_BEAN_NAME_SET);
        }
    }

xxx

Spring 以下的功能都是在bean初始化的哪个阶段执行的?

AOP
变量替换,
@Autoware
@EnableAutoConfiguration 注解,其中真正实现自动配置功能的核心实现者 AutoConfigurationImportSelector
@ComponentScan
@Configuration

bean初始化过程

Spring Bean 的初始化流程是一个从配置解析到最终生成可用 Bean 实例的完整生命周期,结合你提到的各项特性,整体流程可分为以下核心步骤:

一、初始化前的准备阶段

1. 容器启动与配置解析

  • 触发点ApplicationContext 初始化时调用 refresh() 方法,启动整个流程。
  • 核心操作
    • @ComponentScan 生效:通过 ComponentScanAnnotationParser 解析 @ComponentScan 注解,扫描指定包路径下的类(如标注 @Component@Service@Controller 的类),将其转化为 BeanDefinition 注册到容器。
    • @Configuration 处理:标注 @Configuration 的类会被识别为配置类,其内部通过 @Bean 定义的方法会被解析为 BeanDefinition(特殊处理:配置类会被 CGLIB 代理,确保 @Bean 方法的单例特性)。
    • AutoConfigurationImportSelector 作用:在 Spring Boot 中,该类通过 selectImports() 方法自动导入 META-INF/spring.factories 中配置的自动配置类,这些类会被解析为 BeanDefinition 并注册(属于自动配置的核心机制)。

二、BeanFactory 预处理阶段

2. 执行 BeanFactoryPostProcessor

  • 触发点refresh() 方法中的 invokeBeanFactoryPostProcessors(beanFactory)
  • 核心操作
    • 变量替换PropertySourcesPlaceholderConfigurer(内置 BeanFactoryPostProcessor)会在此阶段解析配置中的占位符(如 ${server.port}),从环境变量、配置文件中获取实际值并替换,确保后续注入的是真实值。
    • 其他自定义 BeanFactoryPostProcessor 可修改 BeanDefinition 信息(如动态调整属性值、作用域等)。

三、Bean 实例化与依赖注入阶段

3. 实例化 Bean(创建原始对象)

  • 触发点getBean()doGetBean()createBean()doCreateBean()
  • 核心操作
    • 通过反射调用 Bean 的构造方法创建实例(支持有参构造,需解析构造参数依赖)。
    • 对于单例 Bean,若存在循环依赖,会提前暴露一个「早期引用」(通过 ObjectFactory),避免循环依赖导致的实例化失败。

4. 属性注入(依赖注入)

  • 触发点doCreateBean() 中的 populateBean() 方法。
  • 核心操作
    • @Autowired 生效AutowiredAnnotationBeanPostProcessorBeanPostProcessor 的实现类)会解析 @Autowired 注解,根据类型或名称从容器中查找匹配的 Bean,注入到当前 Bean 的属性中(支持字段注入、setter 注入、构造器注入)。
    • 注入的属性值可能是经过「变量替换」后的实际值(如配置文件中的参数)。

四、Bean 初始化阶段

5. 执行 Aware 接口方法

  • 触发点initializeBean() 中的 invokeAwareMethods()
  • 核心操作:若 Bean 实现了 BeanNameAwareBeanFactoryAwareApplicationContextAware 等接口,会在此阶段调用对应的方法(如 setApplicationContext()),注入容器相关信息。

6. BeanPostProcessor 前置处理

  • 触发点applyBeanPostProcessorsBeforeInitialization()
  • 核心操作:所有 BeanPostProcessorpostProcessBeforeInitialization() 方法被调用,可对 Bean 进行初始化前的增强(如 @PostConstruct 注解的处理,由 CommonAnnotationBeanPostProcessor 完成,其执行时机早于 afterPropertiesSet())。

7. 执行初始化方法(含 afterPropertiesSet()

  • 触发点invokeInitMethods() 方法。
  • 核心操作
    • afterPropertiesSet() 作用:若 Bean 实现了 InitializingBean 接口,Spring 会调用该方法。其核心作用是在所有属性注入完成后执行初始化逻辑,例如属性合法性校验(如非空检查)、基于注入属性的资源初始化(如数据库连接创建)、缓存预热等(依赖于属性注入的操作必须在此处执行,而非构造函数)。
    • 若配置了自定义 init-method(如 @Bean(initMethod = "init")),会调用该方法(执行顺序在 afterPropertiesSet() 之后)。

8. BeanPostProcessor 后置处理(AOP 代理注入)

  • 触发点applyBeanPostProcessorsAfterInitialization()
  • 核心操作
    • AOP 代理生成AnnotationAwareAspectJAutoProxyCreatorBeanPostProcessor 的实现类)会在此阶段检查 Bean 是否匹配切面的切入点(Pointcut)。若匹配,会通过 JDK 动态代理(目标类实现接口)或 CGLIB 代理(目标类无接口)创建代理对象,并替换原始 Bean 实例。
    • 代理对象会包含切面逻辑(如 @Before@After 通知),后续从容器中获取的 Bean 实际是代理对象。

五、初始化完成阶段

9. 注册销毁逻辑与缓存

  • 单例 Bean 会被存入 singletonObjects 缓存,供后续直接获取。
  • 若 Bean 实现了 DisposableBean 或配置了 destroy-method,会注册销毁回调,在容器关闭时执行。

总结:特性与流程对应关系

特性 介入阶段 核心实现类/机制
@ComponentScan 配置解析阶段 ComponentScanAnnotationParser
@Configuration 配置解析阶段 ConfigurationClassPostProcessor
AutoConfigurationImportSelector 配置解析阶段 自动导入 META-INF/spring.factories 配置类
变量替换 BeanFactory 预处理阶段 PropertySourcesPlaceholderConfigurer
@Autowired 属性注入阶段 AutowiredAnnotationBeanPostProcessor
afterPropertiesSet() 初始化方法执行阶段 InitializingBean 接口实现
AOP 代理 Bean 初始化后处理阶段 AnnotationAwareAspectJAutoProxyCreator

通过以上流程,Spring 从配置解析到最终生成增强后的 Bean 实例,各特性在不同阶段协同工作,确保 Bean 最终处于可用状态。其中 afterPropertiesSet() 作为初始化阶段的关键节点,专门用于处理依赖属性注入完成后的初始化逻辑,是连接属性注入与最终可用状态的重要桥梁。

参考资料

https://www.cnblogs.com/jimoer/p/19089736

posted @ 2025-10-15 21:28  向着朝阳  阅读(12)  评论(0)    收藏  举报