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

Spring?Bean名稱不會被代理的命名技巧

 更新時間:2023年11月06日 11:51:04   作者:江南一點雨  
Spring Bean一些使用小細節(jié)就是在不斷的源碼探索中逐步發(fā)現(xiàn)的,今天就來和小伙伴們聊一下通過 beanName 的設置,可以讓一個 bean 拒絕被代理

1. 代碼實踐

假設我有如下一個切面:

@Aspect
@EnableAspectJAutoProxy
@Component
public class LogAspect {
    @Pointcut("execution(* org.javaboy.demo.service.*.*(..))")
    public void pc() {
    }
    @Before("pc()")
    public void before(JoinPoint jp) {
        String name = jp.getSignature().getName();
        System.out.println(name + " 方法開始執(zhí)行了...");
    }
}

這個切面要攔截的方法是 org.javaboy.demo.service 包下的所有類的所有方法,現(xiàn)在,這個包下有一個 BookService 類,內容如下:

@Service("org.javaboy.demo.service.BookService.ORIGINAL")
public class BookService {
    public void hello() {
        System.out.println("hello bs");
    }
}

這個 BookService 的 beanName 我沒有使用默認的 beanName,而是自己配置了一個 beanName,這個 beanName 的配置方式是 類名的完整路徑+.ORIGINAL。

當我們按照這樣的規(guī)則給 bean 取名之后,那么即使當前 bean 已經(jīng)包含在切點所定義的范圍內,這個 bean 也不會被代理了。

這是 Spring5.1 開始的新玩法。

這種寫法的原理是什么呢?

2. 原理分析

在 Spring 創(chuàng)建 Bean 的時候,小伙伴們都知道,bean 創(chuàng)建完成之后會去各種后置處理器(BeanPostProcessor)中走一圈,所以一般我們認為 BeanPostProcessor 是在 bean 實例創(chuàng)建完成之后執(zhí)行。但是,BeanPostProcessor 中有一個特例 InstantiationAwareBeanPostProcessor,這個接口繼承自 BeanPostProcessor,但是在 BeanPostProcessor 的基礎之上,增加了額外的能力:

  • 在 bean 實例化之前先做一些預處理,例如直接創(chuàng)建代理對象,代替后續(xù)的 bean 生成。
  • 在 bean 實例化之后但是屬性填充之前,可以自定義一些屬性注入策略。

大致上就是這兩方面的能力。

具體到代碼上,就是在創(chuàng)建 bean 的 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;
        }
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                "BeanPostProcessor before instantiation of bean failed", ex);
    }
    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    //省略。。。
}

小伙伴們看,這里的 resolveBeforeInstantiation 方法就是給 BeanPostProcessor 一個返回代理對象的機會,在這個方法中,最終就會觸發(fā)到 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 方法,我們來看看這里涉及到的跟 AOP 相關的 AbstractAutoProxyCreator#postProcessBeforeInstantiation 方法:

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    Object cacheKey = getCacheKey(beanClass, beanName);
    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
        if (this.advisedBeans.containsKey(cacheKey)) {
            return null;
        }
        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return null;
        }
    }
    // Create proxy here if we have a custom TargetSource.
    // Suppresses unnecessary default instantiation of the target bean:
    // The TargetSource will handle target instances in a custom fashion.
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    return null;
}

方法作用

這個方法實際上干了兩件事:

  • 檢查當前 bean 是否需要代理,如果不需要代理,那么就存入到一個 map 集合 advisedBeans 中,key 是 bean 的名字,value 如果為 true 則表示這個 bean 是需要代理的,value 為 false,則表示這個 bean 是不需要代理的。
  • 如果有我們有自定義的 TargetSource,則根據(jù)自定義的 TargetSource 去創(chuàng)建代理對象。

這里我要和大家說的是第一點。

在判斷一個 bean 是否需要代理的時候,主要依據(jù)兩個方法:

  • isInfrastructureClass:這個方法主要是檢查當前 bean 是否是 Advice/Advisor/Pointcut 等類型,或者這個類上是否有 @Aspect 注解,這個松哥在之前的文章中其實和大家介紹過了:聽說 Spring Bean 的創(chuàng)建還有一條捷徑?。
  • shouldSkip:如果 isInfrastructureClass 方法返回 false,那么就要執(zhí)行 shouldSkip 了,我們來仔細看下 shouldSkip 方法。
@Override
protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    // TODO: Consider optimization by caching the list of the aspect names
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    for (Advisor advisor : candidateAdvisors) {
        if (advisor instanceof AspectJPointcutAdvisor pointcutAdvisor &&
                pointcutAdvisor.getAspectName().equals(beanName)) {
            return true;
        }
    }
    return super.shouldSkip(beanClass, beanName);
}

這里首先找到系統(tǒng)中所有的切面,找到之后挨個遍歷,遍歷的時候判斷如果當前要創(chuàng)建的 bean 剛好就是切面,那切面肯定是不需要代理的,直接返回 true。否則就會去調用父類的 shouldSkip 方法,我們再來瞅一眼父類的 shouldSkip 方法:

protected boolean shouldSkip(Class<?> beanClass, String beanName) {
    return AutoProxyUtils.isOriginalInstance(beanName, beanClass);
}
static boolean isOriginalInstance(String beanName, Class<?> beanClass) {
    if (!StringUtils.hasLength(beanName) || beanName.length() !=
            beanClass.getName().length() + AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX.length()) {
        return false;
    }
    return (beanName.startsWith(beanClass.getName()) &&
            beanName.endsWith(AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX));
}

父類 shouldSkip 方法主要是調用了一個靜態(tài)工具方法 isOriginalInstance 來判斷當前 bean 是否是一個不需要代理的 bean,這個具體的判斷邏輯就是檢查這個 beanName 是否按照 類名的完整路徑+.ORIGINAL 的方式命名的,如果是則返回 true。

當 shouldSkip 方法返回 true 的時候,就會進入到 postProcessBeforeInstantiation 方法的 if 分支中,該分支將當前 beanName 存入到 advisedBeans 集合中,存儲的 key 就是 beanName,value 則是 false,然后將方法 return。

當 bean 創(chuàng)建完成之后,再進入到 AbstractAutoProxyCreator#postProcessAfterInitialization 方法中處理的時候,就會發(fā)現(xiàn)這個 bean 已經(jīng)存入到 advisedBeans 集合中,并且 value 是 false,這就意味著這個 bean 不需要代理,那么就針對該 bean 就不會進行 AOP 處理了,直接 return 即可。

以上就是Spring Bean名稱不會被代理的命名技巧的詳細內容,更多關于Spring Bean命名的資料請關注腳本之家其它相關文章!

相關文章

  • Java HashSet集合存儲遍歷學生對象代碼實例

    Java HashSet集合存儲遍歷學生對象代碼實例

    這篇文章主要介紹了Java HashSet集合存儲遍歷學生對象代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-04-04
  • Spring和Websocket相結合實現(xiàn)消息的推送

    Spring和Websocket相結合實現(xiàn)消息的推送

    這篇文章主要介紹了Spring和Websocket相結合實現(xiàn)消息的推送的相關資料,本文介紹的非常詳細具有參考借鑒價值,感興趣的朋友一起學習吧
    2016-02-02
  • java使用Abobe Acrobat DC生成模板

    java使用Abobe Acrobat DC生成模板

    這篇文章主要介紹了java使用Abobe Acrobat DC生成模板,文中有非常詳細的代碼示例,對正在學習java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-04-04
  • SpringBoo項目打war包的多種方式

    SpringBoo項目打war包的多種方式

    在idea中,經(jīng)常會對web項目進行打jar包或者war包,jar包在Java環(huán)境中運行,war包在Tomcat服務器中跑,對于打war包有多種方式,一下介紹3鐘方式,需要的朋友可以參考下
    2024-06-06
  • SpringBoot中使用線程池控制主線程與子線程事務的全過程

    SpringBoot中使用線程池控制主線程與子線程事務的全過程

    SpringBoot中使用線程池控制主線程和子線程的事務是一個復雜但重要的話題,在Spring框架中,事務管理是基于ThreadLocal實現(xiàn)的,接下來通過本文給大家介紹SpringBoot中使用線程池控制主線程與子線程事務的方法,感興趣的朋友跟隨小編一起看看吧
    2026-02-02
  • 使用Spring注入Hibernate驗證框架

    使用Spring注入Hibernate驗證框架

    這篇文章主要介紹了使用Spring注入Hibernate驗證框架方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java中輕量級http開發(fā)庫Unirest使用及實用技巧

    Java中輕量級http開發(fā)庫Unirest使用及實用技巧

    Unirest for Java 是一個輕量級、易于使用的 HTTP 客戶端庫,旨在簡化 Java 應用程序中的 HTTP 請求發(fā)送和響應處理,本文詳細介紹它的主要特性、基本用法以及一些實用技巧,感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • Java制作智能拼圖游戲原理及代碼

    Java制作智能拼圖游戲原理及代碼

    本文給大家分享的是使用Java實現(xiàn)智能拼圖游戲的原理,以及實現(xiàn)的源碼,推薦給大家,有需要的小伙伴可以參考下。
    2015-03-03
  • Java HtmlParse提取標簽中的值操作

    Java HtmlParse提取標簽中的值操作

    這篇文章主要介紹了Java HtmlParse提取標簽中的值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Java Array與ArrayList區(qū)別詳解

    Java Array與ArrayList區(qū)別詳解

    這篇文章主要介紹了Java Array與ArrayList區(qū)別詳解的相關資料,需要的朋友可以參考下
    2017-01-01

最新評論

简阳市| 鄯善县| 东平县| 长宁县| 墨脱县| 托克托县| 静安区| 会理县| 阳城县| 海安县| 韶山市| 宜州市| 靖远县| 西贡区| 镇宁| 翁牛特旗| 新和县| 阿坝| 邢台市| 东丽区| 石嘴山市| 北海市| 禹州市| 松阳县| 翁牛特旗| 山阳县| 鄂州市| 滁州市| 通化县| 平定县| 长子县| 山东省| 佛教| 十堰市| 宣城市| 民和| 石门县| 大同市| 万安县| 德格县| 铁岭市|