摘要:只實現(xiàn)了基于方法的攔截器。實現(xiàn)了一個遞歸的調(diào)用,直到執(zhí)行完所有的攔截器。目標對象攔截器列表這個就是我們框架能夠理解的數(shù)據(jù)結(jié)構(gòu),這個時候問題就變成了對于哪個目標,增加哪些攔截器。
原文地址:犀利豆的博客
上一講我們講解了Spring 的 IoC 實現(xiàn)。大家可以去我的博客查看點擊鏈接,這一講我們繼續(xù)說說 Spring 的另外一個重要特性 AOP。之前在看過的大部分教程,對于Spring Aop的實現(xiàn)講解的都不太透徹,大部分文章介紹了Spring Aop的底層技術(shù)使用了動態(tài)代理,至于Spring Aop的具體實現(xiàn)都語焉不詳。這類文章看以后以后,我腦子里浮現(xiàn)的就是這樣一個畫面:
我的想法就是,帶領(lǐng)大家,首先梳理 Spring Aop的實現(xiàn),然后屏蔽細節(jié),自己實現(xiàn)一個Aop框架。加深對Spring Aop的理解。在了解上圖1-4步驟的同時,補充 4 到 5 步驟之間的其他細節(jié)。
讀完這篇文章你將會了解:
Aop是什么?
為什么要使用Aop?
Spirng 實現(xiàn)Aop的思路是什么
自己根據(jù)Spring 思想實現(xiàn)一個 Aop框架
Aop 是什么?面向切面的程序設(shè)計(aspect-oriented programming,AOP)。通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)程序功能統(tǒng)一維護的一種技術(shù)。
為什么需要使用Aop?面向切面編程,實際上就是通過預(yù)編譯或者動態(tài)代理技術(shù)在不修改源代碼的情況下給原來的程序統(tǒng)一添加功能的一種技術(shù)。我們看幾個關(guān)鍵詞,第一個是“動態(tài)代理技術(shù)”,這個就是Spring Aop實現(xiàn)底層技術(shù)。第二個“不修改源代碼”,這個就是Aop最關(guān)鍵的地方,也就是我們平時所說的非入侵性。。第三個“添加功能”,不改變原有的源代碼,為程序添加功能。
舉個例子:如果某天你需要統(tǒng)計若干方法的執(zhí)行時間,如果不是用Aop技術(shù),你要做的就是為每一個方法開始的時候獲取一個開始時間,在方法結(jié)束的時候獲取結(jié)束時間。二者之差就是方法的執(zhí)行時間。如果對每一個需要統(tǒng)計的方法都做如上的操作,那代碼簡直就是災(zāi)難。如果我們使用Aop技術(shù),在不修改代碼的情況下,添加一個統(tǒng)計方法執(zhí)行時間的切面。代碼就變得十分優(yōu)雅。具體這個切面怎么實現(xiàn)?看完下面的文章你一定就會知道。
Spring Aop 是怎么實現(xiàn)的?所謂:
計算機程序 = 數(shù)據(jù)結(jié)構(gòu) + 算法
在閱讀過Spring源碼之后,你就會對這個說法理解更深入了。
Spring Aop實現(xiàn)的代碼非常非常的繞。也就是說 Spring 為了靈活做了非常深層次的抽象。同時 Spring為了兼容 @AspectJ 的Aop協(xié)議,使用了很多 Adapter (適配器)模式又進一步的增加了代碼的復(fù)雜程度。
Spring 的 Aop 實現(xiàn)主要以下幾個步驟:
初始化 Aop 容器。
讀取配置文件。
將配置文件裝換為 Aop 能夠識別的數(shù)據(jù)結(jié)構(gòu) -- Advisor。這里展開講一講這個advisor。Advisor對象中包又含了兩個重要的數(shù)據(jù)結(jié)構(gòu),一個是 Advice,一個是 Pointcut。Advice的作用就是描述一個切面的行為,pointcut描述的是切面的位置。兩個數(shù)據(jù)結(jié)的組合就是”在哪里,干什么“。這樣 Advisor 就包含了”在哪里干什么“的信息,就能夠全面的描述切面了。
Spring 將這個 Advisor 轉(zhuǎn)換成自己能夠識別的數(shù)據(jù)結(jié)構(gòu) -- AdvicedSupport。Spirng 動態(tài)的將這些方法攔截器織入到對應(yīng)的方法。
生成動態(tài)代理代理。
提供調(diào)用,在使用的時候,調(diào)用方調(diào)用的就是代理方法。也就是已經(jīng)織入了增強方法的方法。
自己實現(xiàn)一個 Aop 框架同樣,我也是參考了Aop的設(shè)計。只實現(xiàn)了基于方法的攔截器。去除了很多的實現(xiàn)細節(jié)。
使用上一講的 IoC 框架管理對象。使用 Cglib 作為動態(tài)代理的基礎(chǔ)類。使用 maven 管理 jar 包和 module。所以上一講的 IoC 框架會作為一個 modules 引入項目。
下面我們就來實現(xiàn)我們的Aop 框架吧。
首先來看看代碼的基本結(jié)構(gòu)。
代碼結(jié)構(gòu)比上一講的 IoC 復(fù)雜不少。我們首先對包每個包都干了什么做一個簡單介紹。
invocation 描述的就是一個方法的調(diào)用。注意這里指的是“方法的調(diào)用”,而不是調(diào)用這個動作。
interceptor 大家最熟悉的攔截器,攔截器攔截的目標就是 invcation 包里面的調(diào)用。
advisor 這個包里的對象,都是用來描述切面的數(shù)據(jù)結(jié)構(gòu)。
adapter 這個包里面是一些適配器方法。對于"適配器"不了解的同學(xué)可以去看看"設(shè)計模式"里面的"適配模式"。他的作用就是將 advice 包里的對象適配為 interceptor。
bean 描述我們 json 配置文件的對象。
core 我們框架的核心邏輯。
這個時候宏觀的看我們大概梳理出了一條路線, adaper 將 advisor 適配為 interceptor 去攔截 invoction。
下面我們從這個鏈條的最末端講起:
invcation首先 MethodInvocation 作為所有方法調(diào)用的接口。要描述一個方法的調(diào)用包含三個方法,獲取方法本身getMethod,獲取方法的參數(shù)getArguments,還有執(zhí)行方法本身proceed()。
public interface MethodInvocation { Method getMethod(); Object[] getArguments(); Object proceed() throws Throwable; }
ProxyMethodInvocation 看名字就知道,是代理方法的調(diào)用,增加了一個獲取代理的方法。
public interface ProxyMethodInvocation extends MethodInvocation { Object getProxy(); }interceptor
AopMethodInterceptor 是 Aop 容器所有攔截器都要實現(xiàn)的接口:
public interface AopMethodInterceptor { Object invoke(MethodInvocation mi) throws Throwable; }
同時我們實現(xiàn)了兩種攔截器BeforeMethodAdviceInterceptor和AfterRunningAdviceInterceptor,顧名思義前者就是在方法執(zhí)行以前攔截,后者就在方法運行結(jié)束以后攔截:
public class BeforeMethodAdviceInterceptor implements AopMethodInterceptor { private BeforeMethodAdvice advice; public BeforeMethodAdviceInterceptor(BeforeMethodAdvice advice) { this.advice = advice; } @Override public Object invoke(MethodInvocation mi) throws Throwable { advice.before(mi.getMethod(),mi.getArguments(),mi); return mi.proceed(); } }
public class AfterRunningAdviceInterceptor implements AopMethodInterceptor { private AfterRunningAdvice advice; public AfterRunningAdviceInterceptor(AfterRunningAdvice advice) { this.advice = advice; } @Override public Object invoke(MethodInvocation mi) throws Throwable { Object returnVal = mi.proceed(); advice.after(returnVal,mi.getMethod(),mi.getArguments(),mi); return returnVal; } }
看了上面的代碼我們發(fā)現(xiàn),實際上 mi.proceed()才是執(zhí)行原有的方法。而advice我們上文就說過,是描述增強的方法”干什么“的數(shù)據(jù)結(jié)構(gòu),所以對于這個before攔截器,我們就把advice對應(yīng)的增強方法放在了真正執(zhí)行的方法前面。而對于after攔截器而言,就放在了真正執(zhí)行的方法后面。
這個時候我們過頭來看最關(guān)鍵的 ReflectioveMethodeInvocation
public class ReflectioveMethodeInvocation implements ProxyMethodInvocation { public ReflectioveMethodeInvocation(Object proxy, Object target, Method method, Object[] arguments, ListinterceptorList) { this.proxy = proxy; this.target = target; this.method = method; this.arguments = arguments; this.interceptorList = interceptorList; } protected final Object proxy; protected final Object target; protected final Method method; protected Object[] arguments = new Object[0]; //存儲所有的攔截器 protected final List interceptorList; private int currentInterceptorIndex = -1; @Override public Object getProxy() { return proxy; } @Override public Method getMethod() { return method; } @Override public Object[] getArguments() { return arguments; } @Override public Object proceed() throws Throwable { //執(zhí)行完所有的攔截器后,執(zhí)行目標方法 if(currentInterceptorIndex == this.interceptorList.size() - 1) { return invokeOriginal(); } //迭代的執(zhí)行攔截器。回顧上面的講解,我們實現(xiàn)的攔擊都會執(zhí)行 im.proceed() 實際上又會調(diào)用這個方法。實現(xiàn)了一個遞歸的調(diào)用,直到執(zhí)行完所有的攔截器。 AopMethodInterceptor interceptor = interceptorList.get(++currentInterceptorIndex); return interceptor.invoke(this); } protected Object invokeOriginal() throws Throwable{ return ReflectionUtils.invokeMethodUseReflection(target,method,arguments); } }
在實際的運用中,我們的方法很可能被多個方法的攔截器所增強。所以我們,使用了一個list來保存所有的攔截器。所以我們需要遞歸的去增加攔截器。當處理完了所有的攔截器之后,才會真正調(diào)用調(diào)用被增強的方法。我們可以認為,前文所述的動態(tài)的織入代碼就發(fā)生在這里。
public class CglibMethodInvocation extends ReflectioveMethodeInvocation { private MethodProxy methodProxy; public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, ListinterceptorList, MethodProxy methodProxy) { super(proxy, target, method, arguments, interceptorList); this.methodProxy = methodProxy; } @Override protected Object invokeOriginal() throws Throwable { return methodProxy.invoke(target,arguments); } }
CglibMethodInvocation 只是重寫了 invokeOriginal 方法。使用代理類來調(diào)用被增強的方法。
advisor這個包里面都是一些描述切面的數(shù)據(jù)結(jié)構(gòu),我們講解兩個重要的。
@Data public class Advisor { //干什么 private Advice advice; //在哪里 private Pointcut pointcut; }
如上文所說,advisor 描述了在哪里,干什么。
@Data public class AdvisedSupport extends Advisor { //目標對象 private TargetSource targetSource; //攔截器列表 private Listlist = new LinkedList<>(); public void addAopMethodInterceptor(AopMethodInterceptor interceptor){ list.add(interceptor); } public void addAopMethodInterceptors(List interceptors){ list.addAll(interceptors); } }
這個AdvisedSupport就是 我們Aop框架能夠理解的數(shù)據(jù)結(jié)構(gòu),這個時候問題就變成了--對于哪個目標,增加哪些攔截器。
core有了上面的準備,我們就開始講解核心邏輯了。
@Data public class CglibAopProxy implements AopProxy{ private AdvisedSupport advised; private Object[] constructorArgs; private Class>[] constructorArgTypes; public CglibAopProxy(AdvisedSupport config){ this.advised = config; } @Override public Object getProxy() { return getProxy(null); } @Override public Object getProxy(ClassLoader classLoader) { Class> rootClass = advised.getTargetSource().getTagetClass(); if(classLoader == null){ classLoader = ClassUtils.getDefultClassLoader(); } Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(rootClass.getSuperclass()); //增加攔截器的核心方法 Callback callbacks = getCallBack(advised); enhancer.setCallback(callbacks); enhancer.setClassLoader(classLoader); if(constructorArgs != null && constructorArgs.length > 0){ return enhancer.create(constructorArgTypes,constructorArgs); } return enhancer.create(); } private Callback getCallBack(AdvisedSupport advised) { return new DynamicAdvisedIcnterceptor(advised.getList(),advised.getTargetSource()); } }
CglibAopProxy就是我們代理對象生成的核心方法。使用 cglib 生成代理類。我們可以與之前ioc框架的代碼。比較發(fā)現(xiàn)區(qū)別就在于:
Callback callbacks = getCallBack(advised); enhancer.setCallback(callbacks);
callback與之前不同了,而是寫了一個getCallback()的方法,我們就來看看 getCallback 里面的 DynamicAdvisedIcnterceptor到底干了啥。
篇幅問題,這里不會介紹 cglib 的使用,對于callback的作用,不理解的同學(xué)需要自行學(xué)習(xí)。
public class DynamicAdvisedInterceptor implements MethodInterceptor{ protected final ListinterceptorList; protected final TargetSource targetSource; public DynamicAdvisedInterceptor(List interceptorList, TargetSource targetSource) { this.interceptorList = interceptorList; this.targetSource = targetSource; } @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy); return invocation.proceed(); } }
這里需要注意,DynamicAdvisedInterceptor這個類實現(xiàn)的 MethodInterceptor 是 gclib的接口,并非我們之前的 AopMethodInterceptor。
我們近距離觀察 intercept 這個方法我們看到:
MethodInvocation invocation = new CglibMethodInvocation(obj,targetSource.getTagetObject(),method, args,interceptorList,proxy);
通過這行代碼,我們的整個邏輯終于連起來了。也就是這個動態(tài)的攔截器,把我們通過 CglibMethodInvocation 織入了增強代碼的方法,委托給了 cglib 來生成代理對象。
至此我們的 Aop 的核心功能就實現(xiàn)了。
AopBeanFactoryImplpublic class AopBeanFactoryImpl extends BeanFactoryImpl{ private static final ConcurrentHashMapaopBeanDefinitionMap = new ConcurrentHashMap<>(); private static final ConcurrentHashMap aopBeanMap = new ConcurrentHashMap<>(); @Override public Object getBean(String name) throws Exception { Object aopBean = aopBeanMap.get(name); if(aopBean != null){ return aopBean; } if(aopBeanDefinitionMap.containsKey(name)){ AopBeanDefinition aopBeanDefinition = aopBeanDefinitionMap.get(name); AdvisedSupport advisedSupport = getAdvisedSupport(aopBeanDefinition); aopBean = new CglibAopProxy(advisedSupport).getProxy(); aopBeanMap.put(name,aopBean); return aopBean; } return super.getBean(name); } protected void registerBean(String name, AopBeanDefinition aopBeanDefinition){ aopBeanDefinitionMap.put(name,aopBeanDefinition); } private AdvisedSupport getAdvisedSupport(AopBeanDefinition aopBeanDefinition) throws Exception { AdvisedSupport advisedSupport = new AdvisedSupport(); List interceptorNames = aopBeanDefinition.getInterceptorNames(); if(interceptorNames != null && !interceptorNames.isEmpty()){ for (String interceptorName : interceptorNames) { Advice advice = (Advice) getBean(interceptorName); Advisor advisor = new Advisor(); advisor.setAdvice(advice); if(advice instanceof BeforeMethodAdvice){ AopMethodInterceptor interceptor = BeforeMethodAdviceAdapter.getInstants().getInterceptor(advisor); advisedSupport.addAopMethodInterceptor(interceptor); } if(advice instanceof AfterRunningAdvice){ AopMethodInterceptor interceptor = AfterRunningAdviceAdapter.getInstants().getInterceptor(advisor); advisedSupport.addAopMethodInterceptor(interceptor); } } } TargetSource targetSource = new TargetSource(); Object object = getBean(aopBeanDefinition.getTarget()); targetSource.setTagetClass(object.getClass()); targetSource.setTagetObject(object); advisedSupport.setTargetSource(targetSource); return advisedSupport; } }
AopBeanFactoryImpl是我們產(chǎn)生代理對象的工廠類,繼承了上一講我們實現(xiàn)的 IoC 容器的BeanFactoryImpl。重寫了 getBean方法,如果是一個切面代理類,我們使用Aop框架生成代理類,如果是普通的對象,我們就用原來的IoC容器進行依賴注入。
getAdvisedSupport就是獲取 Aop 框架認識的數(shù)據(jù)結(jié)構(gòu)。
剩下沒有講到的類都比較簡單,大家看源碼就行。與核心邏輯無關(guān)。
寫個方法測試一下我們需要統(tǒng)計一個方法的執(zhí)行時間。面對這個需求我們怎么做?
public class StartTimeBeforeMethod implements BeforeMethodAdvice{ @Override public void before(Method method, Object[] args, Object target) { long startTime = System.currentTimeMillis(); System.out.println("開始計時"); ThreadLocalUtils.set(startTime); } }
public class EndTimeAfterMethod implements AfterRunningAdvice { @Override public Object after(Object returnVal, Method method, Object[] args, Object target) { long endTime = System.currentTimeMillis(); long startTime = ThreadLocalUtils.get(); ThreadLocalUtils.remove(); System.out.println("方法耗時:" + (endTime - startTime) + "ms"); return returnVal; } }
方法開始前,記錄時間,保存到 ThredLocal里面,方法結(jié)束記錄時間,打印時間差。完成統(tǒng)計。
目標類:
public class TestService { public void testMethod() throws InterruptedException { System.out.println("this is a test method"); Thread.sleep(1000); } }
配置文件:
[ { "name":"beforeMethod", "className":"com.xilidou.framework.aop.test.StartTimeBeforeMethod" }, { "name":"afterMethod", "className":"com.xilidou.framework.aop.test.EndTimeAfterMethod" }, { "name":"testService", "className":"com.xilidou.framework.aop.test.TestService" }, { "name":"testServiceProxy", "className":"com.xilidou.framework.aop.core.ProxyFactoryBean", "target":"testService", "interceptorNames":[ "beforeMethod", "afterMethod" ] } ]
測試類:
public class MainTest { public static void main(String[] args) throws Exception { AopApplictionContext aopApplictionContext = new AopApplictionContext("application.json"); aopApplictionContext.init(); TestService testService = (TestService) aopApplictionContext.getBean("testServiceProxy"); testService.testMethod(); } }
最終我們的執(zhí)行結(jié)果:
開始計時 this is a test method 方法耗時:1015ms Process finished with exit code 0
至此 Aop 框架完成。
后記Spring 的兩大核心特性 IoC 與 Aop 兩大特性就講解完了,希望大家通過我寫的兩篇文章能夠深入理解兩個特性。
Spring的源碼實在是復(fù)雜,閱讀起來常常給人極大的挫敗感,但是只要能夠堅持,并采用一些行之有效的方法。還是能夠理解Spring的代碼。并且從中汲取營養(yǎng)。
下一篇文章,我會給大家講講閱讀開源代碼的一些方法和我自己的體會,敬請期待。
最后github:https://github.com/diaozxin00...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/68353.html
摘要:我們就可以將這些請求合并,達到一定數(shù)量我們統(tǒng)一提交。總結(jié)一個比較生動的例子給大家講解了一些多線程的具體運用。學(xué)習(xí)多線程應(yīng)該多思考多動手,才會有比較好的效果。地址徒手擼框架系列文章地址徒手擼框架實現(xiàn)徒手擼框架實現(xiàn) 原文地址:https://www.xilidou.com/2018/01/22/merge-request/ 在高并發(fā)系統(tǒng)中,我們經(jīng)常遇到這樣的需求:系統(tǒng)產(chǎn)生大量的請求,但是這...
摘要:從而能夠進一步深入了解框架。至此我們框架開發(fā)完成。雖然說閱讀源碼是了解框架的最終手段。但是框架作為一個生產(chǎn)框架,為了保證通用和穩(wěn)定,源碼必定是高度抽象,且處理大量細節(jié)。下一篇文章應(yīng)該會是徒手擼框架實現(xiàn)。 原文地址:https://www.xilidou.com/2018/... Spring 作為 J2ee 開發(fā)事實上的標準,是每個Java開發(fā)人員都需要了解的框架。但是Spring 的...
摘要:我們繼續(xù)看代碼的意思是這個是一段內(nèi)嵌匯編代碼。也就是在語言中使用匯編代碼。就是匯編版的比較并交換。就是保證在多線程情況下,不阻塞線程的填充和消費。微觀上看匯編的是實現(xiàn)操作系統(tǒng)級別的原子操作的基石。 原文地址:https://www.xilidou.com/2018/02/01/java-cas/ CAS 是現(xiàn)代操作系統(tǒng),解決并發(fā)問題的一個重要手段,最近在看 eureka 的源碼的時候。...
摘要:徒手擼一個簡單的框架之前在牛逼哄哄的框架,底層到底什么原理得知了遠程過程調(diào)用簡單來說就是調(diào)用遠程的服務(wù)就像調(diào)用本地方法一樣,其中用到的知識有序列化和反序列化動態(tài)代理網(wǎng)絡(luò)傳輸動態(tài)加載反射這些知識點。 徒手擼一個簡單的RPC框架 之前在牛逼哄哄的 RPC 框架,底層到底什么原理得知了RPC(遠程過程調(diào)用)簡單來說就是調(diào)用遠程的服務(wù)就像調(diào)用本地方法一樣,其中用到的知識有序列化和反序列化、動態(tài)...
摘要:先來看代碼吧,一會松哥再慢慢解釋關(guān)于這一段自動配置,解釋如下首先注解表明這是一個配置類。本文的案例,松哥已經(jīng)上傳到上了,地址。我們使用 Spring Boot,基本上都是沉醉在它 Stater 的方便之中。Starter 為我們帶來了眾多的自動化配置,有了這些自動化配置,我們可以不費吹灰之力就能搭建一個生產(chǎn)級開發(fā)環(huán)境,有的小伙伴會覺得這個 Starter 好神奇呀!其實 Starter 也都...
閱讀 3201·2021-11-25 09:43
閱讀 3206·2021-11-23 09:51
閱讀 3519·2019-08-30 13:08
閱讀 1569·2019-08-29 12:48
閱讀 3594·2019-08-29 12:26
閱讀 397·2019-08-28 18:16
閱讀 2562·2019-08-26 13:45
閱讀 2429·2019-08-26 12:15