Springboot源碼分析之EnableAspectJAutoProxy

来源:https://www.cnblogs.com/qinzj/archive/2019/08/22/11397253.html
-Advertisement-
Play Games

摘要: 的兩大核心技術就是 和`AOP AOP Spring AOP CGLIB Spring AOP Spring AOP`的一個運行過程。知其然,知其所以然,才能更好的駕馭這門核心技術。 所有的 驅動技術都得看他的 ,所以上面最重要的是這一句 ,下麵看看它 是一個項容器註冊自動代理創建器 說明 ...


摘要:

Spring Framwork的兩大核心技術就是IOCAOPAOPSpring的產品線中有著大量的應用。如果說反射是你通向高級的基礎,那麼代理就是你站穩高級的底氣。AOP的本質也就是大家所熟悉的CGLIB動態代理技術,在日常工作中想必或多或少都用過但是它背後的秘密值得我們去深思。本文主要從Spring AOP運行過程上,結合一定的源碼整體上介紹Spring AOP的一個運行過程。知其然,知其所以然,才能更好的駕馭這門核心技術。

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Import({AspectJAutoProxyRegistrar.class})
    public @interface EnableAspectJAutoProxy {
        //表明該類採用CGLIB代理還是使用JDK的動態代理
        boolean proxyTargetClass() default false;
         /**
         * @since 4.3.1 代理的暴露方式:解決內部調用不能使用代理的場景  預設為false表示不處理
         * true:這個代理就可以通過AopContext.currentProxy()獲得這個代理對象的一個副本(ThreadLocal裡面),從而我們可以很方便得在Spring框架上下文中拿到當前代理對象(處理事務時很方便)
         * 必須為true才能調用AopContext得方法,否則報錯:Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.
         */
        boolean exposeProxy() default false;
    }

所有的EnableXXX驅動技術都得看他的@Import,所以上面最重要的是這一句@Import(AspectJAutoProxyRegistrar.class),下麵看看它

    class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
        AspectJAutoProxyRegistrar() {
        }
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            //註冊了一個基於註解的自動代理創建器   AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
            AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
            if (enableAspectJAutoProxy != null) {
                  //表示強制指定了要使用CGLIB
                if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                }
              //強制暴露Bean的代理對象到AopContext
                if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                    AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
                }
            }
        }
    }

AspectJAutoProxyRegistrar是一個項容器註冊自動代理創建器

    @Nullable
        public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
                BeanDefinitionRegistry registry, @Nullable Object source) {
            return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
        }

說明spring容器的註解代理創建器就是AnnotationAwareAspectJAutoProxyCreator

    @Nullable
        private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
            Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
          //這裡如果我們自己定義了這樣一個自動代理創建器就是用我們自定義的
            if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
                BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
                if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
                    int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
                  /** 
                   *用戶註冊的創建器,必須是InfrastructureAdvisorAutoProxyCreator
                   *AspectJAwareAdvisorAutoProxyCreator,AnnotationAwareAspectJAutoProxyCreator之一
                  */
                    int requiredPriority = findPriorityForClass(cls);
                    if (currentPriority < requiredPriority) {
                        apcDefinition.setBeanClassName(cls.getName());
                    }
                }
                return null;
            } 
          //若用戶自己沒有定義,那就用預設的AnnotationAwareAspectJAutoProxyCreator
          RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
              beanDefinition.setSource(source);
          //此處註意,增加了一個屬性:最高優先順序執行,後面會和@Async註解一起使用的時候起關鍵作用
            beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
            return beanDefinition;
        }

我們就成功的註入了一個BeanAnnotationAwareAspectJAutoProxyCreator 基於註解的自動代理創建器

Spring中自動創建代理器

file

由此可見,Spring使用BeanPostProcessor讓自動生成代理。基於BeanPostProcessor的自動代理創建器的實現類,將根據一些規則在容器實例化Bean時為匹配的Bean生成代理實例。

AbstractAutoProxyCreator是對自動代理創建器的一個抽象實現。最重要的是,它實現了SmartInstantiationAwareBeanPostProcessor介面,因此會介入到Spring IoC容器Bean實例化的過程。

SmartInstantiationAwareBeanPostProcessor繼承InstantiationAwareBeanPostProcessor所以它最主要的 職責是在bean的初始化前,先會執行所有的InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation,誰第一個返回了不為nullBean,後面就都不會執行了 。然後會再執行BeanPostProcessor#postProcessAfterInitialization

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
            Object exposedObject = bean;
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                        SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                        exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                    }
                }
            }
            return exposedObject;
        }

說明:這個方法是spring的三級緩存中的其中一環,當你調用Object earlySingletonReference = getSingleton(beanName, false);時候就會觸發,其實還有一個地方exposedObject = initializeBean(beanName, exposedObject, mbd);也會觸發導致返回一個代理對象。

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    invokeAwareMethods(beanName, bean);
                    return null;
                }, getAccessControlContext());
            }
            else {
                invokeAwareMethods(beanName, bean);
            }
            Object wrappedBean = bean;
            if (mbd == null || !mbd.isSynthetic()) {
                wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
            }
            try {
                invokeInitMethods(beanName, wrappedBean, mbd);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(
                        (mbd != null ? mbd.getResourceDescription() : null),
                        beanName, "Invocation of init method failed", ex);
            }
            if (mbd == null || !mbd.isSynthetic()) {
                wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
            }
            return wrappedBean;
        }

強調: 這2個地方雖然都有後置增強的作用,但是@Async所使用的AsyncAnnotationBeanPostProcessor不是SmartInstantiationAwareBeanPostProcessor的實現類,所以此處會導致@Transactional@Async處理迴圈依賴時候的不一致性。對於迴圈依賴後續會有單獨章節進行分享。

AbstractAdvisorAutoProxyCreator

如何創建代理對象後續文章在進行分析。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. SqlSessionFactory 與 SqlSession. 通過前面的章節對於mybatis 的介紹及使用,大家都能體會到SqlSession的重要性了吧, 沒錯,從錶面上來看,咱們都是通過SqlSession去執行sql語句(註意:是從錶面看,實際的待會兒就會講)。那麼咱們就先看看是怎麼 ...
  • <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.d... ...
  • 作為編程語言屆的老大哥,學習JAVA的人數不勝數,在這裡分享一些學習JAVA的技巧以及方法,當然,這些技巧及方法使用範圍包含但不限於JAVA. ① 筆記軟體 印象筆記:多端互通很方便(https://www.yinxiang.com/) 應用場景(只描述編程學慣用的到的部分): eDiary: 一款 ...
  • 前後端分離的工作模式於今是非常流行了,前後端工作的對接,就離開不了API文檔的輔助。 根據自己以往的工作經歷,以及瞭解的一些資訊,API文檔的建立,無非以下幾種方式: 1. word文檔模板 2. 第三方平臺,類如postman、showdoc等 3. 框架內單獨自定義一套綁定路由的結構,再... ...
  • 一、線程常用屬性 1.threading.currentThread:返回當前線程變數 2.threading.enumerate:返回一個包含正在運行的線程的list,正在運行的線程指的是線程啟動後,結束前的狀態 3.threading.activeCount:返回正在運行的線程數量,效果跟len ...
  • > **微信公眾號【Java技術江湖】一位阿裡 Java 工程師的技術小站。(關註公眾號後回覆”Java“即可領取 Java基礎、進階、項目和架構師等免費學習資料,更有資料庫、分散式、微服務等熱門技術學習視頻,內容豐富,兼顧原理和實踐,另外也將贈送作者原創的Java學習指南、Java程式員面試指南等 ...
  • 前段時間和室友一起給某個公司做了一個管理系統,每個人分2W多。這裡和大家分享一下做完項目後一點點感受,想到啥就說點啥。 核心競爭力 兩個月就掙了2W塊,掙了我爸媽兩個人一年的收入,每天還賊辛苦,披星戴月的感覺,我還沒睡醒,我爸媽就出去大早上賣菜去了,等我睡醒了,還沒有回來(你站在別動,我去買個橘子) ...
  • 類spring ioc 泛型保留 什麼是泛型擦除 Java並不會傳遞泛型類,舉個直觀的慄子: 這裡 嘗試列印泛型類型, 泛型指定了 類,來個測試看看 是否能被獲取到? 依賴腳本build.gradle 運行可以看到結果是,spring ioc並不能註入獲取泛型 自定義IOC泛型註入 在解決sprin ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...