日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring AOP 源码分析 - 创建代理对象

發布時間:2025/3/21 javascript 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring AOP 源码分析 - 创建代理对象 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.簡介

在上一篇文章中,我分析了 Spring 是如何為目標 bean 篩選合適的通知器的?,F在通知器選好了,接下來就要通過代理的方式將通知器(Advisor)所持有的通知(Advice)織入到 bean 的某些方法前后。與篩選合適的通知器相比,創建代理對象的過程則要簡單不少,本文所分析的源碼不過100行,相對比較簡單。在接下里的章節中,我將會首先向大家介紹一些背景知識,然后再去分析源碼。那下面,我們先來了解一下背景知識。

?2.背景知識

?2.1 proxy-target-class

在 Spring AOP 配置中,proxy-target-class 屬性可影響 Spring 生成的代理對象的類型。以 XML 配置為例,可進行如下配置:

1 2 3 4 5 6 7 <aop:aspectj-autoproxy proxy-target-class="true"/><aop:config proxy-target-class="true"><aop:aspect id="xxx" ref="xxxx"><!-- 省略 --></aop:aspect> </aop:config>

如上,默認情況下 proxy-target-class 屬性為 false。當目標 bean 實現了接口時,Spring 會基于 JDK 動態代理為目標 bean 創建代理對象。若未實現任何接口,Spring 則會通過 CGLIB 創建代理。而當 proxy-target-class 屬性設為 true 時,則會強制 Spring 通過 CGLIB 的方式創建代理對象,即使目標 bean 實現了接口。

關于 proxy-target-class 屬性的用途這里就說完了,下面我們來看看兩種不同創建動態代理的方式。

?2.2 動態代理

?2.2.1 基于 JDK 的動態代理

基于 JDK 的動態代理主要是通過 JDK 提供的代理創建類 Proxy 為目標對象創建代理,下面我們來看一下 Proxy 中創建代理的方法聲明。如下:

1 2 3 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

簡單說一下上面的參數列表:

  • loader - 類加載器
  • interfaces - 目標類所實現的接口列表
  • h - 用于封裝代理邏輯

JDK 動態代理對目標類是有一定要求的,即要求目標類必須實現了接口,JDK 動態代理只能為實現了接口的目標類生成代理對象。至于 InvocationHandler,是一個接口類型,定義了一個 invoke 方法。使用者需要實現該方法,并在其中封裝代理邏輯。

關于 JDK 動態代理的介紹,就先說到這。下面我來演示一下 JDK 動態代理的使用方式,如下:

目標類定義:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public interface UserService {void save(User user);void update(User user); }public class UserServiceImpl implements UserService {@Overridepublic void save(User user) {System.out.println("save user info");}@Overridepublic void update(User user) {System.out.println("update user info");} }

代理創建者定義:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public interface ProxyCreator {Object getProxy(); }public class JdkProxyCreator implements ProxyCreator, InvocationHandler {private Object target;public JdkProxyCreator(Object target) {assert target != null;Class<?>[] interfaces = target.getClass().getInterfaces();if (interfaces.length == 0) {throw new IllegalArgumentException("target class don`t implement any interface");}this.target = target;}@Overridepublic Object getProxy() {Class<?> clazz = target.getClass();// 生成代理對象return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(System.currentTimeMillis() + " - " + method.getName() + " method start");// 調用目標方法Object retVal = method.invoke(target, args);System.out.println(System.currentTimeMillis() + " - " + method.getName() + " method over");return retVal;} }

如上,invoke 方法中的代理邏輯主要用于記錄目標方法的調用時間,和結束時間。下面寫點測試代碼簡單驗證一下,如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class JdkProxyCreatorTest {@Testpublic void getProxy() throws Exception {ProxyCreator proxyCreator = new JdkProxyCreator(new UserServiceImpl());UserService userService = (UserService) proxyCreator.getProxy();System.out.println("proxy type = " + userService.getClass());System.out.println();userService.save(null);System.out.println();userService.update(null);} }

測試結果如下:

如上,從測試結果中。我們可以看出,我們的代理邏輯正常執行了。另外,注意一下 userService 指向對象的類型,并非是 xyz.coolblog.proxy.UserServiceImpl,而是 com.sun.proxy.$Proxy4。

關于 JDK 動態代理,這里先說這么多。下一節,我來演示一下 CGLIB 動態代理,繼續往下看吧。

?2.2.2 基于 CGLIB 的動態代理

當我們要為未實現接口的類生成代理時,就無法使用 JDK 動態代理了。那么此類的目標對象生成代理時應該怎么辦呢?當然是使用 CGLIB 了。在 CGLIB 中,代理邏輯是封裝在 MethodInterceptor 實現類中的,代理對象則是通過 Enhancer 類的 create 方法進行創建。下面我來演示一下 CGLIB 創建代理對象的過程,如下:

本節的演示環節,打算調侃(無貶低之意)一下59式坦克,這是我們國家大量裝備過的一款坦克。59式坦克有很多種改款,一般把改款統稱為59改,59改這個梗也正是源于此。下面我們先來一覽59式坦克的風采:


圖片來源:百度圖片搜索

下面我們的工作就是為咱們的 59 創建一個代理,即 59改。好了,開始我們的魔改吧。

目標類,59式坦克:

1 2 3 4 5 6 7 8 9 10 public class Tank59 {void run() {System.out.println("極速前行中....");}void shoot() {System.out.println("轟...轟...轟...轟...");} }

CGLIB 代理創建者

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class CglibProxyCreator implements ProxyCreator {private Object target;private MethodInterceptor methodInterceptor;public CglibProxyCreator(Object target, MethodInterceptor methodInterceptor) {assert (target != null && methodInterceptor != null);this.target = target;this.methodInterceptor = methodInterceptor;}@Overridepublic Object getProxy() {Enhancer enhancer = new Enhancer();// 設置代理類的父類enhancer.setSuperclass(target.getClass());// 設置代理邏輯enhancer.setCallback(methodInterceptor);// 創建代理對象return enhancer.create();} }

方法攔截器 - 坦克再制造:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class TankRemanufacture implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {if (method.getName().equals("run")) {System.out.println("正在重造59坦克...");System.out.println("重造成功,已獲取 ?59改 之 超音速飛行版?");System.out.print("已起飛,正在突破音障。");methodProxy.invokeSuper(o, objects);System.out.println("已擊落黑鳥 SR-71,正在返航...");return null;}return methodProxy.invokeSuper(o, objects);} }

好了,下面開始演示,測試代碼如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class CglibProxyCreatorTest {@Testpublic void getProxy() throws Exception {ProxyCreator proxyCreator = new CglibProxyCreator(new Tank59(), new TankRemanufacture());Tank59 tank59 = (Tank59) proxyCreator.getProxy();System.out.println("proxy class = " + tank59.getClass() + "\n");tank59.run();System.out.println();System.out.print("射擊測試:");tank59.shoot();} }

測試結果如下:

如上,“極速前行中…” 和 “轟…轟…轟…轟…” 這兩行字符串是目標對象中的方法打印出來的,其他的則是由代理邏輯打印的。由此可知,我們的代理邏輯生效了。

好了,最后我們來看一下,經過魔改后的 59,也就是超音速59改的效果圖:


圖片來源:未知

本節用59式坦克舉例,僅是調侃,并無惡意。作為年輕的一代,我們應感謝那些為國防事業做出貢獻的科技人員們。沒有他們貢獻,我們怕是不會有像今天這樣安全的環境了(盡管不完美)。

到此,背景知識就介紹完了。下一章,我將開始分析源碼。源碼不是很長,主邏輯比較容易懂,所以一起往下看吧。

?3.源碼分析

為目標 bean 創建代理對象前,需要先創建 AopProxy 對象,然后再調用該對象的 getProxy 方法創建實際的代理類。我們先來看看 AopProxy 這個接口的定義,如下:

1 2 3 4 5 6 7 public interface AopProxy {/** 創建代理對象 */Object getProxy();Object getProxy(ClassLoader classLoader); }

在 Spring 中,有兩個類實現了 AopProxy,如下:

Spring 在為目標 bean 創建代理的過程中,要根據 bean 是否實現接口,以及一些其他配置來決定使用 AopProxy 何種實現類為目標 bean 創建代理對象。下面我們就來看一下代理創建的過程,如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);/** 默認配置下,或用戶顯式配置 proxy-target-class = "false" 時,* 這里的 proxyFactory.isProxyTargetClass() 也為 false*/if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {/** 檢測 beanClass 是否實現了接口,若未實現,則將 * proxyFactory 的成員變量 proxyTargetClass 設為 true*/ evaluateProxyInterfaces(beanClass, proxyFactory);}}// specificInterceptors 中若包含有 Advice,此處將 Advice 轉為 AdvisorAdvisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}// 創建代理return proxyFactory.getProxy(getProxyClassLoader()); }public Object getProxy(ClassLoader classLoader) {// 先創建 AopProxy 實現類對象,然后再調用 getProxy 為目標 bean 創建代理對象return createAopProxy().getProxy(classLoader); }

getProxy 這里有兩個方法調用,一個是調用 createAopProxy 創建 AopProxy 實現類對象,然后再調用 AopProxy 實現類對象中的 getProxy 創建代理對象。這里我們先來看一下創建 AopProxy 實現類對象的過程,如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 protected final synchronized AopProxy createAopProxy() {if (!this.active) {activate();}return getAopProxyFactory().createAopProxy(this); }public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {/** 下面的三個條件簡單分析一下:** 條件1:config.isOptimize() - 是否需要優化,這個屬性沒怎么用過,* 細節我不是很清楚* 條件2:config.isProxyTargetClass() - 檢測 proxyTargetClass 的值,* 前面的代碼會設置這個值* 條件3:hasNoUserSuppliedProxyInterfaces(config) * - 目標 bean 是否實現了接口*/if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}// 創建 CGLIB 代理,ObjenesisCglibAopProxy 繼承自 CglibAopProxyreturn new ObjenesisCglibAopProxy(config);}else {// 創建 JDK 動態代理return new JdkDynamicAopProxy(config);}} }

如上,DefaultAopProxyFactory 根據一些條件決定生成什么類型的 AopProxy 實現類對象。生成好 AopProxy 實現類對象后,下面就要為目標 bean 創建代理對象了。這里以 JdkDynamicAopProxy 為例,我們來看一下,該類的 getProxy 方法的邏輯是怎樣的。如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 public Object getProxy() {return getProxy(ClassUtils.getDefaultClassLoader()); }public Object getProxy(ClassLoader classLoader) {if (logger.isDebugEnabled()) {logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());}Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);// 調用 newProxyInstance 創建代理對象return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }

如上,請把目光移至最后一行有效代碼上,會發現 JdkDynamicAopProxy 最終調用 Proxy.newProxyInstance 方法創建代理對象。到此,創建代理對象的整個過程也就分析完了,不知大家看懂了沒。好了,關于創建代理的源碼分析,就先說到這里吧。

?4.總結

本篇文章對 Spring AOP 創建代理對象的過程進行了較為詳細的分析,并在分析源碼前介紹了相關的背景知識??偟膩碚f,本篇文章涉及的技術點不是很復雜,相信大家都能看懂。限于個人能力,若文中有錯誤的地方,歡迎大家指出來。好了,本篇文章到此結束,謝謝閱讀。

?參考

  • 《Spring 源碼深度解析》- 郝佳

?附錄:Spring 源碼分析文章列表

?Ⅰ. IOC

更新時間標題
2018-05-30Spring IOC 容器源碼分析系列文章導讀
2018-06-01Spring IOC 容器源碼分析 - 獲取單例 bean
2018-06-04Spring IOC 容器源碼分析 - 創建單例 bean 的過程
2018-06-06Spring IOC 容器源碼分析 - 創建原始 bean 對象
2018-06-08Spring IOC 容器源碼分析 - 循環依賴的解決辦法
2018-06-11Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象
2018-06-11Spring IOC 容器源碼分析 - 余下的初始化工作

?Ⅱ. AOP

更新時間標題
2018-06-17Spring AOP 源碼分析系列文章導讀
2018-06-20Spring AOP 源碼分析 - 篩選合適的通知器
2018-06-20Spring AOP 源碼分析 - 創建代理對象
2018-06-22Spring AOP 源碼分析 - 攔截器鏈的執行過程

?Ⅲ. MVC

更新時間標題
2018-06-29Spring MVC 原理探秘 - 一個請求的旅行過程
2018-06-30Spring MVC 原理探秘 - 容器的創建過程
  • 本文鏈接:?https://www.tianxiaobo.com/2018/06/20/Spring-AOP-源碼分析-創建代理對象/

http://www.tianxiaobo.com/2018/06/20/Spring-AOP-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E5%88%9B%E5%BB%BA%E4%BB%A3%E7%90%86%E5%AF%B9%E8%B1%A1/?

總結

以上是生活随笔為你收集整理的Spring AOP 源码分析 - 创建代理对象的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。