目录

深入分析spring中Bean的初始化过程

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!


Spring Bean 初始化过程深入分析

Spring Bean 的初始化是生命周期中的关键阶段,涉及多个扩展点和执行顺序的精确控制。以下是初始化过程的详细解析:

1. 初始化阶段的触发时机

初始化在 属性注入(依赖注入)完成之后 开始,此时 Bean 的实例已存在,但尚未完全就绪(如代理未生成、自定义逻辑未执行)。


2. 初始化方法执行流程

Spring 按固定顺序调用三类初始化方法:

  1. @PostConstruct 注解方法

    • 触发方式 :由 CommonAnnotationBeanPostProcessor 处理。
    • 特点:JSR-250 标准注解,执行顺序最先。
    java 复制代码
    @Component
    public class MyBean {
        @PostConstruct
        public void init() {
            System.out.println("@PostConstruct 方法执行");
        }
    }
  2. InitializingBean.afterPropertiesSet()

    • 接口定义 :Spring 原生接口,优先级次于 @PostConstruct
    java 复制代码
    @Component
    public class MyBean implements InitializingBean {
        @Override
        public void afterPropertiesSet() {
            System.out.println("InitializingBean 方法执行");
        }
    }
  3. 自定义 init-method

    • 配置方式 :XML 中 <bean init-method="init">@Bean(initMethod = "init")
    • 执行顺序:最后触发。
    java 复制代码
    public class MyBean {
        public void customInit() {
            System.out.println("自定义 init-method 执行");
        }
    }
    
    @Configuration
    public class AppConfig {
        @Bean(initMethod = "customInit")
        public MyBean myBean() {
            return new MyBean();
        }
    }

3. 初始化过程源码级分析

Spring 通过 initializeBean() 方法协调初始化流程:

java 复制代码
// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    // 1. 执行 Aware 接口回调(BeanNameAware, BeanFactoryAware 等)
    invokeAwareMethods(beanName, bean);

    // 2. BeanPostProcessor 前置处理
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
    }

    try {
        // 3. 执行初始化方法
        invokeInitMethods(beanName, wrappedBean, mbd);
    } catch (Throwable ex) {
        throw new BeanCreationException(...);
    }

    // 4. BeanPostProcessor 后置处理(如生成 AOP 代理)
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}

关键方法 invokeInitMethods 的实现

java 复制代码
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    // 执行 InitializingBean.afterPropertiesSet()
    if (bean instanceof InitializingBean) {
        ((InitializingBean) bean).afterPropertiesSet();
    }

    // 执行自定义 init-method
    String initMethodName = mbd.getInitMethodName();
    if (StringUtils.hasLength(initMethodName) && 
        !(bean instanceof InitializingBean && "afterPropertiesSet".equals(initMethodName))) {
        Method initMethod = ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName);
        ReflectionUtils.makeAccessible(initMethod);
        initMethod.invoke(bean);
    }
}

4. 初始化扩展点详解

4.1 BeanPostProcessor 的作用
  • postProcessBeforeInitialization

    在初始化方法前执行,可修改 Bean 属性或返回包装对象。

    java 复制代码
    public class CustomBeanPostProcessor implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) {
            if (bean instanceof MyBean) {
                System.out.println("BeforeInitialization: " + beanName);
            }
            return bean;
        }
    }
  • postProcessAfterInitialization

    在初始化方法后执行,常用于生成代理对象。

    java 复制代码
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof MyService) {
            return Enhancer.create(bean.getClass(), (MethodInterceptor) (obj, method, args, proxy) -> {
                System.out.println("代理逻辑执行");
                return proxy.invokeSuper(obj, args);
            });
        }
        return bean;
    }
4.2 @PostConstruct 的实现机制
  • CommonAnnotationBeanPostProcessor 处理。
  • 底层通过反射调用标记了 @PostConstruct 的方法。
  • 执行顺序控制 :该处理器优先级高于处理 InitializingBean 的逻辑。

5. 初始化过程中的典型问题与解决方案

5.1 初始化方法未执行
  • 常见原因
    • Bean 未被 Spring 管理(如未加 @Component)。
    • 原型作用域 Bean 未被正确获取。
    • 自定义 init-method 名称拼写错误。
  • 排查工具
    启用 Spring 调试日志(logging.level.org.springframework.beans=DEBUG)。
5.2 初始化顺序依赖
  • 需求场景:Bean A 需在 Bean B 初始化完成后才能初始化。

  • 解决方案

    • 使用 @DependsOn("b") 注解。
    java 复制代码
    @Component
    @DependsOn("b")
    public class A {
        // ...
    }
5.3 在初始化方法中访问其他 Bean
  • 风险:若依赖的 Bean 尚未初始化完成,可能导致 NPE。
  • 最佳实践
    通过 ApplicationContext.getBean() 延迟获取(需谨慎使用)。

6. 初始化阶段与其他生命周期的交互

阶段 与初始化的关系
实例化 初始化必须在实例化之后(对象已存在)
属性注入 所有依赖注入完成才会触发初始化(确保 @Autowired 字段可用)
AOP 代理 代理通常在 postProcessAfterInitialization 生成,因此原始 Bean 的初始化方法先执行
销毁阶段 初始化与销毁方法通过对称设计(如 @PreDestroy 对应 @PostConstruct

7. 特殊场景下的初始化行为

7.1 原型(Prototype)Bean
  • 特点:每次请求创建新实例,初始化方法每次都会执行。
  • 陷阱 :Spring 不会管理原型 Bean 的销毁阶段,@PreDestroy 不生效。
7.2 延迟初始化(Lazy Init)
  • 配置方式@Lazy<bean lazy-init="true">
  • 行为:在首次访问时才会触发初始化和依赖注入。
7.3 FactoryBean 的初始化
  • 特殊逻辑
    FactoryBean.getObject() 返回的对象会单独走初始化流程。

总结:初始化阶段的核心要点

要素 关键实现
执行顺序 @PostConstructInitializingBeaninit-method
扩展点 BeanPostProcessor@PostConstructInitializingBean
代理生成时机 postProcessAfterInitialization(可能覆盖原始 Bean)
设计原则 通过接口与注解分离关注点,避免在初始化方法中实现复杂业务逻辑
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
体育分享_大眼35 分钟前
从零搭建高并发体育直播网站:架构设计、核心技术与性能优化实战
java·性能优化·系统架构
琢磨先生David2 小时前
Java 在人工智能领域的突围:从企业级架构到边缘计算的技术革新
java·人工智能·架构
计算机学姐2 小时前
基于SpringBoo的地方美食分享网站
java·vue.js·mysql·tomcat·mybatis·springboot·美食
Hanson Huang5 小时前
【数据结构】堆排序详细图解
java·数据结构·排序算法·堆排序
慕容静漪5 小时前
如何本地安装Python Flask并结合内网穿透实现远程开发
开发语言·后端·golang
ErizJ5 小时前
Golang|锁相关
开发语言·后端·golang
软件测试曦曦5 小时前
16:00开始面试,16:08就出来了,问的问题有点变态。。。
自动化测试·软件测试·功能测试·程序人生·面试·职场和发展
路在脚下@5 小时前
Redis实现分布式定时任务
java·redis
xrkhy5 小时前
idea的快捷键使用以及相关设置
java·ide·intellij-idea
巨龙之路5 小时前
Lua中的元表
java·开发语言·lua