最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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í)行之前,其實還有一個 resolveBeforeInstantiation 方法會先執(zhí)行,而這個方法可能就直接產(chǎn)生一個 Bean 了!如果這個方法直接產(chǎn)生一個 Bean 了,那么 doCreateBean 方法中的邏輯就不會生效了。

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

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

1. resolveBeforeInstantiation

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

  • instantiation:實例化,從 Class 到 Bean 就是實例化。
  • 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 方法則是判斷當前是否存在 InstantiationAwareBeanPostProcessor 類型的后置處理器,如果存在,則進入到 if 分支中。

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

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

  • applyBeanPostProcessorsBeforeInstantiation:從名字可以看出來,這個是在實例化之前觸發(fā)的方法,所以這個方法的參數(shù)還是 Class,因為還未實例化。
  • applyBeanPostProcessorsAfterInitialization:從名字可以看出來,這個是在初始化之后出發(fā)的方法,所以這個方法的參數(shù)是 Bean,因為此時已經(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 方法。

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

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 其實就是前面 postProcessBeforeInstantiation 方法生成的代理對象,然后這里調(diào)用父類方法去返回,實際上就是把參數(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 方法實際上并未執(zhí)行。

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

2. 源碼實踐

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

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

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

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

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

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

具體識別方法如下:

首先調(diào)用 AnnotationAwareAspectJAutoProxyCreator 的 postProcessBeforeInstantiation 方法(實際上是 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 是怎么處理的。

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

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

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

  • 調(diào)用父類的方法去判斷當前類是否和 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 方法去判斷當前類是否包含 @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);
}

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

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

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

2.2 普通 Bean

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

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

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

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

相關(guān)文章

  • java對象轉(zhuǎn)換String類型的三種方法

    java對象轉(zhuǎn)換String類型的三種方法

    在很多情況下我們都需要將一個對象轉(zhuǎn)換為String類型。一般來說有三種方法可以實現(xiàn):Object.toString()、(String)Object、String.valueOf(Object)。下面對這三種方法一一分析
    2013-11-11
  • spring?boot?2.x靜態(tài)資源會被攔截器攔截的原因分析及解決

    spring?boot?2.x靜態(tài)資源會被攔截器攔截的原因分析及解決

    這篇文章主要介紹了spring?boot?2.x靜態(tài)資源會被攔截器攔截的原因分析及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Java模擬新浪微博登陸抓取數(shù)據(jù)

    Java模擬新浪微博登陸抓取數(shù)據(jù)

    本文主要介紹了Java模擬新浪微博登陸抓取數(shù)據(jù)的實現(xiàn)方法。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • Java?多線程并發(fā)LockSupport

    Java?多線程并發(fā)LockSupport

    這篇文章主要介紹了Java?多線程并發(fā)LockSupport,LockSupport?類是用于創(chuàng)建鎖和其他同步類的基本線程阻塞原語,更多相關(guān)內(nèi)容需要得小伙伴可以參考一下下面文章內(nèi)容
    2022-06-06
  • 詳解Servlet入門級設(shè)置(超詳細 IDEA2020版)

    詳解Servlet入門級設(shè)置(超詳細 IDEA2020版)

    這篇文章主要介紹了詳解Servlet入門級設(shè)置(超詳細 IDEA2020版),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • Mybatis查詢語句返回對象和泛型集合的操作

    Mybatis查詢語句返回對象和泛型集合的操作

    這篇文章主要介紹了Mybatis查詢語句返回對象和泛型集合的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 最新評論

    普格县| 阿城市| 乌什县| 上思县| 景德镇市| 新宾| 威宁| 旬阳县| 定远县| 兴国县| 巴林右旗| 项城市| 阿拉善右旗| 大关县| 义马市| 卓资县| 伊春市| 潢川县| 钦州市| 马关县| 田林县| 察隅县| 泰兴市| 临邑县| 沙洋县| 石阡县| 阳春市| 常德市| 潜江市| 兰溪市| 阳曲县| 临高县| 贵德县| 麦盖提县| 湾仔区| 黄骅市| 辛集市| 达孜县| 营口市| 罗定市| 关岭|