行业资讯 2025年08月6日
0 收藏 0 点赞 631 浏览 5432 个字
摘要 :

文章目录 一、初始化阶段的触发时机 二、初始化方法的执行流程 (一)@PostConstruct注解方法 (二)InitializingBean.afterPropertiesSet() (三)自定义init-method ……




  • 一、初始化阶段的触发时机
  • 二、初始化方法的执行流程
    • (一)@PostConstruct注解方法
    • (二)InitializingBean.afterPropertiesSet()
    • (三)自定义init-method
  • 三、初始化过程的源码分析
  • 四、初始化扩展点详解
    • (一)BeanPostProcessor的作用
    • (二)@PostConstruct的实现机制
  • 五、初始化过程中的典型问题及解决方案
    • (一)初始化方法未执行
    • (二)初始化顺序依赖
    • (三)在初始化方法中访问其他Bean
  • 六、初始化阶段与其他生命周期的交互
  • 七、特殊场景下的初始化行为
    • (一)原型(Prototype)Bean
    • (二)延迟初始化(Lazy Init)
    • (三)FactoryBean的初始化
  • 八、总结初始化阶段的核心要点

Spring框架中Bean的初始化是一个关键的环节,它处于Bean生命周期的重要位置,涉及多个扩展点,执行顺序也有着严格的控制。深入了解这一过程,对开发者更好地运用Spring框架至关重要。下面,我们就来详细剖析一下Spring中Bean的初始化过程。

一、初始化阶段的触发时机

Bean的初始化发生在属性注入(也就是依赖注入)完成之后。当依赖注入结束,Bean的实例虽然已经创建出来了,但此时它还不能算完全准备好。比如,可能还没有生成代理对象,一些自定义的逻辑也还未执行。只有在初始化完成后,Bean才能正式投入使用。

二、初始化方法的执行流程

Spring在Bean初始化时,会按照特定的顺序调用三类初始化方法。

(一)@PostConstruct注解方法

这是JSR – 250标准里的注解,由CommonAnnotationBeanPostProcessor进行处理,在所有初始化方法中执行顺序是最先的。看下面这个示例代码:

@Component
public class MyBean {
    @PostConstruct
    public void init() {
        System.out.println(\"@PostConstruct方法执行\");
    }
}

在这个例子里,当MyBean进行初始化时,@PostConstruct注解标注的init方法会首先执行。

(二)InitializingBean.afterPropertiesSet()

InitializingBean是Spring原生提供的接口。实现了这个接口的Bean,其afterPropertiesSet方法会在@PostConstruct注解方法之后执行。示例代码如下:

@Component
public class MyBean implements InitializingBean {
    @Override
    public void afterPropertiesSet() {
        System.out.println(\"InitializingBean方法执行\");
    }
}

当MyBean初始化时,在@PostConstruct方法执行完后,就会执行afterPropertiesSet方法。

(三)自定义init-method

我们可以通过XML配置<bean init-method=\"init\">,或者在Java配置类里使用@Bean(initMethod = \"init\")来指定自定义的初始化方法。这个方法是在前面两种方法之后执行的。示例如下:

public class MyBean {
    public void customInit() {
        System.out.println(\"自定义init-method执行\");
    }
}

@Configuration
public class AppConfig {
    @Bean(initMethod = \"customInit\")
    public MyBean myBean() {
        return new MyBean();
    }
}

在上述代码中,customInit方法就是自定义的初始化方法,会在@PostConstructInitializingBean.afterPropertiesSet()执行之后运行。

三、初始化过程的源码分析

Spring通过initializeBean()方法来协调整个初始化流程,下面是这个方法在AbstractAutowireCapableBeanFactory.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方法尤为关键,它负责具体执行初始化方法,源码如下:

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);
    }
}

从这段代码可以看出,它先检查Bean是否实现了InitializingBean接口,如果实现了就执行afterPropertiesSet方法,然后再执行自定义的init - method

四、初始化扩展点详解

(一)BeanPostProcessor的作用

BeanPostProcessor提供了两个重要的方法,用于在Bean初始化前后进行额外的处理。

  1. postProcessBeforeInitialization:这个方法在初始化方法执行之前调用,开发者可以利用它来修改Bean的属性,或者返回一个包装对象。例如:
public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (bean instanceof MyBean) {
            System.out.println(\"BeforeInitialization: \" + beanName);
        }
        return bean;
    }
}

在上述代码中,当检测到要处理的Bean是MyBean时,就会打印一条信息。
2. postProcessAfterInitialization:该方法在初始化方法执行之后调用,常被用于生成代理对象。示例代码如下:

@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;
}

在这个例子里,如果Bean是MyService类型,就会为其创建一个代理对象,并在代理逻辑中打印一条信息。

(二)@PostConstruct的实现机制

@PostConstruct注解是由CommonAnnotationBeanPostProcessor处理的。它的底层原理是通过反射机制,调用那些标记了@PostConstruct的方法。而且,这个处理器的优先级比处理InitializingBean的逻辑要高,所以@PostConstruct注解方法会先执行。

五、初始化过程中的典型问题及解决方案

(一)初始化方法未执行

  1. 常见原因
    • Bean没有被Spring管理,比如没有添加@Component注解。
    • 对于原型作用域的Bean,获取方式不正确。
    • 自定义的init - method名称拼写错误。
  2. 排查工具:我们可以启用Spring的调试日志(logging.level.org.springframework.beans=DEBUG ),通过查看日志来定位问题。

(二)初始化顺序依赖

在实际开发中,可能会遇到Bean A需要在Bean B初始化完成后才能初始化的情况。这时,可以使用@DependsOn(\"b\")注解来解决。例如:

@Component
@DependsOn(\"b\")
public class A {
    //...
}

这样,Spring在初始化A时,会先确保B已经完成初始化。

(三)在初始化方法中访问其他Bean

在初始化方法里访问其他Bean时,存在一定风险。如果依赖的Bean还没有初始化完成,就可能会导致空指针异常(NPE)。比较好的做法是通过ApplicationContext.getBean()方法延迟获取,但这种方式需要谨慎使用。

六、初始化阶段与其他生命周期的交互

Bean的初始化阶段和其他生命周期阶段有着紧密的联系:

  • 实例化:初始化必须在实例化之后进行,因为只有先创建出对象,才谈得上对其进行初始化。
  • 属性注入:只有当所有依赖注入都完成后,才会触发初始化,这样可以确保@Autowired注解的字段在初始化时是可用的。
  • AOP代理:通常情况下,代理对象是在postProcessAfterInitialization方法中生成的,所以原始Bean的初始化方法会先执行。
  • 销毁阶段:初始化和销毁方法在设计上是对称的,比如@PreDestroy@PostConstruct就相互对应。

七、特殊场景下的初始化行为

(一)原型(Prototype)Bean

原型Bean的特点是每次请求都会创建一个新的实例,所以它的初始化方法每次都会执行。不过需要注意的是,Spring不会管理原型Bean的销毁阶段,@PreDestroy注解在这种情况下是不生效的。

(二)延迟初始化(Lazy Init)

我们可以通过@Lazy注解或者<bean lazy - init=\"true\">配置来实现延迟初始化。采用这种方式后,Bean的初始化和依赖注入会在首次访问时才触发。

(三)FactoryBean的初始化

FactoryBean比较特殊,它的getObject()方法返回的对象会单独走一套初始化流程。

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

  • 执行顺序:初始化方法的执行顺序是@PostConstruct注解方法 → InitializingBean接口的afterPropertiesSet方法 → 自定义init - method
  • 扩展点:主要的扩展点有BeanPostProcessor@PostConstruct注解、InitializingBean接口。
  • 代理生成时机:代理对象一般在postProcessAfterInitialization方法中生成,生成的代理对象可能会覆盖原始Bean。
  • 设计原则:Spring通过接口和注解将不同的关注点分离,开发者应避免在初始化方法中编写过于复杂的业务逻辑。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/10494.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号