欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring?Bean創(chuàng)建的另一條捷徑

 更新時間:2023年08月01日 15:00:51   作者:江南一點(diǎn)雨  
這篇文章主要為大家介紹了Spring?Bean創(chuàng)建的另一條方法捷徑詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

Spring Bean創(chuàng)建方法

有如下一段代碼:

AbstractAutowireCapableBeanFactory#createBean:

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
        throws BeanCreationException {
    //...
    try {
        // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
            return bean;
        }
    }
    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        return beanInstance;
    }
    //...
}

我們平時說的 Bean 的創(chuàng)建邏輯都是指 doCreateBean 方法中的邏輯,在松哥前面幾篇文章中,凡是涉及到 Bean 的創(chuàng)建流程的,我說的也都是 doCreateBean 方法的流程。

但是小伙伴們注意,在 doCreateBean 方法執(zhí)行之前,其實(shí)還有一個 resolveBeforeInstantiation 方法會先執(zhí)行,而這個方法可能就直接產(chǎn)生一個 Bean 了!如果這個方法直接產(chǎn)生一個 Bean 了,那么 doCreateBean 方法中的邏輯就不會生效了。

那么 resolveBeforeInstantiation 方法存在的意義是什么呢?其實(shí)大家從該方法的注釋上大概也能看出一些端倪出來了:

給 BeanPostProcessor 一個機(jī)會去創(chuàng)建一個代理對象,用這個代理對象來代替目標(biāo) Bean。

1. resolveBeforeInstantiation

看下面的源碼小伙伴們一定要先搞清楚兩個比較相似的單詞,否則看到后面就亂了:

  • instantiation:實(shí)例化,從 Class 到 Bean 就是實(shí)例化。
  • initialization:初始化,給 Bean 做各種配置就是初始化。

搞明白這兩個單詞,我們來看源碼。

首先我先來和小伙伴們稍微梳理一下 resolveBeforeInstantiation 方法。

AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation:

@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}

小伙伴們看一下,這里有一個判斷條件,mbd.isSynthetic 是判斷是否是一個合成 Bean,這種一般都是 Spring 定義的,我們自定義的 Bean 一般都不屬于這一類,然后后面的 hasInstantiationAwareBeanPostProcessors 方法則是判斷當(dāng)前是否存在 InstantiationAwareBeanPostProcessor 類型的后置處理器,如果存在,則進(jìn)入到 if 分支中。

如果我們想要在 resolveBeforeInstantiation 方法中就完成 Bean 的處理,那么就需要自己提供一個 InstantiationAwareBeanPostProcessor 類型的后置處理器。

接下來會調(diào)用兩個方法:

  • applyBeanPostProcessorsBeforeInstantiation:從名字可以看出來,這個是在實(shí)例化之前觸發(fā)的方法,所以這個方法的參數(shù)還是 Class,因?yàn)檫€未實(shí)例化。
  • applyBeanPostProcessorsAfterInitialization:從名字可以看出來,這個是在初始化之后出發(fā)的方法,所以這個方法的參數(shù)是 Bean,因?yàn)榇藭r已經(jīng)完成了初始化了。

1.1 applyBeanPostProcessorsBeforeInstantiation

@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
        Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
        if (result != null) {
            return result;
        }
    }
    return null;
}

這個地方就很簡單了,就是執(zhí)行 InstantiationAwareBeanPostProcessor 類型的后置處理器的 postProcessBeforeInstantiation 方法。

1.2 applyBeanPostProcessorsAfterInitialization

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

這個是執(zhí)行 BeanPostProcessor 的 postProcessAfterInitialization 方法。

所以這塊的源碼其實(shí)并不難,道理很簡單。

1.3 案例

松哥寫一個簡單的案例小伙伴們來看下。

假設(shè)我有一個 BookService,如下:

public class BookService {
    public void hello() {
        System.out.println("hello javaboy");
    }
}

然后我再創(chuàng)建一個 InstantiationAwareBeanPostProcessor 類型的后置處理器,并且重寫前面提到的 postProcessBeforeInstantiation 和 postProcessAfterInitialization 方法:

public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass == BookService.class) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(beanClass);
            enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
                String name = method.getName();
                System.out.println(name + " 方法開始執(zhí)行了...");
                Object invoke = proxy.invokeSuper(obj, args);
                System.out.println(name + " 方法執(zhí)行結(jié)束了...");
                return invoke;
            });
            BookService bookService = (BookService) enhancer.create();
            return bookService;
        }
        return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean.getClass() ========= " + bean.getClass());
        return InstantiationAwareBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}

在 postProcessBeforeInstantiation 方法中,如果要創(chuàng)建的 Bean 是 BookService,則這里通過 Enhancer 來創(chuàng)建一個 CGLIB 的代理對象,如果是其他的 Bean 的創(chuàng)建,則調(diào)用父類方法即可。這樣重寫之后,就會導(dǎo)致在 1.1 小節(jié)中,獲取到的 result 就是一個代理的 BookService 對象。

在 postProcessAfterInitialization 方法中,我未做任何額外處理,就是把拿到的 Bean 打印了一下,此時我們拿到手的 Bean 其實(shí)就是前面 postProcessBeforeInstantiation 方法生成的代理對象,然后這里調(diào)用父類方法去返回,實(shí)際上就是把參數(shù) Bean 原封不動返回。

最后將這兩個 Bean 注冊到 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean class="org.javaboy.bean.aop2.BookService" id="bookService"/>
    <bean class="org.javaboy.bean.aop2.MyInstantiationAwareBeanPostProcessor"
          id="myInstantiationAwareBeanPostProcessor"/>
</beans>

然后初始化 Spring 容器:

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("aop2.xml");
BookService bs = ctx.getBean(BookService.class);
System.out.println("bs.getClass() = " + bs.getClass());
bs.hello();

最終執(zhí)行結(jié)果如下:

可以看到,最終拿到的 BookService 就是一個代理對象,從源碼層面來講,這個代理對象在 resolveBeforeInstantiation 方法中就生成了,后續(xù)的 doCreateBean 方法實(shí)際上并未執(zhí)行。

這就是 resolveBeforeInstantiation 方法的作用,實(shí)際上就是給 BeanPostProcessor 一個機(jī)會去創(chuàng)建一個代理對象,用這個代理對象來代替目標(biāo) Bean。

2. 源碼實(shí)踐

松哥為什么會關(guān)注到這個方法呢?

如果有小伙伴研究過 Spring AOP 源碼,就會發(fā)現(xiàn)這個方法在處理 Spring AOP 的時候,有一個用武之地。

當(dāng)我們在 Spring AOP 中,往往通過如下代碼來定義切面:

@Component
@Aspect
@EnableAspectJAutoProxy
public class LogAspect {
    //...
}

這個類上面有一個 @Aspect 注解,那么問題來了,Spring 是如何識別出這是一個切面而非普通的 Bean 的?

答案就是在 1.1 小節(jié)中的 applyBeanPostProcessorsBeforeInstantiation 方法中,這個方法會遍歷所有 InstantiationAwareBeanPostProcessor 類型的后置處理器,InstantiationAwareBeanPostProcessor 有一個子類是 AnnotationAwareAspectJAutoProxyCreator,在這個處理器中,識別出來了 LogAspect 是一個切面。

具體識別方法如下:

首先調(diào)用 AnnotationAwareAspectJAutoProxyCreator 的 postProcessBeforeInstantiation 方法(實(shí)際上是 AnnotationAwareAspectJAutoProxyCreator 的父類 AbstractAutoProxyCreator 中的方法):

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);
    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }
    //...
}

從這個地方開始,分成了兩條線:

  • 如果是一個切面 Bean 的話,則執(zhí)行第一個方法 isInfrastructureClass 就可以返回 true 了。
  • 如果是一個普通 Bean 的話,則第一個方法會返回 false,此時就會執(zhí)行第二個方法 shouldSkip(雖然該方法也會返回 false),但是該方法有一些其他的價值在里邊。

2.1 切面 Bean

我們先來看 isInfrastructureClass 方法,先來看切面 Bean 是怎么處理的。

這個方法我摘了一部分出來,我們重點(diǎn)關(guān)注 isInfrastructureClass 方法,這個方法用來判斷當(dāng)前類是否是一個 Aspect:

@Override
protected boolean isInfrastructureClass(Class<?> beanClass) {
    return (super.isInfrastructureClass(beanClass) ||
            (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass)));
}

這里的判斷主要是兩方面:

  • 調(diào)用父類的方法去判斷當(dāng)前類是否和 AOP 相關(guān):
protected boolean isInfrastructureClass(Class<?> beanClass) {
    boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
            Pointcut.class.isAssignableFrom(beanClass) ||
            Advisor.class.isAssignableFrom(beanClass) ||
            AopInfrastructureBean.class.isAssignableFrom(beanClass);
    return retVal;
}

這個就不用我解釋了,這些都是我們在 AOP 中的老熟人了。

  • 調(diào)用 aspectJAdvisorFactory.isAspect 方法去判斷當(dāng)前類是否包含 @Aspect 注解:

AbstractAspectJAdvisorFactory#isAspect

@Override
public boolean isAspect(Class<?> clazz) {
    return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}
private boolean hasAspectAnnotation(Class<?> clazz) {
    return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null);
}

這代碼可太好理解了,就是檢查當(dāng)前類是否有 @Aspect 注解。后面那個 compiledByAjc 方法是檢查是否需要 ajc 編譯,這個我們一般都不需要,所以,只要 hasAspectAnnotation 方法返回 true,整體上就會返回 true。

如果我們的類上包含 @Aspect 注解,那么最終就會在將當(dāng)前類名加入到 advisedBeans Map 中,在 advisedBeans 這個 Map 中,key 是當(dāng)前 Bean 的名稱,value 則是 false 是一個標(biāo)記,表示當(dāng)前類不需要生成代理類。

這就是 isInfrastructureClass 方法執(zhí)行的大致邏輯。

2.2 普通 Bean

如果是普通 Bean 的話,很明顯 isInfrastructureClass 方法會返回 false,這就會導(dǎo)致 shouldSkip 方法去執(zhí)行,這個方法名雖然叫 shouldSkip,但是卻干了不少實(shí)事。

這個方法我會在下篇文章中和小伙伴們分享 AOP 的創(chuàng)建過程中再和大家詳解,這里先說一句,這個方法會把各種 Aspect Bean 都收集整理起來,將來根據(jù)這些 Bean 去生成 Advisor。

好啦,這就是 resolveBeforeInstantiation 方法的作用,感興趣的小伙伴可以自己 DEBUG 看一些哦~

以上就是Spring Bean創(chuàng)建的另一條捷徑的詳細(xì)內(nèi)容,更多關(guān)于Spring Bean創(chuàng)建的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • springboot與springmvc基礎(chǔ)入門講解

    springboot與springmvc基礎(chǔ)入門講解

    本篇文章主要介紹了詳解快速搭建Spring Boot+Spring MVC,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-07-07
  • spring快速入門實(shí)例教程

    spring快速入門實(shí)例教程

    這篇文章主要介紹了spring快速入門實(shí)例,主要分析了spring的基本配置與控制反轉(zhuǎn),對于spring的學(xué)習(xí)具有一定的參考借鑒價值,需要的朋友可以參考下
    2014-12-12
  • Java虛擬機(jī)運(yùn)行時棧的棧幀

    Java虛擬機(jī)運(yùn)行時棧的棧幀

    本節(jié)將會介紹一下Java虛擬機(jī)棧中的棧幀,會對棧幀的組成部分(局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口)分別進(jìn)行介紹,最后還會通過javap命令反解析編譯后的.class文件,進(jìn)行分析方法執(zhí)行時的局部變量表、操作數(shù)棧等
    2021-09-09
  • 淺談SpringMVC的執(zhí)行流程

    淺談SpringMVC的執(zhí)行流程

    下面小編就為大家?guī)硪黄獪\談SpringMVC的執(zhí)行流程。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Java中條件運(yùn)算符的嵌套使用技巧總結(jié)

    Java中條件運(yùn)算符的嵌套使用技巧總結(jié)

    在Java中,我們經(jīng)常需要使用條件運(yùn)算符來進(jìn)行多個條件的判斷和選擇,條件運(yùn)算符可以簡化代碼,提高代碼的可讀性和執(zhí)行效率,本文將介紹條件運(yùn)算符的嵌套使用技巧,幫助讀者更好地掌握條件運(yùn)算符的應(yīng)用,需要的朋友可以參考下
    2023-11-11
  • Mybatis(ParameterType)傳遞多個不同類型的參數(shù)方式

    Mybatis(ParameterType)傳遞多個不同類型的參數(shù)方式

    這篇文章主要介紹了Mybatis(ParameterType)傳遞多個不同類型的參數(shù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • JAVA實(shí)現(xiàn)經(jīng)典游戲坦克大戰(zhàn)的示例代碼

    JAVA實(shí)現(xiàn)經(jīng)典游戲坦克大戰(zhàn)的示例代碼

    小時候大家都玩過坦克大戰(zhàn)吧,熟悉的旋律和豐富的關(guān)卡陪伴了我們一整個寒暑假。本文將通過Java+Swing實(shí)現(xiàn)這一經(jīng)典游戲,感興趣的可以學(xué)習(xí)一下
    2022-01-01
  • IntelliJ IDEA 如何徹底刪除項(xiàng)目的步驟

    IntelliJ IDEA 如何徹底刪除項(xiàng)目的步驟

    本篇文章主要介紹了IntelliJ IDEA 如何徹底刪除項(xiàng)目的步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • 關(guān)于SpringMVC請求域?qū)ο蟮臄?shù)據(jù)共享問題

    關(guān)于SpringMVC請求域?qū)ο蟮臄?shù)據(jù)共享問題

    這篇文章主要介紹了SpringMVC請求域?qū)ο蟮臄?shù)據(jù)共享問題,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • 淺談Java生命周期管理機(jī)制

    淺談Java生命周期管理機(jī)制

    最近有位細(xì)心的朋友在閱讀筆者的文章時,對java類的生命周期問題有一些疑惑,筆者打開百度搜了一下相關(guān)的問題,看到網(wǎng)上的資料很少有把這個問題講明白的,主要是因?yàn)槟壳皣鴥?nèi)java方面的教材大多只是告訴你“怎樣做”,但至于“為什么這樣做”卻不多說
    2016-01-01

最新評論