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

文章目錄

    前言
    自定義擴展類
    Bean的生命周期
        說明
    流程說明
        1. Bean的實例化 (通過構(gòu)造方法進行實例化)
        2. InstantiationAwareBeanPostProcessor
        3. 設(shè)置屬性(依賴注入)
        4. 注入Aware接口
        5. BeanPostProcessor的postProcessBeforeInitialzation方法(前置處理 )
        6. InitializingBean與init-method(檢查自定義配置)
        7.BeanPostProcess的postProcessAfterInitialzation方法()
        8. Bean初始化結(jié)束
        9. DispostbleBean接口
    完整的執(zhí)行demo示例
    總結(jié)
    參考

前言

上一篇我們介紹了Spring IOC容器的啟動過程以及bean的實例化過程,這一篇我們接著來學習另外一個知識點,就是Bean的生命周期,我們知道直接通過(new XX())來創(chuàng)建的實例,當這個實例沒有被引用時就會被垃圾回收機制回收,但是通過IOC容器實例化的Bean的生命周期又是如何呢?IOC容器負責管理容器中所有的bean的生命周期,而在bean生命周期的不同階段,Spring提供了不同的擴展點來改變bean的命運。當然容器只能幫助我們管理單例模式bean的完整生命周期,對于property的bean,Spring在創(chuàng)建好交給使用者之后則不會在管理后續(xù)的生命周期。
自定義擴展類

如果我們要自定義一個擴展類,通常需要實現(xiàn)org.springframework.beans.factory.config.BeanFactoryPostProcessor接?,它是Spring對外提供的用來拓展Spring的接口,能夠在Spring容器加載了所有bean的信息之后、bean實例化之前執(zhí)行。他修改bean的定義屬性;其中PropertyResourceConfigurer就是BeanFactoryPostProcessor的典型應(yīng)用,PropertyResourceConfigurer可以將Xml文件中的占位符替換成屬性文件中相應(yīng)key對應(yīng)的value值。下面以常見的數(shù)據(jù)庫的連接配置舉例說明一下:

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>


屬性文件—mysqldb.properties

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

PropertyResourceConfigurer類的繼承結(jié)構(gòu)如下:

在這里插入圖片描述

上述占位符的例子只是BeanFactoryPostProcessor的應(yīng)用之一,但這是Spring提供的拓展,不是我們自定義的,在實際項目中,我們可以通過自定義BeanFactoryPostProcessor來實現(xiàn)敏感信息的解密處理,例如數(shù)據(jù)庫的連接配置中的,密碼我們可以配置成密文,這樣就可以防止泄密的風險。解密處理的操作實例如下所示:我們自定義了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("數(shù)據(jù)庫密碼錯誤", e);
		}
	}
	super.processProperties(beanFactoryToProcess, props);
}

}

Bean的生命周期

說完了自定義擴展類,下面讓我們來看看Bean的生命周期。首先,讓我們看一下容器的生命周期以及其管理的Bean的生命周期。圖中背景為綠色的部分就是Bean的生命周期。
在這里插入圖片描述

說明

Bean的完整生命周期經(jīng)歷了各種方法的調(diào)用,這些方法可以劃分為以下幾類:

Bean自身的方法:這個包括了Bean本身調(diào)用的方法和通過配置文件中<bean>的init-method和destroy-method指定的方法。
Bean級生命周期接口方法: 這個包括BeanNameAware,InitializingBean和DiposableBean這些接口的方法。
容器級生命周期接口方法:這個包括了BeanFactoryProcessor接口和InstantiationAwareBeanPostProcessor接口。
工廠后處理器接口方法:這個包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工廠后處理器接口的方法。工廠后處理器也是容器級的。在應(yīng)用上下文裝配配置文件之后立即調(diào)用。

流程說明

  1. Bean的實例化 (通過構(gòu)造方法進行實例化)

    Spring對Bean進行實例化(相當于 new XXX()),對于BeanFactory一般是延遲實例化,就是說調(diào)用getBean方法才會實例化,但是對于ApplicationContext,當容器初始化完成之后,就完成了所有Bean的實例化工作。實例化的對象被包裝在BeanWrapper對象中,BeanWrapper提供了設(shè)置對象屬性的接口,從而避免了使用反射機制設(shè)置屬性。

  2. InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor這個接口主要是幫助你在Bean實例化之前做一些操作。它繼承自 BeanPostProcessor接口,其中 postProcessBeforeInstantiation()方法是在目標對象實例化之前調(diào)用的方法,可以返回目標實例的一個代理用來代替目標實例。postProcessPropertyValues方法是在屬性值被設(shè)置到目標實例之前調(diào)用,可以修改屬性的設(shè)值。
3. 設(shè)置屬性(依賴注入)

實例化后的對象被封裝到BeanWrapper對象中,并且此時對象是一個原生狀態(tài),并沒有執(zhí)行依賴注入。緊接著,Spring根據(jù)BeanDefinition中的信息進行依賴注入。并且通過BeanWrapper提供的設(shè)置屬性的接口完成依賴注入。也就是說第二步就是通過Setter方法設(shè)置對象的屬性。
4. 注入Aware接口

Spring 會檢測該對象是否實現(xiàn)了Aware接口,如果實現(xiàn)了,則通過BeanNameAware的setBeanName方法設(shè)置對象名,通過BeanClassLoaderAware的setBeanClassLoader設(shè)置對象的加載器,通過BeanFactoryAware設(shè)置setBeanFactory設(shè)置BeanFactory接口的實現(xiàn)類是AbstractAutowireCapableBeanFactory。
各種各樣的Aware接口,其作用就是在對象實例化完成后將Aware接口定義中規(guī)定的依賴注入到當前實例中。比較常見的ApplicationContextAware接口,實現(xiàn)了這個接口的類都可以獲取到一個ApplicationContext對象,當容器中每個對象的實例化過程走到BeanPostProcessor前置處理這一步時,容器會檢測到之前注冊到容器的ApplicationContextAwareProcessor,然后就會調(diào)用其postProcessorBeforeInitialization()方法,檢查并設(shè)置Aware相關(guān)的依賴。示例如下:

@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配置成功========");
}
}

  1. BeanPostProcessor的postProcessBeforeInitialzation方法(前置處理 )

經(jīng)過上述步驟后,Bean對象已經(jīng)被正確構(gòu)造了,如果你想要對象被使用之前在進行自定義的處理,可以通過BeanPostProcessor接口實現(xiàn)。該接口提供了兩個方法
其中postProcessBeforeInitialzation( Object bean, String beanName ) 方法;當前正在初始化的bean對象會被傳遞進來,我們就可以對這個Bean做任何處理,這個方法會先于InitializingBean執(zhí)行,因此稱為前置處理。
6. InitializingBean與init-method(檢查自定義配置)

如果Bean實現(xiàn)了InitializingBean接口,Spring將調(diào)用它們的afterPropertiesSet方法,作用與在配置文件中對Bean使用init-method聲明初始化的作用一樣,都是在Bean的全部屬性設(shè)置成功后執(zhí)行的初始化方法。afterPropertiesSet 方法與前置處理不同的是,由于其沒有把Bean對象傳進來,因此在這一步?jīng)]有辦法處理對象本身,只能增加一些額外的邏輯。
7.BeanPostProcess的postProcessAfterInitialzation方法()

BeanPostProcess的postProcessAfterInitialzation(Object bean, String beanName ) 方法;當前正在初始化的bean對象會被傳遞進來,我們就可以對這個bean做任何處理。這個函數(shù)會在InitializingBean完成后執(zhí)行,因此稱為后置處理。
8. Bean初始化結(jié)束

經(jīng)過以上的工作以后,Bean的初始化就結(jié)束了,Bean將一直駐留在應(yīng)用上下文中給應(yīng)用使用,知道應(yīng)用上下文被銷毀。
9. DispostbleBean接口

如果Bean實現(xiàn)了DispostbleBean接口,Spring將調(diào)用它的destroy方法,作用與在配置文件中對Bean使用destroy-method屬性的作用是一樣的,都是在Bean實例銷毀前執(zhí)行的方法。
完整的執(zhí)行demo示例
在這里插入圖片描述

總結(jié)

本文首先介紹了如何自定義擴展類,對 BeanFactoryProcessor接口的作用做了詳細闡述,并介紹了其實現(xiàn)類 PropertyResourceConfigurer,這個實現(xiàn)類的作用就是講占位符替換成屬性文件中對應(yīng)的屬性值,緊接著就是介紹了如果自定義擴展類,通過數(shù)據(jù)庫連接密碼解密為例說明。第二部分就是介紹了Spring容器管理的Bean的完整生命周期,如何在面試中回答好Spring中Bean只需要注重如下幾個要點,生命周期內(nèi)要干啥?怎么做的?
要干啥就是:
通過構(gòu)造器實例化Bean,然后通過Setter方法來設(shè)置Bean的屬性,那這些做完了,
如果我們要對Bean進行修改該怎么辦呢?這就涉及到鉤子函數(shù),前置處理和后置處理。
另外我如果要獲取Bean的實例該怎么獲取呢?例如獲取ApplicationContext的實例!那么我們可以通過xxxAware方法,當然需要這個Bean繼承 了Aware接口。
最后就是Bean的銷毀過程,Bean是通過DispostbleBean接口的destroy方法進行銷毀的。
希望本文對讀者朋友們有所幫助。
參考

Spring Bean的生命周期(非常詳細)





作者:碼農(nóng)飛哥
微信公眾號:碼農(nóng)飛哥