Spring源碼分析之AOP-@EnableAspectJAutoProxy

前言

? 這篇文章之前我們說了Springboot的啟動流程,Bean對象怎么實現從無到有的一個過程還有一些接口的拓展的實現等等那么從這一篇文章開始的話我們就會開始說一說我們的常用的AOP它的底層實現原理所以大家一起加油加油!!!

AOP:

1.簡介:

? AOP的話大家其實就是特別熟悉就算沒有使用過的話其實也聽過,其實它就是面向切面編程,我的理解就是說將不同模塊的公共的邏輯全部都提取出來然后進行一個統一的處理如:日志的打印,權限的管理,其實這么做的目的就是說其實就是減少代碼的冗余我在下面寫了一個Demo

切面:

@Aspect
@Component
public class AOPDemo {//這個是第一種方法@Pointcut("execution( * com.example.springsimpledemo.Demo.test(..))")public  void testMethod(){};@Before("testMethod()")public  void testDemo(){System.out.println("成功調用AOP方法");}//這個是第二種方法@Before("execution( * com.example.springsimpledemo.Demo.test(..))")public  void testDemo(){System.out.println("成功調用AOP方法");}
}

測試類:

@Component
public class Demo {public  void test(){System.out.println("這個是一個測試的方法");}
}

啟動類:

@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringSimpleDemoApplication {public static void main(String[] args) {// 啟動 Spring Boot 應用ConfigurableApplicationContext context =SpringApplication.run(SpringSimpleDemoApplication.class, args);Demo bean = (Demo) context.getBean("demo");bean.test();}
}

結果:

那么從上面我們就是可以知道: 整個AOP的組成主要就是說有四部分組成的:

1.切面(添加Aspect注解)這個就是對不同模塊中公共的邏輯進行通過處理的地方也就是說進行了模塊化的處理管理連接點 切點以及通知(這個就是在哪里干以及干什么 什么時候干 的集合)

2.切點(@pointcut):這個的話其實就是說是一個表達式就是對哪些地方進行AOP的操作可以是一個包或者具體的一個類,這個也是后面用于和連接點進行一個匹配(這個就是在哪里干)

3.通知(advice):這個表示的就是說進行AOP操作的時間有前置后置以及環繞(什么時候干)

4.連接點(joinPoint):這個就是就是正在進行AOP操作的地方,也就是說根據切點匹配之后然后根據通過進行相對應的額外增強的操作,一般就是在方法調用的時候會生效(干的地方)

2.AOP失效的場景:

?我們在上面測試類的基礎之上的話進行了一次修改然后我們繼續進行測試

新的測試類:

//這個就是添加了一個check方法并且在test方法里面進行方法的調用
@Component
public class Demo {public void check(){System.out.println("在這個測試方法調用前進行校驗");}public  void test(){check();System.out.println("這個是一個測試的方法");}
}

測試代碼:

@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringSimpleDemoApplication {public static void main(String[] args) {// 啟動 Spring Boot 應用ConfigurableApplicationContext context =SpringApplication.run(SpringSimpleDemoApplication.class, args);Demo bean = (Demo) context.getBean("demo");bean.test();}
}

這個其實的話就是會產生一個AOP失效的場景,如果我們按照正常的思路其實我們就是說這個最后的結果里面肯定會有兩個輸出的結果為成功調用AOP方法但是最終的只會出現一個因為調用test方法的對象是代理對象但是調用test方法里面調用的對象是普通的對象不信的話我們就一起來看一看如下圖所示:

解決方案:

? 其實現在的話解決方案就是說進行功能之間的拆分


@Component
public class Demo {public void check(){System.out.println("在這個測試方法調用前進行校驗");}public  void test(){System.out.println("這個是一個測試的方法");}
}
@SpringBootApplication
@EnableAspectJAutoProxy
public class SpringSimpleDemoApplication {public static void main(String[] args) {// 啟動 Spring Boot 應用ConfigurableApplicationContext context =SpringApplication.run(SpringSimpleDemoApplication.class, args);Demo bean = (Demo) context.getBean("demo");bean.check();bean.test();}
}

通過我上面進行拆分之后就能夠達到我們想要其得到的最終的效果:

2.原理:

EnableAspectJAutoProxy注解:

?其實這個注解的話就是我們整個AOP的整個核心,下面的話我們就看看這個源碼:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {//這個就是說是否通過CGLB動態代理創建一個代理對象進行動態代理boolean proxyTargetClass() default false;//是否暴露代理對象boolean exposeProxy() default false;}

@Import(AspectJAutoProxyRegistrar.class)?注解引入了AspectJAutoProxyRegistrar?類那么我們再來看看這個源碼

@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//如果必要的話那么就是會創建一個自動代理的創建器(這個就是創建代理對象的核心)//1.如果說容器里面不存在或者優先級比較低的話那么肯定是會進行創建的//2.創建出來就是會進行一個優先級的比較的使用的肯定就是說優先級比較高的那一個AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);// 獲取 @EnableAspectJAutoProxy 注解AnnotationAttributes enableAspectJAutoProxy =AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);if (enableAspectJAutoProxy != null) {//這個就是解析要解析proxyTargetClass屬性if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}//這個就是解析exposeProxy的屬性if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}

從上面的源碼我們就是可以看出來這個類的主要的目的就是說進行一個自動代理創建器的創建因為這個的作用的話其實見名知意:就是自動為需要進行代理的Bean對象創建代理對象(這個就是簡單理解為就是Spring封裝的一個工具類而已)下面的話我們就來看一看這個自動代理創建器

registerAspectJAnnotationAutoProxyCreatorIfNecessary:

這個方法經過幾次的跳轉之后就是到達了AopConfigUtils.registerOrEscalateApcAsRequired()這個方法里面下面的話我們就是來看一看這個里面的方法

@Nullableprivate static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");//如果已經存在的話if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);if (!cls.getName().equals(apcDefinition.getBeanClassName())) {int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());int requiredPriority = findPriorityForClass(cls);//這個就是優先級之間的比較if (currentPriority < requiredPriority) {//這個就是修改BeanDefinition對應的class的值apcDefinition.setBeanClassName(cls.getName());}}//如果已經存在的自動代理創建器和將要創建的自動代理創建器一致的話那么就不需要進行如何處理return null;}//不存在的話那么就進行創建就行了RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);//public static final String AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator";registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}//這個優先級的話就是這個自動代理創建器的BeanClassName在集合中的位置private static int findPriorityForClass(Class<?> clazz) {return APC_PRIORITY_LIST.indexOf(clazz);}private static int findPriorityForClass(@Nullable String className) {for (int i = 0; i < APC_PRIORITY_LIST.size(); i++) {Class<?> clazz = APC_PRIORITY_LIST.get(i);if (clazz.getName().equals(className)) {return i;}}throw new IllegalArgumentException("Class name [" + className + "] is not a known auto-proxy creator class");}

從上面我們可以看出來我們這個自動代理創建器的話都是注冊為BeanName為"org.springframework.aop.config.internalAutoProxyCreator"的Bean對象但是在AOP的場景下使用的是AnnotationAwareAspectJAutoProxyCreator這個類型的,那么我們只要分析其的源碼其實就是可以明白了

AnnotationAwareAspectJAutoProxyCreator:

? 上面就是將AspectJAnnotationAutoProxyCreator注冊到Spring容器里面然后這個里面的邏輯其實是不多的最重要的邏輯就是在AbstarctAutoProxyCreator

AbstarctAutoProxyCreator:

? 這個wrapIfNecessary放法就是創建代理對象的方法但是真正實現代理對象創建是CreateProxy(這個的話我的下篇文章會進行仔細說明),我覺得更多得就是進行判斷是否要創建一個代理對象,從Bean的生命周期的話以及循環依賴的話代理對象的創建時間是不一樣的,如果在正常的Bean的生命周期的條件下的話那么初始化之后才會創建代理對象但是如果循環依賴的化那么就是在實例化之前就要進行判斷是否要創建代理對象

wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {//已經代理過了的話if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}//不需要繼續增強if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}//如果是(advice advisor pointcut )或者應該跳過代理的話if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.//這個方法就是說如果存在advice或者advisor(也就是說進行增強的處理)Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);//這個就是創建一個代理對象Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}//如果沒有增強的處理的話那么就直接返回普通的對象this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}

下面的話我們就是對需要跳過代理的條件進行判斷的依據進行一個判斷主要就isInfrastructureClass以及shouldSkip這兩個方法進行判斷

isInfrastructureClass:
protected boolean isInfrastructureClass(Class<?> beanClass) {//這個就是通過判斷這個Bean對象對應的class的類型//如果為 Advice  Pointcut Advisor AopInfrastructureBean//那么就會進行跳過 boolean retVal = Advice.class.isAssignableFrom(beanClass) ||Pointcut.class.isAssignableFrom(beanClass) ||Advisor.class.isAssignableFrom(beanClass) ||AopInfrastructureBean.class.isAssignableFrom(beanClass);if (retVal && logger.isTraceEnabled()) {logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");}return retVal;}
shouldSkip:

?這個方法通過幾次跳轉之后就到達了AutoProxyUtils.isOriginalInstance這個方法當中然后我們就是看一下這個源碼

//這個方法就是用來識別哪些原始的對象(就是原汁原味的對象)//就是沒有進行額外的處理的對象就是通過ORIGINAL_INSTANCE_SUFFIX后綴進行區別的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));}

這個類既然是創建代理對象的地方那么這里的話我們就是來復習一下在Bean生命周期中是不是就是實例化前(這個是不是就是說提前進行AOP的操作就是為了解決循環依賴的問題)以及初始化之后也會創建實例對象下面的話那么就是簡單看一下這個里面的方法:

	@Override@Nullable//初始化之后創建代理對象public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyBeanReferences.remove(cacheKey) != bean) {//這個就是創建的就是一個代理對象return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}//這個就是實例話之前創建代理對象其實判斷條件的話就是和wrapIfNecessary一樣//這個注解的話那么就是不進行標記了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;}

總結:

? 如果要進行AOP操作的話那么就是通過@EnableAspectJAutoProxy注解來進行開啟(這個也就是一個自動配置的過程就是通過AopAutoConfiguration),就是通過AspectJAutoProxyRegistrar這個類就是創建了注冊了自動代理創建器AnnotationAwareAspectJAutoProxyCreator,然后之后的操作的話都是在AnnotationAwareAspectJAutoProxyCreator中進行然后實現創建代理類的話就是通過AbstractAutoProxyCreator中的CreateProxy方法當中然后就是說通過ProxyFactory(下一篇文章會進行仔細的說明)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/63893.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/63893.shtml
英文地址,請注明出處:http://en.pswp.cn/web/63893.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux(Centos 7.6)基本信息查看

1.服務器硬件信息查看 1.1.服務器廠商、產品名稱查看 dmidecode -s system-manufacturer&#xff1a;查看服務器廠商信息 dmidecode -s system-product-name&#xff1a;查看服務器產品名稱信息 1.Windows使用VMware安裝的Linux(Centos 7.6)后&#xff0c;服務器廠商、產品名…

多個圖片轉換為PDF文件

將多個圖片轉換為PDF文件在Python中可以通過多個庫來實現&#xff0c;其中最常用的庫之一是Pillow&#xff08;用于圖像處理&#xff09;和reportlab&#xff08;用于生成PDF&#xff09;。不過&#xff0c;對于直接圖片轉PDF的操作&#xff0c;更推薦使用Pillow配合PyMuPDF&am…

小程序app封裝公用頂部篩選區uv-drop-down

參考ui:DropDown 下拉篩選 | 我的資料管理-uv-ui 是全面兼容vue32、nvue、app、h5、小程序等多端的uni-app生態框架 樣式示例&#xff1a; 封裝公用文件代碼 dropDownTemplete <template><!-- 頂部下拉篩選區封裝公用組件 --><view><uv-drop-down ref&…

LeetCode:101. 對稱二叉樹

跟著carl學算法&#xff0c;本系列博客僅做個人記錄&#xff0c;建議大家都去看carl本人的博客&#xff0c;寫的真的很好的&#xff01; 代碼隨想錄 LeetCode&#xff1a;101. 對稱二叉樹 給你一個二叉樹的根節點 root &#xff0c; 檢查它是否軸對稱。 示例 1&#xff1a; 輸…

Docker-如何啟動docker

作者介紹&#xff1a;簡歷上沒有一個精通的運維工程師。希望大家多多關注作者&#xff0c;下面的思維導圖也是預計更新的內容和當前進度(不定時更新)。 我們在上一章&#xff0c;講了虛擬化&#xff0c;虛擬化是把硬件虛擬化&#xff0c;然后創建出來的虛擬機完全隔離&#xff…

COMSOL with Matlab

文章目錄 基本介紹COMSOL with MatlabCOMSOL主Matlab輔Matlab為主Comsol為輔 操作步驟常用指令mphopenmphgeommghmeshmphmeshstatsmphnavigatormphplot常用指令mphsavemphlaunchModelUtil.clear 實例教學自動另存新檔**把語法套用到邊界條件**把語法套用到另存新檔 函數及其微分…

游戲關卡設計方法的雜感

1、正規思路是&#xff1a;先寫設計文檔&#xff0c;畫平面圖&#xff0c;再做白模關卡&#xff0c;再做正規模型的關卡。 一步步擴大。 當然是有道理的&#xff0c;從小到大&#xff0c; 但實際上這需要很強的想象力&#xff0c;很多細節靠腦補&#xff0c;初學者很難做好。…

JVM系列(十二) -常用調優命令匯總

最近對 JVM 技術知識進行了重新整理&#xff0c;再次獻上 JVM系列文章合集索引&#xff0c;感興趣的小伙伴可以直接點擊如下地址快速閱讀。 JVM系列(一) -什么是虛擬機JVM系列(二) -類的加載過程JVM系列(三) -內存布局詳解JVM系列(四) -對象的創建過程JVM系列(五) -對象的內存分…

bmp390l傳感器的IIC命令通信(學習匯總)

參考鏈接&#xff1a; BMP390高精度壓力傳感器數據讀取與處理&#xff08;基于STM32&#xff09;-CSDN博客 https://blog.csdn.net/qq_43862401/article/details/106502397 利用usb轉iic模塊測試bmp390l傳感器采集當前環境的溫度和氣壓數據&#xff0c;下圖中reserved表示…

C/C++基礎知識復習(43)

1) 什么是運算符重載&#xff1f;如何在 C 中進行運算符重載&#xff1f; 運算符重載是指在 C 中為現有的運算符定義新的行為&#xff0c;使得它們能夠用于用戶定義的數據類型&#xff08;如類或結構體&#xff09;。通過運算符重載&#xff0c;可以讓自定義類型像內置數據類型…

Windows11 家庭版安裝配置 Docker

1. 安裝WSL WSL 是什么&#xff1a; WSL 是一個在 Windows 上運行 Linux 環境的輕量級工具&#xff0c;它可以讓用戶在 Windows 系統中運行 Linux 工具和應用程序。Docker 為什么需要 WSL&#xff1a; Docker 依賴 Linux 內核功能&#xff0c;WSL 2 提供了一個高性能、輕量級的…

2025系統架構師(一考就過):案例題之一:嵌入式架構、大數據架構、ISA

一、嵌入式系統架構 軟件脆弱性是軟件中存在的弱點(或缺陷)&#xff0c;利用它可以危害系統安全策略&#xff0c;導致信息丟失、系統價值和可用性降低。嵌入式系統軟件架構通常采用分層架構&#xff0c;它可以將問題分解為一系列相對獨立的子問題&#xff0c;局部化在每一層中…

新手SEO指南如何快速入門與提升網站排名

內容概要 搜索引擎優化&#xff08;SEO&#xff09;是提高網站可見度和排名的重要手段&#xff0c;尤其對新手來說&#xff0c;掌握其基本概念和實用技巧至關重要。本文將針對新手提供一系列的指導&#xff0c;幫助你快速入門并逐步提升網站排名。 首先&#xff0c;了解SEO的…

Oracle下載安裝(保姆級教學)

方法1 1. 官網下載安裝包 對于 Oracle 軟件的下載&#xff0c;建議通過官網免費下載&#xff0c;安全且有保證。 下載地址&#xff1a; https://www.oracle.com/database/technologies/oracle19c-windows-downloads.html 通過下載頁面可以選擇安裝壓縮包&#xff08; WIND…

第22天:信息收集-Web應用各語言框架安全組件聯動系統數據特征人工分析識別項目

#知識點 1、信息收集-Web應用-開發框架-識別安全 2、信息收集-Web應用-安全組件-特征分析 一、ICO圖標&#xff1a; 1、某個應用系統的標示&#xff0c;如若依系統有自己特點的圖標&#xff1b;一旦該系統出問題&#xff0c;使用該系統的網站都會受到影響&#xff1b; 2、某個公…

重溫設計模式--建造者模式

文章目錄 建造者模式&#xff08;Builder Pattern&#xff09;概述建造者模式UML圖作用&#xff1a;建造者模式的結構產品&#xff08;Product&#xff09;&#xff1a;抽象建造者&#xff08;Builder&#xff09;&#xff1a;具體建造者&#xff08;Concrete Builder&#xff…

每日一題(4)

有一只蝸牛位于二維坐標系的原點(0,0)&#xff0c;在x軸上有n根平行于y軸的竹竿&#xff0c;它們底部的縱坐標為0&#xff0c;橫坐標分別為x_1,x_2,\cdots,x_n。蝸牛想要從原點走到第n根竹竿的底部(x_n,0)。蝸牛在x軸上的移動速度是1單位每秒&#xff0c;在竹竿上向上爬的速度是…

arcgisPro相接多個面要素轉出為完整獨立線要素

1、使用【面轉線】工具&#xff0c;并取消勾選“識別和存儲面鄰域信息”&#xff0c;如下&#xff1a; 2、得到的線要素&#xff0c;如下&#xff1a;

51單片機仿真搖號抽獎機源程序 12864液晶顯示

資料下載地址&#xff1a;51單片機仿真搖號抽獎機源程序12864液晶顯示仿真程序 一、功能介紹 單片機連接12864&#xff08;st7920&#xff09;液晶顯示器和1個按鍵接INT0&#xff0c;模擬一個抽獎機。 實現效果&#xff1a; 1、液晶初始顯示“祝你好運&#xff01;”&#xff…

如何在 Ubuntu 22.04 上安裝 Graylog 開源日志管理平臺

簡介 Graylog 的開源特性、豐富的功能、靈活性和可擴展性使其成為一個流行的日志管理平臺。在本教程中&#xff0c;我將向你展示如何在 Ubuntu 22.04 上安裝 Graylog&#xff0c;包括配置 Graylog 服務器軟件包和訪問 Graylog Web UI。 Graylog 是什么&#xff1f; Graylog …