SpringBean生命周期

devtools/2024/9/24 19:18:46/

文章目录

  • Spring Bean 的生命周期
  • 1. 什么是 Spring Bean?
    • 1_Spring是如何管理Bean的?
    • 2_Spring Bean 的作用域
    • 3_Spring Bean对象的获取
  • 2. Spring Bean 的生命周期概览
    • 2.1. 实例化(Instantiation)
    • 2.2. 属性注入(Dependency Injection)
    • 2.3. 初始化(Initialization)
    • 2.4. 使用(Usage)
    • 2.5. 销毁(Destruction)
  • 3. Spring Bean 生命周期中的关键回调方法
  • 4. Spring Bean 的生命周期流程图
  • 5. 示例代码
  • 6. 理解后置处理器
    • 6.1 介绍 BeanDefinition
    • 6.2 演示bean后处理器
    • 6.3 bean 后处理器作用💡
    • 6.4 @Autowired bean 后处理器运行分析💡
    • 6.5 演示 BeanFactory 后处理器
    • 6.6 BeanFactory 后处理器的作用
    • 6.7 BeanDefinitionRegistry 后置处理器
    • 6.8 三种 PostProcessor 的对比
  • 7. Aware 接口
    • 7.1 配置类 @Autowired 失效分析
    • 7.2 对应代码
  • 8. 结论

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环境。

@Lazy 注解:可以在其中一个 Bean 的依赖注入上使用 @Lazy 注解,这样 Spring 就会延迟初始化该 Bean,直到实际需要时才创建它。

例如:如果初始化ApplicationContext容器时为一个Scope为单例的 bean对象注入作用域为requestsession的依赖,在不加@Lazy注解的情况下会抛出异常。原因是ApplicationContext容器获取bean类似于饿汉式,并且单例bean对象所注入依赖bean的requestsession作用域并没有激活(没有在HTTP 请求上下文中),导致 Spring 无法正确地创建这个依赖bean并注入。

单例注入多例会导致Scope失效,假设单例对象 E 注入多例(原型)对象 F:

对于单例对象来讲,依赖注入仅发生了一次,后续再没有用到多例的 F,因此 E 用的始终是第一次依赖注入的 F。

e 创建
e set 注入 f
f 创建

解决——单例注入其他作用域的 bean 都会有问题,不仅仅是prototype。都可以采用以下方式:

  • 仍然使用 @Lazy 生成代理
  • 代理对象虽然还是同一个,但当每次使用代理对象的任意方法时,由代理创建新的 f 对象
使用f方法
使用f方法
使用f方法
e 创建
e set 注入 f代理
f 创建
f 创建
f 创建
  1. 单例注入其它 scope 的四种解决方法

    • @Lazy
    • @Scope(value = “prototype”, proxyMode = ScopedProxyMode.TARGET_CLASS)
    • ObjectFactory
      @Autowired
      private ObjectFactory<F> f;public F getF() {return f.getObject();
      }
      
    • ApplicationContext
      @Autowired
      private ApplicationContext context;public F getF() {return context.getBean(F.class);
      }
      
  2. 解决方法虽然不同,但理念上殊途同归: 都是推迟其它 scope bean 的获取

    @Component
    public class E {@Autowired@Lazypublic void setF(F f) {this.f = f;log.info("setF(F f) {}", f.getClass());}// ...
    }
    

    注意

    • @Lazy 加在也可以加在成员变量上,但加在 set 方法上的目的是可以观察输出,加在成员变量上就不行了
    • @Autowired 加在 set 方法的目的类似
    • 代理对象是原对象的子类。

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容器对象
    

常见的 Spring 容器实现,供大家参考:

  • DefaultListableBeanFactory,是 BeanFactory 最重要的实现,像控制反转依赖注入功能,都是它来实现
  • ClassPathXmlApplicationContext,从类路径查找 XML 配置文件,创建容器(旧)
  • FileSystemXmlApplicationContext,从磁盘路径查找 XML 配置文件,创建容器(旧)
  • XmlWebApplicationContext,传统 SSM 整合时,基于 XML 配置文件的容器(旧)
  • AnnotationConfigWebApplicationContext,传统 SSM 整合时,基于 java 配置类的容器(旧)
  • AnnotationConfigApplicationContext,Spring boot 中非 web 环境容器(新)
  • AnnotationConfigServletWebServerApplicationContext,Spring boot 中 servlet web 环境容器(新)
  • AnnotationConfigReactiveWebServerApplicationContext,Spring boot 中 reactive web 环境容器(新)

另外要注意的是,后面这些带有 ApplicationContext 的类都是 ApplicationContext 接口的实现,但它们是组合了 DefaultListableBeanFactory 的功能,并非继承而来。

2. Spring Bean 的生命周期概览

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

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

在这里插入图片描述

接口/注解方法作用
BeanNameAwarevoid setBeanName(String name)让 Bean 获取它在 Spring 容器中的名称。
BeanFactoryAwarevoid setBeanFactory(BeanFactory beanFactory)让 Bean 获取 Spring 容器的 BeanFactory 引用,用于访问其他 Bean 或容器内的配置信息。
ApplicationContextAwarevoid setApplicationContext(ApplicationContext applicationContext)让 Bean 获取 ApplicationContext 引用,用于更高级的容器操作。
BeanFactoryPostProcessorvoid postProcessBeanFactory (ConfigurableListableBeanFactory configurableListableBeanFactory)容器初始化之后、实际实例化任何 Bean 之前,修改应用上下文的内部 BeanFactory
BeanPostProcessorObject postProcessBeforeInitialization(Object bean, String beanName)Bean 初始化之前执行自定义逻辑,这里返回的对象若不为 null 会替换掉原本的 bean,并且仅会走 postProcessAfterInitialization 流程。
BeanPostProcessorObject postProcessAfterInitialization(Object bean, String beanName)Bean 初始化之后执行自定义逻辑,这里返回的对象会替换掉原本的 bean, 如代理增强
InitializingBeanvoid afterPropertiesSet()在所有属性设置完成后执行自定义初始化逻辑。
DisposableBeanvoid destroy()在容器销毁 Bean 之前执行自定义销毁逻辑。
@PostConstructvoid init()标记一个方法在依赖注入完成后立即调用,用于自定义初始化逻辑。
@PreDestroyvoid 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 的生命周期:

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

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
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;@Resourcepublic void setAli(DemoDao demoMySQLDaoImpl) {System.out.println("2.属性注入........");this.demoMySQLDaoImpl = demoMySQLDaoImpl;}public ApiController() {System.out.println("1.构造方法...........实例化");}@Overridepublic void setBeanName(String beanName) {//形参beanName值为apiController System.out.println("3.setBeanName");}@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {//形参值为当前对象的beanFactorySystem.out.println("4.setBeanFactory");}@Override						  //形参值为当前对象的ApplicationContext public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {System.out.println("5.setApplicationContext");}@PostConstructpublic void init() {System.out.println("6.init....PostConstruct........初始化");}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("7.afterPropertiesSet");}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")||beanName.equals("apiController"))System.out.println("9.postProcessBeforeInitialization");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")||beanName.equals("apiController"))System.out.println(this+" 10.postProcessAfterInitialization");return bean;}@PreDestroypublic void preDestroy(){System.out.println("11....PreDestroy.....销毁");}public void destroy(){System.out.println("12....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+"构造");}@Autowiredpublic void autowire(@Value("${JAVA_HOME}") String home) {System.out.println(this+"依赖注入"+home);}@PostConstructpublic void init() {System.out.println(this+"初始化");}@PreDestroypublic void destroy() {System.out.println(this+"销毁");}
}

注意:下面的方法都是全局生效的,也就是说如果不加 if 只要是个Bean就会打印输出,会输出很多份。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 {@Overridepublic void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))System.out.println("<<<<<< 销毁之前执行, 如 @PreDestroy");}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))System.out.println("<<<<<< 实例化之前执行, 这里返回的对象会替换掉原本的 bean");return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean")) {System.out.println("<<<<<< 实例化之后执行, 这里如果返回 false 会跳过依赖注入阶段");
//            return false;}return true;}@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))System.out.println("<<<<<< 依赖注入阶段执行, 如 @Autowired、@Value、@Resource");return pvs;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))System.out.println("<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("lifeCycleBean"))System.out.println("<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强");return bean;}
}

BeanFactory 后处理器:

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.stereotype.Component;/*** @author shenyang* @version 1.0* @info test* @since 2024/8/10 19:30*/
@Component
public class MyBeanFactoryPostProcessorOne implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {System.out.println("8....postProcessBeanFactory----在容器初始化之后、实际实例化任何 Bean 之前,修改应用上下文的内部 BeanFactory");}
}

测试生命周期的组件类

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

输出结果——如果postProcessBeanFactory在apiController中才会出现在8号位:

8....postProcessBeanFactory----在容器初始化之后、实际实例化任何 Bean 之前,修改应用上下文的内部 BeanFactory
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
9.postProcessBeforeInitialization
<<<<<< 初始化之前执行, 这里返回的对象会替换掉原本的 bean, 如 @PostConstruct、@ConfigurationProperties
com.shenyang.spi.LifeCycleBean@79d9214d初始化
com.shenyang.spi.ApiController@5c534b5b 10.postProcessAfterInitialization
<<<<<< 初始化之后执行, 这里返回的对象会替换掉原本的 bean, 如代理增强<<<<<< 销毁之前执行, 如 @PreDestroy
com.shenyang.spi.LifeCycleBean@79d9214d销毁
11....PreDestroy.....销毁
12....destroy进程已结束,退出代码0

为什么会这样呢💡:

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

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

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

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

同时,还有一个知识点需要注意,我们这里使用的是ApplicationContext作为 Spring 的容器。

如果使用BeanFactroy 的话:只有需要访问容器中的某个受管对象的时候(比如说getBean()),才对该受管对象进行初始化以及依赖注入操作。除了个别实现提供了preInstantiateSingletons()方法可以预先进行初始化操作,如:DefaultListableBeanFactory。

ApplicationContext 所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。

所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。

相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中,ApplicationContext类型的容器是比较合适的选择。

6. 理解后置处理器

Spring Framework 中设计的后置处理器主要分为两种类型:

  • 针对 bean 对象的后置处理器 BeanPostProcessor;
  • 针对 BeanDefinition 的后置处理器 BeanFactoryPostProcessor,也可以理解成针对 BeanFactory 的后置处理器。

这两种都是主要针对 IOC 容器中的 Bean,在生命周期中进行一些切入处理和干预。

另外这两者都有一些扩展的子接口,它们切入的时机也不尽相同。

6.1 介绍 BeanDefinition

BeanDefinition 描述了 Spring Framework 中 Bean 的元信息,包含Bean的类信息、属性、行为、依赖关系、配置信息等,并可以在 IOC容器的初始化阶段被拦截处理。

  • Bean 的类信息:全限定名(beanClassName)。
  • Bean 的属性:作用域(scope)、是否默认 Bean(primary)、描述信息(description)等。
  • Bean 的行为特征:是否延迟加载(lazy)、是否自动注入(autowireCandidate)、初始化/销毁的方法(initMethod/destroyMethod)等。
  • Bean 与其他 Bean 的关系:父 Bean 名称(parentName)、依赖的 Bean (dependsOn)等。
  • Bean 的配置属性:构造方法参数(constructorArgumentValues)、属性变量值(propertyValues)等。

Spring Framework 中为什么会设计BeanDefintion?为什么没有选择直接注入的方式?

如果选择直接创建出 bean 对象,放入IOC容器中,这样的设计更加简单,但同时对应了另一个问题: Bean 的管理机制过于简单,无法应对各种复杂的场景,而且无法针对某些特殊的 Bean 进行附加的处理(如 AOP代理、事务增强支持等)。

设计 BeanDefinition,本质上接近面向对象开发中的先编写 Class类,再 new 出对象。 Spring 面对一个应用程序,也需要对其中的 Bean进行定义抽取,只有抽取成可以统一类型/格式的模型,才能在后续的 bean 对象管理时进行统一管理,或者是对特定的 Bean进行特殊化处理。而这一切最终落地到同一类型上就是 BeanDefinition 这个抽象化的模型。

换一种更简单的说法,有了定义信息,按照既定的规则,就可以任意解析生成 bean对象,也可以根据实际需求对解析和生成对象的过程任意扩展。

使用模式注解+组件扫描的方式,每扫描到一个类,就相当于构建了一个 BeanDefinition。比如 @Component+ AnnotationConfigApplicationContext容器。同理基于 @Bean 注解也是 BeanDefinition 构造。

6.2 演示bean后处理器

BeanPostProcessor 切入的时机是在 bean 对象的初始化阶段前后添加自定义处理逻辑

常见的bean后处理器(PostProcessor)示例:

// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();// ⬇️️️️️用原始方法注册三个 bean
context.registerBean("bean1", Bean1.class);
context.registerBean("bean2", Bean2.class);
context.registerBean("bean3", Bean3.class);
context.registerBean("bean4", Bean4.class);//DefaultListableBeanFactory中的AutowireCandidateResolver不具备获取字符串的功能需要重新设置一下
context.getDefaultListableBeanFactory().setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
context.registerBean(AutowiredAnnotationBeanPostProcessor.class); // @Autowired @Valuecontext.registerBean(CommonAnnotationBeanPostProcessor.class); // @Resource @PostConstruct @PreDestroy//此方法为了方便还有其他方法,可能需要绑定其他设置,这个方法只需要绑定对应的工厂信息
ConfigurationPropertiesBindingPostProcessor.register(context.getDefaultListableBeanFactory());// ⬇️初始化容器
context.refresh(); // 执行beanFactory后处理器, 添加bean后处理器, 初始化所有单例System.out.println(context.getBean(Bean1.class));// ⬇️销毁容器
context.close();

补充:除了registerBean()方法外,registerSingleton(String beanName, Object singletonObject)也可以注册bean,只不过使用此方法会认为注册的bean是完好的成品了,也就是不会走创建、初始化、依赖注入的过程了。

还有DefaultListableBeanFactory,它在注册bean对象时,可以使用registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法注册 bean。需要先调用BeanDefinitionBuilder构建BeanDefinition (bean元信息),再设置bean的名称。例:

DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
AbstractBeanDefinition beanDefinition =BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);

6.3 bean 后处理器作用💡

  1. @Autowired 等注解的解析属于 bean 生命周期阶段(依赖注入, 初始化)的扩展功能,这些扩展功能由 bean 后处理器来完成
  2. 每个后处理器各自增强什么功能
    • AutowiredAnnotationBeanPostProcessor 解析 @Autowired 与 @Value
    • CommonAnnotationBeanPostProcessor 解析 @Resource、@PostConstruct、@PreDestroy
    • ConfigurationPropertiesBindingPostProcessor 解析 @ConfigurationProperties
  3. 另外 ContextAnnotationAutowireCandidateResolver 负责获取 @Value 的值,解析 @Qualifier、泛型、@Lazy 等。
  4. Resource注解先于Autowired注解,原因:beanFactory.getDependencyComparator()->OrderComparator比较器对象,内部会排序。

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

  1. BeanFactory会调用AutowiredAnnotationBeanPostProcessor.postProcessProperties方法间接再调用findAutowiringMetadata 方法。
  2. AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata 用来获取某个 bean 上加了 @Value @Autowired 的成员变量,方法参数的信息,表示为 InjectionMetadata。也就是说:查找哪些属性、方法加了 @Autowired, 这称之为 InjectionMetadata(方法返回此类型对象)。
  3. InjectionMetadata 可以完成依赖注入。
  4. InjectionMetadata 内部根据成员变量,方法参数封装为 DependencyDescriptor 类型。
  5. 有了 DependencyDescriptor,就可以利用 beanFactory.doResolveDependency 方法进行基于类型的查找。
    //此方法可以根据传递的DependencyDescriptor在BeanFactory中根据类型查找到对应的bean
    @Nullable
    public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException 
    //可以依靠反射拿到 Field字段,并封装成DependencyDescriptor 调用上方的方法在BeanFactory中查找到 Field类型的bean
    public DependencyDescriptor(Field field, boolean required) //required参数决定是抛出异常,还是返回null
    //将方法参数封装成 DependencyDescriptor,调用最上方的方法在BeanFactory中查找到方法参数类型的bean
    public DependencyDescriptor(MethodParameter methodParameter, boolean required) 
    //拿到某个方法的参数描述,parameterIndex为参数列表中的第几个参数
    public MethodParameter(Method method, int parameterIndex)
    

6.5 演示 BeanFactory 后处理器

BeanFactoryPostProcessor 切入的时机是在 IOC容器的生命周期中,所有 BeanDefinition 都注册到BeanDefinitionRegistry 后切入回调,它的主要工作是访问/修改已经存在的 BeanDefinition,BeanFactoryPostProcessor 操作的是 Bean的配置元信息(即 BeanDefinition )。

另外,BeanFactroyPostProcessor 可以在初始化之前修改 Bean 的定义信息,换句话说,它可以对原有的 BeanDefinition 进行修改。由于 Spring Framework中设计的所有 bean 对象在没有实例化以前都是以 BeanDefinition 的形式存在的,如果提前修改了 BeanDefinition ,那么在Bean的实例化时,最终创建出的 bean 对象就会受到影响。

// ⬇️GenericApplicationContext 是一个【干净】的容器
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("config", Config.class);//自定义类,内部有使用 @Bean注解的Configuration配置类
context.registerBean(ConfigurationClassPostProcessor.class); // @ComponentScan @Bean @Import @ImportResource
context.registerBean(MapperScannerConfigurer.class, bd -> { // @MapperScannerbd.getPropertyValues().add("basePackage", "com.shen.mapper");
});
//context.registerBean(ComponentScanPostProcessor.class); // 解析 @ComponentScan,自定义解析器
//context.registerBean(AtBeanPostProcessor.class); // 解析 @Bean,自定义解析器
//context.registerBean(MapperPostProcessor.class); // 解析 Mapper 接口,自定义解析器
// ⬇️初始化容器
context.refresh();
for (String name : context.getBeanDefinitionNames()) {System.out.println(name);
}
Mapper1 mapper1 = context.getBean(Mapper1.class);//自定义Mapper类
Mapper2 mapper2 = context.getBean(Mapper2.class);//自定义Mapper类
// ⬇️销毁容器
context.close();

6.6 BeanFactory 后处理器的作用

  1. ConfigurationClassPostProcessor 可以解析 @ComponentScan、@Bean、@Import、@ImportResource

  2. MapperScannerConfigurer 可以解析 Mapper 接口

  3. @ComponentScan, @Bean, @Mapper 等注解的解析属于核心容器(即 BeanFactory)的扩展功能

  4. 这些扩展功能由不同的 BeanFactory 后处理器来完成,其实主要就是补充了一些 bean 定义

6.7 BeanDefinitionRegistry 后置处理器

如果需要在 BeanFactory 的后置处理阶段动态注册新的 BeanDefinition,就可以使用BeanDefinitionRegistryPostProcessor 。从类名上可以看出,它是针对 BeanDefinitionRegistry 的后置处理器,它的执行时机比 BeanFactory 后置处理器要早,这就意味着 BeanDefinitionRegistryPostProcessor 允许在 BeanFactroyPostProcessor 之前注册新的 BeanDefinition。

从设计上来讲,BeanFactoryPostProcessor 只用来修改、扩展 BeanDefinition 中的信息,而 BeanDefinitionRegistryPostProcessor 则可以在 BeanFactroyPostProcessor 处理 BeanDefinition 之前向 BeanFactory 注册新的 BeanDefinition甚至注册新的 BeanFactoryPostProcessor 用于下一个阶段的回调

BeanDefinitionRegistryPostProcessor 在 IOC 容器的生命周期中应当出现在 “解析注册配置类 加载BeanDefinition”之后、BeanFactoryPostProcessor 的集中回调之前。

注意看 BeanDefinitionRegistryPostProcessor 接口的源代码:

package org.springframework.beans.factory.support;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}

BeanDefinitionRegistryPostProcessor接口是BeanFactoryPostProcessor的子接口。这代表了什么呢?

实现了 BeanDefinitionRegistryPostProcessor 的类同时也实现了 BeanFactoryPostProcessorpostProcessBeanFactory 方法,因此在执行完所有 BeanDefinitionRegistryPostProcessor 的接口方法后,会立即执行这些类的 postProcessBeanFactory 方法,之后再执行那些普通的只实现了 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法。

6.8 三种 PostProcessor 的对比

Spring Framework 中三种后置处理器的对比:

BeanPostProcessorBeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor
处理目标bean 对象BeanDefinitionBeanDefinition、.class文件等
执行时机bean 对象的初始化阶段前后(已创建 bean对象 )BeanDefinition 解析完毕并注册进BeanFactory之后(此时bean对象未实例化)配置文件、配置类已解析完毕并注册进BeanFactory,但还没有被BeanFactoryPostProcessor处理
可操作的空间给 bean 对象的属性赋值、创建代理对象等在 BeanDefinition 中增删属性、移除 BeanDefinition 等向 BeanFactory 注册新的 BeanDefinition

7. Aware 接口

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

  1. Aware 接口提供了一种【内置】 的注入手段,例如
    • BeanNameAware 注入 bean 的名字
    • BeanFactoryAware 注入 BeanFactory 容器
    • ApplicationContextAware 注入 ApplicationContext 容器
    • EmbeddedValueResolverAware 注入 ${} 解析器
  2. InitializingBean 接口提供了一种【内置】的初始化手段
  3. 对比——明明上方这些功能Autowired注解就可以实现,为什么还需要Aware接口。
    • Autowired 的解析需要用到 bean 后处理器, 属于扩展功能
    • 而 Aware 接口属于内置功能, 不加任何扩展, Spring 就能识别
    • 内置的注入和初始化不受扩展功能的影响,总会被执行
    • 而扩展功能受某些情况影响可能会失效
    • 因此 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);@Autowiredpublic void setApplicationContext(ApplicationContext applicationContext) {log.debug("注入 ApplicationContext");}@PostConstructpublic void init() {log.debug("初始化");}@Bean //  ⬅️ 注释或添加 beanFactory 后处理器对应上方两种情况public BeanFactoryPostProcessor processor1() {return beanFactory -> {log.debug("执行 processor1");};}}

注意

解决方法:

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

8. 结论

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


http://www.ppmy.cn/devtools/94202.html

相关文章

GPT损失和是模型模型是否真的学会(困惑度)。

#加入输入如下&#xff0c;label 也就是输出希望是targets inputs torch.tensor([[16833, 3626, 6100], # ["every effort moves",[40, 1107, 588]]) # "I really like"]targets torch.tensor([[3626, 6100, 345 ], # [" effort moves yo…

【自动驾驶】ROS中的重名问题:工作空间、节点、参数

目录 功能包的重名ROS节点名称重名设置命名空间解决在launch设置命名空间c编码实现 Ros话题重名处理启动时的话题重映射使用launch文件完成话题重映射c编码实现话题重映射全局名称相对名称私有名称 ROS参数设置启动节点时设置launch文件进行设置使用c编码进行设置 功能包的重名…

车身域测试学习、CANoe工具实操学习、UDS诊断测试、功能安全测试、DTC故障注入测试、DBC数据库、CDD数据库、CAN一致性测试、ECU刷写测试

每日直播时间&#xff1a;&#xff08;直播方式&#xff1a;腾讯会议&#xff09;周一到周五&#xff1a;20&#xff1a;00-23&#xff1a;00周六与周日&#xff1a;9&#xff1a;00-17&#xff1a;00 进腾讯会议学习的&#xff0c;可以关注我并后台留言 直播内容&#xff1a;&…

Haproxy讲解

Haproxy: haproxy是一个开源的高性能反向代理和负载均衡器&#xff0c;主要用于‌TCP和‌HTTP流量管理。 功能和特点&#xff1a;haproxy能够处理大量的并发连接&#xff0c;支持TCP和HTTP协议&#xff0c;具有高可用性和负载均衡功能。它特别适用于需要处理大量流量的网站&am…

【Python快速入门和实践009】Python高级编程

9. 高级Python 9.1 迭代器与生成器 迭代器: 迭代器是一种遵循迭代器协议的对象&#xff0c;该协议要求对象实现两个特殊的方法&#xff1a;__iter__() 和 __next__()。__iter__() 方法返回迭代器自身&#xff0c;而 __next__() 方法返回序列中的下一个值。当没有更多的值时&a…

winform中设置DateTimePicker参数为空

在C#中&#xff0c;使用DateTimePicker控件时&#xff0c;您可以将其Value属性设置为null或者DateTime.MinValue来表示没有选定的日期或时间。以下是如何设置默认值为空的示例代码&#xff1a; dateTimePicker1.Value DateTime.MinValue; 或者&#xff0c;如果您希望用户不能…

NPM依赖管理:掌握自动更新行为的策略与实践

引言 在快速发展的JavaScript生态系统中&#xff0c;依赖包的持续更新对于保持项目现代化和安全性至关重要。NPM&#xff08;Node Package Manager&#xff09;作为Node.js的包管理器&#xff0c;提供了一套灵活的机制来管理依赖包的更新。本文将详细介绍如何使用NPM设置包的版…

HikariCP连接池:Possibly consider using a shorter maxLifetime value.

相关的SQL总结&#xff1a; session级别&#xff1a; show variables like %timeout%; mysql的global级别&#xff1a; show global variables like %timeout%; # 对应 mysql 修改配置&#xff08;单位 秒&#xff09; set global wait_timeout300; set global interacti…