【Spring註解驅動開發】BeanPostProcessor在Spring底層是如何使用的?看完這篇我懂了!!

冰河團隊 發表於 2020-06-30

寫在前面

在《【String註解驅動開發】面試官再問你BeanPostProcessor的執行流程,就把這篇文章甩給他!》一文中,我們詳細的介紹了BeanPostProcessor的執行流程。那麼,BeanPostProcessor在Spring底層是如何使用的?今天,我們就一起來探討下Spring的原始碼,一探BeanPostProcessor在Spring底層的使用情況。

專案工程原始碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

BeanPostProcessor介面

我們先來看下BeanPostProcessor介面的原始碼,如下所示。

package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;

public interface BeanPostProcessor {
    
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

可以看到,在BeanPostProcessor介面中,提供了兩個方法:postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法。postProcessBeforeInitialization()方法會在bean初始化之前呼叫,postProcessAfterInitialization()方法會在bean初始化之後呼叫。接下來,我們就分析下BeanPostProcessor介面在Spring中的實現。

注意:這裡,我列舉幾個BeanPostProcessor介面在Spring中的實現類,來讓大家更加清晰的理解BeanPostProcessor介面在Spring底層的應用。

ApplicationContextAwareProcessor類

org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor介面的實現類,這個類的作用是可以向元件中注入IOC容器,大致的原始碼如下所示。

package org.springframework.context.support;

import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.EmbeddedValueResolver;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.lang.Nullable;
import org.springframework.util.StringValueResolver;
class ApplicationContextAwareProcessor implements BeanPostProcessor {
    /****************************省略N多行程式碼************************/
}

這裡,省略了原始碼的細節,只給出了類結構,感興趣的小夥伴們可自行翻閱Spring原始碼進行檢視,我這裡的Spring版本為5.2.6.RELEASE。

那具體如何使用ApplicationContextAwareProcessor類向元件中注入IOC容器呢?別急,我用一個例子來說明下,相信小夥伴們看完後會有一種豁然開朗的感覺——哦,原來是它啊,我之前在專案中使用過的!

要想使用ApplicationContextAwareProcessor類向元件中注入IOC容器,我們就不得不提Spring中的另一個介面:ApplicationContextAware,如果需要向元件中注入IOC容器,可以使元件實現ApplicationContextAware介面。

例如,我們建立一個Employee類,使其實現ApplicationContextAware介面,此時,我們需要實現ApplicationContextAware介面的setApplicationContext()方法,在setApplicationContext()方法中有一個ApplicationContext型別的引數,這個就是IOC容器物件,我們可以在Employee類中定義一個ApplicationContext型別的成員變數,然後在setApplicationContext()方法中為這個成員變數賦值,此時就可以在Employee中的其他方法中使用ApplicationContext物件了,如下所示。

package io.mykit.spring.plugins.register.bean;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試ApplicationContextAware
 */
@Component
public class Employee implements ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

看到這裡,相信不少小夥伴們都有一種很熟悉的感覺:沒錯,我之前也在專案中使用過!是的,這就是BeanPostProcessor在Spring底層的一種使用場景。至於上面的案例程式碼為何會在setApplicationContext()方法中獲取到ApplicationContext物件,這就是ApplicationContextAwareProcessor類的功勞了!

接下來,我們就深入分析下ApplicationContextAwareProcessor類。

我們先來看下ApplicationContextAwareProcessor類中對於postProcessBeforeInitialization()方法的實現,如下所示。

@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
          bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
          bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
        return bean;
    }

    AccessControlContext acc = null;

    if (System.getSecurityManager() != null) {
        acc = this.applicationContext.getBeanFactory().getAccessControlContext();
    }

    if (acc != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareInterfaces(bean);
            return null;
        }, acc);
    }
    else {
        invokeAwareInterfaces(bean);
    }

    return bean;
}

在bean初始化之前,首先對當前bean的型別進行判斷,如果當前bean的型別不是EnvironmentAware,不是EmbeddedValueResolverAware,不是ResourceLoaderAware,不是ApplicationEventPublisherAware,不是MessageSourceAware,也不是ApplicationContextAware,則直接返回bean。如果是上面型別中的一種型別,則最終會呼叫invokeAwareInterfaces()方法,並將bean傳遞給invokeAwareInterfaces()方法。invokeAwareInterfaces()方法又是個什麼鬼呢?我們繼續看invokeAwareInterfaces()方法的原始碼,如下所示。

private void invokeAwareInterfaces(Object bean) {
    if (bean instanceof EnvironmentAware) {
        ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
    }
    if (bean instanceof EmbeddedValueResolverAware) {
        ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
    }
    if (bean instanceof ResourceLoaderAware) {
        ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
    }
    if (bean instanceof ApplicationEventPublisherAware) {
        ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
    }
    if (bean instanceof MessageSourceAware) {
        ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
    }
    if (bean instanceof ApplicationContextAware) {
        ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
    }
}

可以看到invokeAwareInterfaces()方法的原始碼比較簡單,就是判斷當前bean屬於哪種介面型別,則將bean強轉為哪種介面型別的物件,然後呼叫介面的方法,將相應的引數傳遞到介面的方法中。這裡,我們在建立Employee類時,實現的是ApplicationContextAware介面,所以,在invokeAwareInterfaces()方法中,會執行如下的邏輯程式碼。

if (bean instanceof ApplicationContextAware) {
    ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}

我們可以看到,此時會將this.applicationContext傳遞到ApplicationContextAware介面的setApplicationContext()方法中。所以,我們在Employee類中的setApplicationContext()方法中就可以直接接收到ApplicationContext物件了。

我們也可以在IDEA中通過Debug的形式來看一下程式的執行過程,此時我們在Employee類的setApplicationContext()方法上設定斷點,如下所示。

在這裡插入圖片描述

接下來,我們以Debug的方式來執行SpringBeanTest類的testAnnotationConfig2()方法,執行後的效果如下圖所示。

在這裡插入圖片描述

在IDEA的左下角可以看到方法的呼叫堆疊,通過對方法呼叫棧的分析,我們看到在執行Employee類中的setApplicationContext()方法之前,執行了ApplicationContextAwareProcessor類的invokeAwareInterfaces方法,如下所示。

在這裡插入圖片描述

當我們點選方法呼叫棧中的invokeAwareInterfaces()方法時,程式碼的執行定位到如下一行程式碼。

((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);

和我們之前分析的邏輯一致。

BeanValidationPostProcessor類

org.springframework.validation.beanvalidation.BeanValidationPostProcessor類主要是用來為bean進行校驗操作,當我們建立bean,併為bean賦值後,我們可以通過BeanValidationPostProcessor類為bean進行校驗操作。BeanValidationPostProcessor類的結構如下所示。

package org.springframework.validation.beanvalidation;

import java.util.Iterator;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean {
    /*******************************省略N行程式碼**********************************/
}

這裡,我們也來看看postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法的實現,如下所示。

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (!this.afterInitialization) {
        doValidate(bean);
    }
    return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (this.afterInitialization) {
        doValidate(bean);
    }
    return bean;
}

可以看到,在postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法中的主要邏輯都是呼叫doValidate()方法對bean進行校驗,只不過在兩個方法中都會對afterInitialization這個boolean型別的成員變數進行判斷,如果afterInitialization的值為false,則在postProcessBeforeInitialization()方法中呼叫doValidate()方法對bean進行校驗;如果afterInitialization的值為true,則在postProcessAfterInitialization()方法中呼叫doValidate()方法對bean進行校驗。

InitDestroyAnnotationBeanPostProcessor類

org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor類主要用來處理@PostConstruct註解和@PreDestroy註解。

例如,我們之前建立的Cat類中就使用了@PostConstruct註解和@PreDestroy註解,如下所示。

package io.mykit.spring.plugins.register.bean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
 * @author binghe
 * @version 1.0.0
 * @description 測試@PostConstruct註解和@PreDestroy註解
 */
public class Cat {

    public Cat(){
        System.out.println("Cat類的構造方法...");
    }

    public void init(){
        System.out.println("Cat的init()方法...");
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("Cat的postConstruct()方法...");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("Cat的preDestroy()方法...");
    }

    public void destroy(){
        System.out.println("Cat的destroy()方法...");
    }
}

那麼,在Cat類中使用了 @PostConstruct註解和@PreDestroy註解來標註方法,Spring怎麼就知道什麼時候執行 @PostConstruct註解標註的方法,什麼時候執行@PreDestroy標註的方法呢?這就要歸功於InitDestroyAnnotationBeanPostProcessor類的實現了。

接下來,我們也通過Debug的方式來跟進下程式碼的執行流程。首先,在Cat類的postConstruct()方法上打上斷點,如下所示。

在這裡插入圖片描述

接下來,我們以Debug的方式執行BeanLifeCircleTest類的testBeanLifeCircle04()方法,效果如下所示。

在這裡插入圖片描述

我們還是帶著問題來分析,Spring怎麼就能定位到使用@PostConstruct註解標註的方法呢?通過分析方法的呼叫棧我們發現了在進入使用@PostConstruct註解標註的方法之前,Spring呼叫了InitDestroyAnnotationBeanPostProcessor類的postProcessBeforeInitialization()方法,如下所示。
在這裡插入圖片描述
在InitDestroyAnnotationBeanPostProcessor類的postProcessBeforeInitialization()方法中,首先會找到bean中有關生命週期的註解,比如@PostConstruct註解等,找到這些註解之後,則將這些資訊賦值給LifecycleMetadata型別的變數metadata,之後呼叫metadata的invokeInitMethods()方法,通過反射來呼叫標註了@PostConstruct註解的方法。這就是為什麼標註了@PostConstruct註解的方法被Spring執行。

AutowiredAnnotationBeanPostProcessor類

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor類主要是用於處理標註了@Autowired註解的變數或方法。

Spring為何能夠自動處理標註了@Autowired註解的變數或方法,就交給小夥伴們自行分析了。大家可以寫一個測試方法並通過方法呼叫堆疊來分析AutowiredAnnotationBeanPostProcessor類的原始碼,從而找到自己想要的答案。

好了,我們們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!

專案工程原始碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation

寫在最後

如果覺得文章對你有點幫助,請微信搜尋並關注「 冰河技術 」微信公眾號,跟冰河學習Spring註解驅動開發。公眾號回覆“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。