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


寫在前面

在《【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注解驅動開發不再迷茫。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM