Spring Bean初始化的三种方式

280 阅读3分钟

一、 实现 InitializingBean 接口

通过实现InitializingBean接口并重写afterPropertiesSet()方法,Spring会在依赖注入完成后调用此方法进行初始化。

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class MyBean implements InitializingBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化逻辑(在属性注入后执行)
        System.out.println("InitializingBean: afterPropertiesSet() called");
    }
}

二、 实现 ApplicationContextAware 接口

通过实现ApplicationContextAware接口,Bean可以获取ApplicationContext实例(例如获取其他Bean)。注意:这主要用于获取上下文,初始化逻辑建议结合@PostConstruct使用。

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class ContextAwareBean implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;  // 保存ApplicationContext
        System.out.println("ApplicationContext injected");
    }

    // 可选:结合@PostConstruct执行初始化
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct in ContextAwareBean");
        // 通过applicationContext获取其他Bean等操作
    }
}

三、使用 @PostConstruct 注解

✅ 使用条件:

  1. 只能用在非静态的方法上。
  2. 方法不能有参数。
  3. 返回类型必须为 void
  4. 通常用于初始化 Bean 的依赖或加载资源。 这是最推荐的方式(JSR-250标准)。在方法上添加@PostConstruct注解,Spring会在依赖注入后自动调用该方法。

✅ 示例代码:

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;

@Component
public class PostConstructBean {

    @PostConstruct
    public void init() {
        // 初始化逻辑(在属性注入后执行)
        System.out.println("@PostConstruct: init() called");
    }
}

✅调用时机

在 Spring 容器中,一个 Bean 的生命周期大致如下:

  1. 实例化:Spring 调用构造函数创建 Bean 实例。

  2. 属性注入:Spring 注入依赖(如其他 Bean 或配置值)。

  3. 初始化回调

    • 如果 Bean 实现了 InitializingBean 接口,则调用 afterPropertiesSet()
    • 如果定义了 @PostConstruct 注解方法,则优先调用该方法。
  4. 使用 Bean

  5. 销毁前回调:如 @PreDestroy 或 DisposableBean.destroy()

因此,@PostConstruct 是在构造函数和属性注入完成后立即执行的

✅与构造函数的区别

对比项构造函数@PostConstruct
执行时间Bean 创建时最先执行构造函数执行后,属性注入完成后执行
是否支持依赖注入❌ 不支持(此时依赖尚未注入)✅ 支持(所有依赖已注入)
是否适合初始化业务逻辑❌ 否✅ 是

✅ 原理简析

  1. 底层机制

    • Spring 框架通过 CommonAnnotationBeanPostProcessor 来识别并处理 @PostConstruct 注解。
    • 当 Bean 被创建并完成属性注入后,Spring 会查找带有 @PostConstruct 的方法,并反射调用它。
  2. AOP 代理中的行为

    • 如果 Bean 被 AOP 代理包裹,@PostConstruct 方法仍然会在目标对象初始化阶段执行一次,不会受到代理影响。

✅ 注意事项

  1. 不要在 @PostConstruct 中启动线程或长时间阻塞主线程,这会影响容器启动。
  2. 避免循环依赖:如果两个 Bean 相互依赖并在 @PostConstruct 中调用对方的方法,可能会导致问题。
  3. 多个 @PostConstruct 方法:可以在一个类中定义多个,但它们的执行顺序不确定,建议合并成一个方法。

总结

执行顺序与对比

方式执行顺序耦合度推荐度
@PostConstruct最先执行无框架耦合★★★★★
InitializingBean其次执行耦合Spring接口★★★☆☆
ApplicationContextAware最早注入上下文耦合Spring接口★★★★☆

实际初始化逻辑应优先使用@PostConstruct,需要上下文时才结合ApplicationContextAware


配置要求

确保Spring启用了注解扫描:

<!-- XML配置方式 -->
<context:component-scan base-package="com.example"/>

或使用Java配置:

@Configuration
@ComponentScan("com.example")
public class AppConfig {}

完整示例代码

// 同时使用三种方式的Bean
@Component
public class AllInOneBean implements 
        ApplicationContextAware, InitializingBean {

    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext context) {
        this.context = context;
        System.out.println("ApplicationContext set");
    }

    @PostConstruct
    public void postConstruct() {
        System.out.println("@PostConstruct executed");
    }

    @Override
    public void afterPropertiesSet() {
        System.out.println("InitializingBean executed");
    }
}

输出顺序

ApplicationContext set
@PostConstruct executed
InitializingBean executed

关键提示

  • @PostConstruct 是初始化逻辑的首选(解耦、标准)
  • 需要ApplicationContext时才实现ApplicationContextAware
  • 避免在InitializingBean@PostConstruct中重复实现相同逻辑