SpringBean生命周期

发布于:2024-08-10 ⋅ 阅读:(102) ⋅ 点赞:(0)

Spring Bean 的生命周期

Spring 是一个功能强大的 Java 应用程序框架,以其灵活的依赖注入和控制反转(IOC)容器而闻名。Spring Bean 是 Spring IOC 容器中管理的对象,其生命周期从实例化到销毁,包含多个重要阶段。了解 Spring Bean 的生命周期对于开发高效、可维护的 Spring 应用程序至关重要。

1. 什么是 Spring Bean?

Spring Bean 是由 Spring 容器管理的对象。它们是应用程序中依赖注入的核心,通过 Spring 配置文件或注解声明,由 Spring 容器负责其创建、初始化、使用和销毁。

1_Spring是如何管理Bean的?

Spring通过IoC容器来管理Bean,我们可以通过XML配置或者注解配置,来指导IoC容器对Bean的管理。因为注解配置比XML配置方便很多,所以现在大多时候会使用注解配置的方式。

  1. @ComponentScan用于声明扫描策略,通过它的声明,容器就知道要扫描哪些包下带有声明的类,也可以知道哪些特定的类是被排除在外的。

  2. @Component@Repository@Service@Controller用于声明 (自定义类)Bean,它们的作用一样,但是语义不同。@Component用于声明通用的Bean,@Repository用于声明DAO层的Bean,@Service用于声明业务层的Bean,@Controller用于声明视图层的控制器Bean,被这些注解声明的类就可以被容器扫描并创建。

    如果这个类它不是我们自己定义的,而是引入的第三方依赖当中提供的类,而且我们还想将这个类交给IOC容器管理。此时我们就需要在配置类(@Configuration)中定义一个方法,在方法上加上一个@Bean注解,通过这种方式来声明第三方的bean对象。

  3. @Autowired@Qualifier用于注入Bean,即告诉容器应该为当前属性注入哪个Bean。其中,@Autowired是按照Bean的类型进行匹配的,如果这个属性的类型具有多个Bean,就可以通过@Qualifier指定Bean的名称,以消除歧义。

  4. @Scope用于声明Bean的作用域,默认情况下Bean是单例的,即在整个容器中这个类型只有一个实例。可以通过@Scope注解指定prototype值将其声明为多例的,也可以将Bean声明为session级作用域、request级作用域等等,但最常用的还是默认的单例模式。

  5. @PostConstruct@PreDestroy用于声明Bean的生命周期。其中,被@PostConstruct修饰的方法将在Bean实例化后被调用,@PreDestroy修饰的方法将在容器销毁前被调用。

2_Spring Bean 的作用域

可以借助Spring中的@Scope注解来进行配置作用域:

类型 说明
singleton 在Spring容器中仅存在一个实例,即Bean以单例的形式存在。
prototype 每次调用getBean()时,都会执行new操作,返回一个新的实例。
request 每次HTTP请求都会创建一个新的Bean。
session 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。
globalSession 同一个全局的Session共享一个Bean,一般用于Portlet环境。

3_Spring Bean对象的获取

Spring容器中提供了一些方法,可以主动从IOC容器中获取到bean对象,下面介绍3种常用方式:

  1. 根据name获取bean

    Object getBean(String name)
    
  2. 根据类型获取bean

    <T> T getBean(Class<T> requiredType)
    
  3. 根据name获取bean(带类型转换)

    <T> T getBean(String name, Class<T> requiredType)
    

思考:要从IOC容器当中来获取到bean对象,需要先拿到IOC容器对象,怎么样才能拿到IOC容器呢?

  • 想获取到IOC容器,直接将IOC容器对象注入进来就可以了
    @Autowired
    private ApplicationContext applicationContext; //IOC容器对象
    

2. Spring Bean 的生命周期概览

Spring Bean 的生命周期由多个阶段组成,包括实例化、依赖注入、初始化和销毁。每个阶段都有特定的回调方法供开发者自定义处理逻辑。

创建
依赖注入
初始化
可用
销毁
  1. 创建:根据 bean 的构造方法或者工厂方法来创建 bean 实例对象
  2. 依赖注入:根据 @Autowired,@Value 或其它一些手段,为 bean 的成员变量填充值、建立关系
  3. 初始化:回调各种 Aware 接口,调用对象的各种初始化方法
  4. 销毁:在容器关闭时,会销毁所有单例对象(即调用它们的销毁方法)
    • prototype 对象也能够销毁,不过需要容器这边主动调用

在这里插入图片描述

接口/注解 方法 作用
BeanNameAware void setBeanName(String name) 让 Bean 获取它在 Spring 容器中的名称。
BeanFactoryAware void setBeanFactory(BeanFactory beanFactory) 让 Bean 获取 Spring 容器的 BeanFactory 引用,用于访问其他 Bean 或容器内的配置信息。
ApplicationContextAware void setApplicationContext(ApplicationContext applicationContext) 让 Bean 获取 ApplicationContext 引用,用于更高级的容器操作。
BeanPostProcessor Object postProcessBeforeInitialization(Object bean, String beanName) 在 Bean 初始化之前执行自定义逻辑,这里返回的对象若不为 null 会替换掉原本的 bean,并且仅会走 postProcessAfterInitialization 流程。
BeanPostProcessor Object postProcessAfterInitialization(Object bean, String beanName) 在 Bean 初始化之后执行自定义逻辑,这里返回的对象会替换掉原本的 bean, 如代理增强
InitializingBean void afterPropertiesSet() 在所有属性设置完成后执行自定义初始化逻辑。
DisposableBean void destroy() 在容器销毁 Bean 之前执行自定义销毁逻辑。
@PostConstruct void init() 标记一个方法在依赖注入完成后立即调用,用于自定义初始化逻辑。
@PreDestroy void preDestroy() 标记一个方法在 Bean 被销毁之前调用,用于清理资源。

这个表格概述 类中常见的生命周期接口和注解,以及它们的主要作用。

InstantiationAwareBeanPostProcessor接口

方法 作用 使用场景
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) 在 Bean 实例化之前调用,允许返回代理对象或阻止实例化。
这里返回的对象若不为 null 会替换掉原本的 bean,并且仅会走 postProcessAfterInitialization 流程
创建 Bean 的代理对象或自定义实例化逻辑。
boolean postProcessAfterInstantiation(Object bean, String beanName) 在 Bean 实例化之后、属性注入之前调用。返回 false`将阻止依赖注入。 通过返回 false 来控制 Bean 的依赖注入,或在依赖注入前执行特定操作。
PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) 在属性注入之前,修改或替换即将注入的属性值。 动态修改 Bean 的属性值,定制注入过程。

DestructionAwareBeanPostProcessor 接口

方法 作用 使用场景
void postProcessBeforeDestruction(Object bean, String beanName) 在 Bean 销毁之前调用,用于执行自定义的销毁逻辑。
这里返回的对象会替换掉原本的 bean。
如 @PostConstruct、@ConfigurationProperties
在 Bean 销毁前执行清理操作,释放资源。
boolean requiresDestruction(Object bean) 判断某个 Bean 是否需要销毁,如果返回 true,则执行销毁回调方法。 确定哪些 Bean 需要销毁,以及在销毁时执行自定义操作。

这两个接口为开发者提供了对 Spring Bean 实例化和销毁过程的细粒度控制,适用于需要在这些生命周期阶段插入额外逻辑的场景。

2.1. 实例化(Instantiation)

在 Spring 容器启动时,首先创建并实例化所有定义的 Bean。这个过程通常是通过调用 Bean 的构造方法来完成的。对于单例(Singleton)作用域的 Bean,容器在启动时就会实例化它们,而对于原型(Prototype)作用域的 Bean,实例化在每次请求时进行。

2.2. 属性注入(Dependency Injection)

在 Bean 实例化之后,Spring 会自动注入其依赖。依赖可以通过构造方法、setter 方法或字段注入的方式进行。这一过程是 Spring IOC 容器的核心功能之一。

2.3. 初始化(Initialization)

Bean 属性注入完成后,Spring 容器会调用任何实现了 InitializingBean 接口的 Bean 的 afterPropertiesSet() 方法,或执行使用 @PostConstruct 注解的方法。开发者也可以在 Bean 配置文件中指定自定义的初始化方法,通过 init-method 属性来定义。

2.4. 使用(Usage)

在初始化完成后,Bean 进入使用阶段。在这个阶段,Bean 处于就绪状态,可以被应用程序中的其他组件调用和使用。这是 Bean 生命周期中最长的阶段。

2.5. 销毁(Destruction)

当 Spring 容器关闭时,它会自动销毁所有的单例作用域的 Bean。对于实现了 DisposableBean 接口的 Bean,Spring 容器会调用其 destroy() 方法。开发者也可以通过配置文件或注解(如 @PreDestroy)定义自定义的销毁方法。

对于原型作用域的 Bean,Spring 容器不会自动管理它们的销毁,开发者需要手动处理。

3. Spring Bean 生命周期中的关键回调方法

  • afterPropertiesSet():当 Bean 实现了 InitializingBean 接口时,Spring 在 Bean 属性设置完成后调用此方法。
  • destroy():当 Bean 实现了 DisposableBean 接口时,Spring 容器在销毁 Bean 时调用此方法。
  • @PostConstruct:在依赖注入完成后,执行带有此注解的方法。它通常用于替代 afterPropertiesSet() 方法。
  • @PreDestroy:在 Spring 容器销毁 Bean 之前,执行带有此注解的方法。它通常用于替代 destroy() 方法。
  • init-methoddestroy-method:在 XML 配置文件或 Java 配置类中定义的初始化和销毁方法。

4. Spring Bean 的生命周期流程图

实例化(Instantiation) → 属性注入(Dependency Injection) → 初始化(Initialization) → 使用(Usage) → 销毁(Destruction)

5. 示例代码

以下是一个简单的示例,展示了 Spring Bean 的生命周期:

注意:8.9. 方法都是全局的。且不会对自身生效。

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;


@Component
public class ApiController implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, BeanPostProcessor ,
        InitializingBean, DisposableBean,Aware {


    private DemoDao demoMySQLDaoImpl;

    @Resource
    public void setAli(DemoDao demoMySQLDaoImpl) {
        System.out.println("2.属性注入........");
        this.demoMySQLDaoImpl = demoMySQLDaoImpl;
    }

    public ApiController() {
        System.out.println("1.构造方法...........实例化");
    }

    @Override
    public void setBeanName(String s) {
        System.out.println("3.setBeanName");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("4.setBeanFactory");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("5.setApplicationContext");
    }

    @PostConstruct
    public void init() {
        System.out.println("6.init....PostConstruct........初始化");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("7.afterPropertiesSet");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")||beanName.equals("apiController"))
            System.out.println("8.postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")||beanName.equals("apiController"))
            System.out.println("9.postProcessAfterInitialization");
        return bean;
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("10....PreDestroy.....销毁");
    }

    public void destroy(){
        System.out.println("11....destroy");
    }


}

测试生命周期类:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
public class LifeCycleBean {

    public LifeCycleBean() {
        System.out.println(this+"构造");
    }

    @Autowired
    public void autowire(@Value("${JAVA_HOME}") String home) {

        System.out.println(this+"依赖注入"+home);

    }

    @PostConstruct
    public void init() {
        System.out.println(this+"初始化");
    }

    @PreDestroy
    public void destroy() {
        System.out.println(this+"销毁");
    }
}

注意:下面的方法都是全局生效的,也就是说如果不加 if 只要是个Bean就会打印输出,会输出很多份。

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class MyBeanPostProcessor implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            System.out.println("<<<<<< 销毁之前执行, 如 @PreDestroy");
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            System.out.println("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean")) {
            System.out.println("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
//            return false;
        }
        return true;
    }

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            System.out.println("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");
        return pvs;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            System.out.println("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("lifeCycleBean"))
            System.out.println("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");
        return bean;
    }
}

测试生命周期的组件类

在 Spring 启动类中:

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SpringTestApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(SpringTestApplication.class, args);
        run.close();
    }
}

输出结果:

1.构造方法...........实例化

2.属性注入........
3.setBeanName
4.setBeanFactory
5.setApplicationContext
6.init....PostConstruct........初始化
7.afterPropertiesSet

<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean
com.shenyang.spi.LifeCycleBean@79d9214d构造
<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段
<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource
com.shenyang.spi.LifeCycleBean@79d9214d依赖注入E:\shenyang\.jdks\jdk-1.8
8.postProcessBeforeInitialization
<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties
com.shenyang.spi.LifeCycleBean@79d9214d初始化
com.shenyang.spi.ApiController@5c534b5b 9.postProcessAfterInitialization
<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强

<<<<<< 销毁之前执行, 如 @PreDestroy
com.shenyang.spi.LifeCycleBean@79d9214d销毁
10....PreDestroy.....销毁
11....destroy

进程已结束,退出代码0

为什么会这样呢💡:

如果同一个 bean 用了以上手段声明了 3 个初始化方法,那么它们的执行顺序是

  1. @PostConstruct 标注的初始化方法
  2. InitializingBean 接口的初始化方法
  3. @Bean(initMethod) 指定的初始化方法

与初始化类似,Spring 也提供了多种销毁手段,执行顺序为

  1. @PreDestroy 标注的销毁方法
  2. DisposableBean 接口的销毁方法
  3. @Bean(destroyMethod) 指定的销毁方法

6. Bean 后处理器

6.1 后处理器作用💡

  1. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能,这些扩展功能由 bean 后处理器来完成
  2. 每个后处理器各自增强什么功能
    • AutowiredAnnotationBeanPostProcessor 解析 @Autowired 与 @Value
    • CommonAnnotationBeanPostProcessor 解析 @Resource、@PostConstruct、@PreDestroy
    • ConfigurationPropertiesBindingPostProcessor 解析 @ConfigurationProperties
  3. 另外 ContextAnnotationAutowireCandidateResolver 负责获取 @Value 的值,解析 @Qualifier、泛型、@Lazy 等

6.2 @Autowired bean 后处理器运行分析💡

  1. AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata 用来获取某个 bean 上加了 @Value @Autowired 的成员变量,方法参数的信息,表示为 InjectionMetadata
  2. InjectionMetadata 可以完成依赖注入
  3. InjectionMetadata 内部根据成员变量,方法参数封装为 DependencyDescriptor 类型
  4. 有了 DependencyDescriptor,就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找

6.3 BeanFactory 后处理器

  1. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能
  2. 这些扩展功能由不同的 BeanFactory 后处理器来完成,其实主要就是补充了一些 bean 定义

7. Aware 接口

在 Spring 框架中,Aware 接口是一组用于让 Bean 获取到 Spring 容器内特定资源或信息的接口。这些接口提供了一种依赖注入之外的机制,通过实现这些接口,Bean 可以访问到 Spring 容器的一些内部资源,如 Bean 名称、容器上下文、类加载器等。此类型的接口方法名都是setXXXX形式的。

  1. Aware 接口提供了一种【内置】 的注入手段,例如
    • BeanNameAware 注入 bean 的名字
    • BeanFactoryAware 注入 BeanFactory 容器
    • ApplicationContextAware 注入 ApplicationContext 容器
    • EmbeddedValueResolverAware 注入 ${} 解析器
  2. InitializingBean 接口提供了一种【内置】的初始化手段
  3. 对比
    • 内置的注入和初始化不受扩展功能的影响,总会被执行
    • 而扩展功能受某些情况影响可能会失效
    • 因此 Spring 框架内部的类常用内置注入和初始化

7.1 配置类 @Autowired 失效分析

Java 配置类不包含 BeanFactoryPostProcessor 的情况

ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 1. 执行 BeanFactoryPostProcessor 2. 注册 BeanPostProcessor 3. 创建和初始化 3.1 依赖注入扩展(如 @Value 和 @Autowired) 3.2 初始化扩展(如 @PostConstruct) 3.3 执行 Aware 及 InitializingBean 3.4 创建成功 ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类

Java 配置类包含 BeanFactoryPostProcessor 的情况,因此要创建其中的 BeanFactoryPostProcessor 必须提前创建 Java 配置类,而此时的 BeanPostProcessor 还未准备好,导致 @Autowired 等注解失效

ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类 3. 创建和初始化 3.1 执行 Aware 及 InitializingBean 3.2 创建成功 1. 执行 BeanFactoryPostProcessor 2. 注册 BeanPostProcessor ApplicationContext BeanFactoryPostProcessor BeanPostProcessor Java配置类

7.2 对应代码

@Configuration
public class MyConfig1 {

    private static final Logger log = LoggerFactory.getLogger(MyConfig1.class);

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        log.debug("注入 ApplicationContext");
    }

    @PostConstruct
    public void init() {
        log.debug("初始化");
    }

    @Bean //  ⬅️ 注释或添加 beanFactory 后处理器对应上方两种情况
    public BeanFactoryPostProcessor processor1() {
        return beanFactory -> {
            log.debug("执行 processor1");
        };
    }

}

注意

解决方法:

  • 用内置依赖注入和初始化取代扩展依赖注入和初始化
  • 用静态工厂方法代替实例工厂方法,避免工厂对象提前被创建

8. 结论

理解 Spring Bean 的生命周期对于开发者来说非常重要,因为它帮助我们更好地管理对象的创建、初始化、使用和销毁过程。通过掌握这些生命周期的细节,开发者可以编写更高效、更可靠的 Spring 应用程序,充分利用 Spring 提供的强大功能。


网站公告

今日签到

点亮在社区的每一天
去签到