看了这一篇Spring容器Bean的生命周期,面试再也不用怕了

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 上一篇我们介绍了Spring IOC容器的启动过程以及bean的实例化过程,这一篇我们接着来学习另外一个知识点,就是Bean的生命周期,我们知道直接通过(new XX())来创建的实例,当这个实例没有被引用时就会被垃圾回收机制回收,但是通过IOC容器实例化的Bean的生命周期又是如何呢?

前言

上一篇我们介绍了Spring IOC容器的启动过程以及bean的实例化过程,这一篇我们接着来学习另外一个知识点,就是Bean的生命周期,我们知道直接通过(new XX())来创建的实例,当这个实例没有被引用时就会被垃圾回收机制回收,但是通过IOC容器实例化的Bean的生命周期又是如何呢?IOC容器负责管理容器中所有的bean的生命周期,而在bean生命周期的不同阶段,Spring提供了不同的扩展点来改变bean的命运。当然容器只能帮助我们管理单例模式bean的完整生命周期,对于property的bean,Spring在创建好交给使用者之后则不会在管理后续的生命周期。

自定义扩展类

如果我们要自定义一个扩展类,通常需要实现org.springframework.beans.factory.config.BeanFactoryPostProcessor接⼝,它是Spring对外提供的用来拓展Spring的接口,能够在Spring容器加载了所有bean的信息之后、bean实例化之前执行。他修改bean的定义属性;其中PropertyResourceConfigurer就是BeanFactoryPostProcessor的典型应用,PropertyResourceConfigurer可以将Xml文件中的占位符替换成属性文件中相应key对应的value值。下面以常见的数据库的连接配置举例说明一下:


1.Spring的配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
        <list>
            <value>classpath:mysqldb.properties</value>
        </list>
        </property>
    </bean>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName"value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}"/>
        <property name="password"value="${jdbc.password}" />
    </bean>
</beans>

2.属性文件—mysqldb.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis
jdbc.username=root
jdbc.password=root

3.PropertyResourceConfigurer类的继承结构如下:

b7b8999f6d8f69ed7edf84e373a790a2_20200503073255466.jpg

上述占位符的例子只是BeanFactoryPostProcessor的应用之一,但这是Spring提供的拓展,不是我们自定义的,在实际项目中,我们可以通过自定义BeanFactoryPostProcessor来实现敏感信息的解密处理,例如数据库的连接配置中的,密码我们可以配置成密文,这样就可以防止泄密的风险。解密处理的操作实例如下所示:我们自定义了AESPropertyPlaceholderConfigurer类继承了PropertyPlaceholderConfigurer类,然后重写processProperties方法。

public class AESPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
  @Override
  protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
    throws BeansException {
  String key = "jdbc.password";
  String aeskey = "0123456789012345";
  Object password = props.get(key);
  if (password != null) {
    try {
    props.put(key, AESUtil.decrypt(password.toString(), aeskey));
    } catch (Exception e) {
    log.warn("数据库密码错误", e);
    }
  }
  super.processProperties(beanFactoryToProcess, props);
  }
}

Bean的生命周期

说完了自定义扩展类,下面让我们来看看Bean的生命周期。首先,让我们看一下容器的生命周期以及其管理的Bean的生命周期。图中背景为绿色的部分就是Bean的生命周期。

d3ccf302fad6cb0ca0acb3e6177c8cbe_watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ1MzQ4MDg=,size_16,color_FFFFFF,t_70.png

说明

Bean的完整生命周期经历了各种方法的调用,这些方法可以划分为以下几类:


Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中<bean>的init-method和destroy-method指定的方法。

Bean级生命周期接口方法: 这个包括BeanNameAware,InitializingBean和DiposableBean这些接口的方法。

容器级生命周期接口方法:这个包括了BeanFactoryProcessor接口和InstantiationAwareBeanPostProcessor接口。

工厂后处理器接口方法:这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。

流程说明

1. Bean的实例化 (通过构造方法进行实例化)

Spring对Bean进行实例化(相当于 new XXX()),对于BeanFactory一般是延迟实例化,就是说调用getBean方法才会实例化,但是对于ApplicationContext,当容器初始化完成之后,就完成了所有Bean的实例化工作。实例化的对象被包装在BeanWrapper对象中,BeanWrapper提供了设置对象属性的接口,从而避免了使用反射机制设置属性。

2. InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor这个接口主要是帮助你在Bean实例化之前做一些操作。它继承自 BeanPostProcessor接口,其中 postProcessBeforeInstantiation()方法是在目标对象实例化之前调用的方法,可以返回目标实例的一个代理用来代替目标实例。postProcessPropertyValues方法是在属性值被设置到目标实例之前调用,可以修改属性的设值。

3. 设置属性(依赖注入)

实例化后的对象被封装到BeanWrapper对象中,并且此时对象是一个原生状态,并没有执行依赖注入。紧接着,Spring根据BeanDefinition中的信息进行依赖注入。并且通过BeanWrapper提供的设置属性的接口完成依赖注入。也就是说第二步就是通过Setter方法设置对象的属性。

4. 注入Aware接口

Spring 会检测该对象是否实现了Aware接口,如果实现了,则通过BeanNameAware的setBeanName方法设置对象名,通过BeanClassLoaderAware的setBeanClassLoader设置对象的加载器,通过BeanFactoryAware设置setBeanFactory设置BeanFactory接口的实现类是AbstractAutowireCapableBeanFactory。

各种各样的Aware接口,其作用就是在对象实例化完成后将Aware接口定义中规定的依赖注入到当前实例中。比较常见的ApplicationContextAware接口,实现了这个接口的类都可以获取到一个ApplicationContext对象,当容器中每个对象的实例化过程走到BeanPostProcessor前置处理这一步时,容器会检测到之前注册到容器的ApplicationContextAwareProcessor,然后就会调用其postProcessorBeforeInitialization()方法,检查并设置Aware相关的依赖。示例如下:

@Component
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtils.applicationContext = applicationContext;
        System.out.println("========ApplicationContext配置成功========");
    }
  }

5. BeanPostProcessor的postProcessBeforeInitialzation方法(前置处理 )

经过上述步骤后,Bean对象已经被正确构造了,如果你想要对象被使用之前在进行自定义的处理,可以通过BeanPostProcessor接口实现。该接口提供了两个方法

其中postProcessBeforeInitialzation( Object bean, String beanName ) 方法;当前正在初始化的bean对象会被传递进来,我们就可以对这个Bean做任何处理,这个方法会先于InitializingBean执行,因此称为前置处理。

6. InitializingBean与init-method(检查自定义配置)

如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法。afterPropertiesSet 方法与前置处理不同的是,由于其没有把Bean对象传进来,因此在这一步没有办法处理对象本身,只能增加一些额外的逻辑。

7.BeanPostProcess的postProcessAfterInitialzation方法()

BeanPostProcess的postProcessAfterInitialzation(Object bean, String beanName ) 方法;当前正在初始化的bean对象会被传递进来,我们就可以对这个bean做任何处理。这个函数会在InitializingBean完成后执行,因此称为后置处理。

8. Bean初始化结束

经过以上的工作以后,Bean的初始化就结束了,Bean将一直驻留在应用上下文中给应用使用,知道应用上下文被销毁。

9. DispostbleBean接口

如果Bean实现了DispostbleBean接口,Spring将调用它的destroy方法,作用与在配置文件中对Bean使用destroy-method属性的作用是一样的,都是在Bean实例销毁前执行的方法。

完整的执行demo示例

总结

本文首先介绍了如何自定义扩展类,对 BeanFactoryProcessor接口的作用做了详细阐述,并介绍了其实现类 PropertyResourceConfigurer,这个实现类的作用就是讲占位符替换成属性文件中对应的属性值,紧接着就是介绍了如果自定义扩展类,通过数据库连接密码解密为例说明。第二部分就是介绍了Spring容器管理的Bean的完整生命周期,如何在面试中回答好Spring中Bean只需要注重如下几个要点,生命周期内要干啥?怎么做的?

要干啥就是:

通过构造器实例化Bean,然后通过Setter方法来设置Bean的属性,那这些做完了,

如果我们要对Bean进行修改该怎么办呢?这就涉及到钩子函数,前置处理和后置处理。

另外我如果要获取Bean的实例该怎么获取呢?例如获取ApplicationContext的实例!那么我们可以通过xxxAware方法,当然需要这个Bean继承 了Aware接口。

最后就是Bean的销毁过程,Bean是通过DispostbleBean接口的destroy方法进行销毁的。

希望本文对读者朋友们有所帮助。

参考

Spring Bean的生命周期(非常详细)


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
24天前
|
XML 安全 Java
|
6天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
34 6
|
8天前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
53 3
|
22天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
41 2
|
25天前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
22天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
32 1
|
1天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
1天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
1月前
|
前端开发 Java Docker
使用Docker容器化部署Spring Boot应用程序
使用Docker容器化部署Spring Boot应用程序
|
1月前
|
Java Docker 微服务
利用Docker容器化部署Spring Boot应用
利用Docker容器化部署Spring Boot应用
51 0