與篩選合適的通知器相比,建立代理對象的過程則要簡單很多,本文所分析的源碼不過100行,相對比較簡單。在接下里的章節中,我將會首先向你們介紹一些背景知識,而後再去分析源碼。那下面,咱們先來了解一下背景知識。java
在 Spring AOP 配置中,proxy-target-class 屬性可影響 Spring 生成的代理對象的類型。以 XML 配置爲例,可進行以下配置:安全
<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 實現了接口。ide
關於 proxy-target-class 屬性的用途這裏就說完了,下面咱們來看看兩種不一樣建立動態代理的方式。源碼分析
基於 JDK 的動態代理主要是經過 JDK 提供的代理建立類 Proxy 爲目標對象建立代理,下面咱們來看一下 Proxy 中建立代理的方法聲明。以下:測試
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
簡單說一下上面的參數列表:優化
JDK 動態代理對目標類是有必定要求的,即要求目標類必須實現了接口,JDK 動態代理只能爲實現了接口的目標類生成代理對象。至於 InvocationHandler,是一個接口類型,定義了一個 invoke 方法。使用者須要實現該方法,並在其中封裝代理邏輯。ui
關於 JDK 動態代理的介紹,就先說到這。下面我來演示一下 JDK 動態代理的使用方式,以下:this
目標類定義:lua
public interface UserService { void save(User user); void update(User user); } public class UserServiceImpl implements UserService { @Override public void save(User user) { System.out.println("save user info"); } @Override public void update(User user) { System.out.println("update user info"); } }
代理建立者定義:spa
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; } @Override public Object getProxy() { Class<?> clazz = target.getClass(); // 生成代理對象 return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } @Override public 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 方法中的代理邏輯主要用於記錄目標方法的調用時間,和結束時間。下面寫點測試代碼簡單驗證一下,以下:
public class JdkProxyCreatorTest { @Test public 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 動態代理,繼續往下看吧。
當咱們要爲未實現接口的類生成代理時,就沒法使用 JDK 動態代理了。那麼此類的目標對象生成代理時應該怎麼辦呢?固然是使用 CGLIB 了。在 CGLIB 中,代理邏輯是封裝在 MethodInterceptor 實現類中的,代理對象則是經過 Enhancer 類的 create 方法進行建立。下面我來演示一下 CGLIB 建立代理對象的過程,以下:
本節的演示環節,打算調侃(無貶低之意)一下59式坦克
,這是咱們國家大量裝備過的一款坦克。59式坦克有不少種改款,通常把改款統稱爲59改
,59改這個梗也正是源於此。下面咱們先來一覽59式坦克
的風采:
下面咱們的工做就是爲我們的 59 建立一個代理,即 59改。好了,開始咱們的魔改吧。
目標類,59式坦克:
public class Tank59 { void run() { System.out.println("極速前行中...."); } void shoot() { System.out.println("轟...轟...轟...轟..."); } }
CGLIB 代理建立者
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; } @Override public Object getProxy() { Enhancer enhancer = new Enhancer(); // 設置代理類的父類 enhancer.setSuperclass(target.getClass()); // 設置代理邏輯 enhancer.setCallback(methodInterceptor); // 建立代理對象 return enhancer.create(); } }
方法攔截器 - 坦克再製造:
public class TankRemanufacture implements MethodInterceptor { @Override public 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); } }
好了,下面開始演示,測試代碼以下:
public class CglibProxyCreatorTest { @Test public 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式坦克舉例,僅是調侃,並沒有惡意。做爲年輕的一代,咱們應感謝那些爲國防事業作出貢獻的科技人員們。沒有他們貢獻,咱們怕是不會有像今天這樣安全的環境了(儘管不完美)。
到此,背景知識就介紹完了。下一章,我將開始分析源碼。源碼不是很長,主邏輯比較容易懂,因此一塊兒往下看吧。
爲目標 bean 建立代理對象前,須要先建立 AopProxy 對象,而後再調用該對象的 getProxy 方法建立實際的代理類。咱們先來看看 AopProxy 這個接口的定義,以下:
public interface AopProxy { /** 建立代理對象 */ Object getProxy(); Object getProxy(ClassLoader classLoader); }
在 Spring 中,有兩個類實現了 AopProxy,以下:
Spring 在爲目標 bean 建立代理的過程當中,要根據 bean 是否實現接口,以及一些其餘配置來決定使用 AopProxy 何種實現類爲目標 bean 建立代理對象。下面咱們就來看一下代理建立的過程,以下:
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 轉爲 Advisor Advisor[] 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 實現類對象的過程,以下:
protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); } return getAopProxyFactory().createAopProxy(this); } public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public 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 繼承自 CglibAopProxy return new ObjenesisCglibAopProxy(config); } else { // 建立 JDK 動態代理 return new JdkDynamicAopProxy(config); } } }
如上,DefaultAopProxyFactory 根據一些條件決定生成什麼類型的 AopProxy 實現類對象。生成好 AopProxy 實現類對象後,下面就要爲目標 bean 建立代理對象了。這裏以 JdkDynamicAopProxy 爲例,咱們來看一下,該類的 getProxy 方法的邏輯是怎樣的。以下:
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 方法建立代理對象。到此,建立代理對象的整個過程也就分析完了,不知你們看懂了沒。好了,關於建立代理的源碼分析,就先說到這裏吧。
本篇文章對 Spring AOP 建立代理對象的過程進行了較爲詳細的分析,並在分析源碼前介紹了相關的背景知識。總的來講,本篇文章涉及的技術點不是很複雜,相信你們都能看懂。限於我的能力,若文中有錯誤的地方,歡迎你們指出來。好了,本篇文章到此結束,謝謝閱讀。