1. 程式人生 > >手寫AOP實現過程

手寫AOP實現過程

##### 一.手寫Aop前基礎知識 **1.aop是什麼?** 面向切面程式設計(AOP):是一種程式設計正規化,提供從另一個角度來考慮程式結構從而完善面向物件程式設計(OOP)。 在進行OOP開發時,都是基於對元件(比如類)進行開發,然後對元件進行組合,OOP最大問題就是無法解耦元件進行開發,而AOP就是為了克服這個問題而出現的,它來進行這種耦合的分離。 ​ AOP為開發者提供一種進行橫切關注點(比如日誌關注點橫切了支付關注點)分離並織入的機制,把橫切關注點分離,然後通過某種技術織入到系統中,從而無耦合的完成了我們的功能。 **2.aop能幹什麼?** AOP主要用於橫切關注點和織入,因此需要理解橫切關注點和織入: - **關注點:**可以認為是所關注的任何東西,比如上邊的支付元件; - **關注點分離:**將問題細化從而單獨部分,即可以理解為不可再分割的元件,如上邊的日誌元件和支付元件; - **橫切關注點:**一個元件無法完成需要的功能,需要其他元件協作完成,如日誌元件橫切於支付元件; - **織入:**橫切關注點分離後,需要通過某種技術將橫切關注點融合到系統中從而完成需要的功能,因此需要織入,**織入可能在編譯期、載入期、執行期**等進行。 橫切關注點可能包含很多,比如非業務的:日誌、事務處理、快取、效能統計、許可權控制等等這些非業務的基礎功能;還可能是業務的:如某個業務元件橫切於多個模組。 ![](https://img2020.cnblogs.com/blog/1054413/202008/1054413-20200812151018661-1083872562.jpg) 面向切面方式,先將橫切關注點分離,再將橫切關注點織入到支付系統中: ![](https://img2020.cnblogs.com/blog/1054413/202008/1054413-20200812151029793-1039896019.jpg) ![](https://img2020.cnblogs.com/blog/1054413/202008/1054413-20200812151040417-1416647869.jpg) **3.aop的優點?** - 完善OOP; - 降低元件和模組之間的耦合性; - 使系統容易擴充套件; - 而且由於關注點分離從而可以獲得元件的更好複用。 ##### 二.aop底層實現原理:代理模式 關於靜態代理模式,詳情請參閱我另一片部落格: https://www.cnblogs.com/tc971121/p/13474638.html ##### **三.手寫aop主要實現過程** **1.定義配置標記** @Aspect:配置標記橫切物件(方法)的地址 @Order:配置標記橫切的順序 @Aspect/@Order ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Aspect { String pointcut(); } ``` ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Order { /** * 控制類的執行順序,值越小優先順序越高 */ int value(); } ``` **2.定義預設切面類** ```java package org.simplespring.aop.aspect; import java.lang.reflect.Method; /** * 定義預設切面類 */ public abstract class DefaultAspect { /** * 事前攔截 * @param targetClass 被代理的目標類 * @param method 被代理的目標方法 * @param args 被代理的目標方法對應的引數列表 * @throws Throwable */ public void before(Class targetClass, Method method, Object[] args) throws Throwable{ } /** * 事後攔截 * @param targetClass 被代理的目標類 * @param method 被代理的目標方法 * @param args 被代理的目標方法對應的引數列表 * @param returnValue 被代理的目標方法執行後的返回值 * @throws Throwable */ public Object afterReturning(Class targetClass, Method method, Object[] args, Object returnValue) throws Throwable{ return returnValue; } /** * * @param targetClass 被代理的目標類 * @param method 被代理的目標方法 * @param args 被代理的目標方法對應的引數列表 * @param e 被代理的目標方法丟擲的異常 * @throws Throwable */ public void afterThrowing(Class targetClass, Method method, Object[] args, Throwable e) throws Throwable{ } } ``` **3.定義切面織入器** **核心流程:** 1.獲取所有的切面類 2.遍歷容器管理的類 3.篩選出匹配容器管理類的切面aspectlist 4.嘗試進行Aspect的織入 生成動態代理物件 並替換beancontainer中原先class對應所對應的例項物件 ```java package org.simplespring.aop; import org.simplespring.aop.annotation.Aspect; import org.simplespring.aop.annotation.Order; import org.simplespring.aop.aspect.AspectInfo; import org.simplespring.aop.aspect.DefaultAspect; import org.simplespring.core.BeanContainer; import org.simplespring.util.ValidationUtil; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * @Classname AspectWeaver * @Description 切面織入器 * @Date 2020/8/8 19:43 * @Created by zhangtianci */ public class AspectWeaver { /** * 擁有一個bean容器 */ private BeanContainer beanContainer; public AspectWeaver(){ beanContainer = BeanContainer.getInstance(); } /** * 1.獲取所有的切面類 * 2.遍歷容器裡的類 * 3.篩選出匹配類的切面aspect * 4.嘗試進行Aspect的織入 生成動態代理物件 */ public void doAop() { //1.獲取所有的切面類 Set> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class); if(ValidationUtil.isEmpty(aspectSet)){return;} //2.拼裝AspectInfoList List aspectInfoList = packAspectInfoList(aspectSet); //3.遍歷容器裡的類 Set> classSet = beanContainer.getClasses(); for (Class targetClass: classSet) { //排除AspectClass自身 if(targetClass.isAnnotationPresent(Aspect.class)){ continue; } //4.粗篩符合條件的Aspect List roughMatchedAspectList = collectRoughMatchedAspectListForSpecificClass(aspectInfoList, targetClass); //5.嘗試進行Aspect的織入 wrapIfNecessary(roughMatchedAspectList,targetClass); } } private void wrapIfNecessary(List roughMatchedAspectList, Class targetClass) { if(ValidationUtil.isEmpty(roughMatchedAspectList)){return;} //建立動態代理物件 AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass, roughMatchedAspectList); Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor); beanContainer.addBean(targetClass, proxyBean); } private List collectRoughMatchedAspectListForSpecificClass(List aspectInfoList, Class targetClass) { List roughMatchedAspectList = new ArrayList<>(); for(AspectInfo aspectInfo : aspectInfoList){ //粗篩 if(aspectInfo.getPointcutLocator().roughMatches(targetClass)){ roughMatchedAspectList.add(aspectInfo); } } return roughMatchedAspectList; } private List packAspectInfoList(Set> aspectSet) { List aspectInfoList = new ArrayList<>(); for(Class aspectClass : aspectSet){ if (verifyAspect(aspectClass)){ Order orderTag = aspectClass.getAnnotation(Order.class); Aspect aspectTag = aspectClass.getAnnotation(Aspect.class); DefaultAspect defaultAspect = (DefaultAspect) beanContainer.getBean(aspectClass); //初始化表示式定位器 PointcutLocator pointcutLocator = new PointcutLocator(aspectTag.pointcut()); AspectInfo aspectInfo = new AspectInfo(orderTag.value(), defaultAspect, pointcutLocator); aspectInfoList.add(aspectInfo); } else { //不遵守規範則直接丟擲異常 throw new RuntimeException("@Aspect and @Order must be added to the Aspect class, and Aspect class must extend from DefaultAspect"); } } return aspectInfoList; } //框架中一定要遵守給Aspect類新增@Aspect和@Order標籤的規範,同時,必須繼承自DefaultAspect.class //此外,@Aspect的屬性值不能是它本身 private boolean verifyAspect(Class aspectClass) { return aspectClass.isAnnotationPresent(Aspect.class) && aspectClass.isAnnotationPresent(Order.class) && DefaultAspect.class.isAssignableFrom(aspectClass); } } ``` 切面資訊包裝類(增強的動作/增強物件地址/橫切順序) ```java package org.simplespring.aop.aspect; import lombok.AllArgsConstructor; import lombok.Getter; import org.simplespring.aop.PointcutLocator; @AllArgsConstructor @Getter public class AspectInfo { private int orderIndex; private DefaultAspect aspectObject; private PointcutLocator pointcutLocator; } ``` 採用cglib動態的實現方式: 實現net.sf.cglib.proxy.MethodInterceptor介面,定義代理後方法的動作(相當於實現jdk動態代理中的InvocationHandler介面) ```java package org.simplespring.aop; import lombok.Getter; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import org.simplespring.aop.aspect.AspectInfo; import org.simplespring.util.ValidationUtil; import java.lang.reflect.Method; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; public class AspectListExecutor implements MethodInterceptor { //被代理的類 private Class targetClass; //排好序的Aspect列表 @Getter private ListsortedAspectInfoList; public AspectListExecutor(Class targetClass, List aspectInfoList){ this.targetClass = targetClass; this.sortedAspectInfoList = sortAspectInfoList(aspectInfoList); } /** * 按照order的值進行升序排序,確保order值小的aspect先被織入 * * @param aspectInfoList * @return */ private List sortAspectInfoList(List aspectInfoList) { Collections.sort(aspectInfoList, new Comparator() { @Override public int compare(AspectInfo o1, AspectInfo o2) { //按照值的大小進行升序排序 return o1.getOrderIndex() - o2.getOrderIndex(); } }); return aspectInfoList; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object returnValue = null; collectAccurateMatchedAspectList(method); if(ValidationUtil.isEmpty(sortedAspectInfoList)){ returnValue = methodProxy.invokeSuper(proxy, args); return returnValue; } //1.按照order的順序升序執行完所有Aspect的before方法 invokeBeforeAdvices(method, args); try{ //2.執行被代理類的方法 returnValue = methodProxy.invokeSuper(proxy, args); //3.如果被代理方法正常返回,則按照order的順序降序執行完所有Aspect的afterReturning方法 returnValue = invokeAfterReturningAdvices(method, args, returnValue); } catch (Exception e){ //4.如果被代理方法丟擲異常,則按照order的順序降序執行完所有Aspect的afterThrowing方法 invokeAfterThrowingAdvides(method, args, e); } return returnValue; } private void collectAccurateMatchedAspectList(Method method) { if(ValidationUtil.isEmpty(sortedAspectInfoList)){return;} Iterator it = sortedAspectInfoList.iterator(); while (it.hasNext()){ AspectInfo aspectInfo = it.next(); if(!aspectInfo.getPointcutLocator().accurateMatches(method)){ it.remove(); } } } //4.如果被代理方法丟擲異常,則按照order的順序降序執行完所有Aspect的afterThrowing方法 private void invokeAfterThrowingAdvides(Method method, Object[] args, Exception e) throws Throwable { for (int i = sortedAspectInfoList.size() - 1; i >=0 ; i--){ sortedAspectInfoList.get(i).getAspectObject().afterThrowing(targetClass, method, args, e); } } //3.如果被代理方法正常返回,則按照order的順序降序執行完所有Aspect的afterReturning方法 private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable { Object result = null; for (int i = sortedAspectInfoList.size() - 1; i >=0 ; i--){ result = sortedAspectInfoList.get(i).getAspectObject().afterReturning(targetClass, method, args, returnValue); } return result; } //1.按照order的順序升序執行完所有Aspect的before方法 private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable { for(AspectInfo aspectInfo : sortedAspectInfoList){ aspectInfo.getAspectObject().before(targetClass, method, args); } } } ``` 建立代理類: ```java package org.simplespring.aop; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; public class ProxyCreator { /** * 建立動態代理物件並返回 * @param targetClass 被代理的Class物件 * @param methodInterceptor 方法攔截器 * @return */ public static Object createProxy(Class targetClass, MethodInterceptor methodInterceptor){ return Enhancer.create(targetClass, methodInterceptor); } } ``` 解析Aspect表示式並且定位被織入的目標工具類: ```java package org.simplespring.aop; import org.aspectj.weaver.tools.PointcutExpression; import org.aspectj.weaver.tools.PointcutParser; import org.aspectj.weaver.tools.ShadowMatch; import java.lang.reflect.Method; /** * 解析Aspect表示式並且定位被織入的目標 */ public class PointcutLocator { /** * Pointcut解析器,直接給它賦值上Aspectj的所有表示式,以便支援對眾多表達式的解析 */ private PointcutParser pointcutParser= PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution( PointcutParser.getAllSupportedPointcutPrimitives() ); /** * 表示式解析器 */ private PointcutExpression pointcutExpression; public PointcutLocator(String expression){ this.pointcutExpression = pointcutParser.parsePointcutExpression(expression); } /** * 判斷傳入的Class物件是否是Aspect的目標代理類,即匹配Pointcut表示式(初篩) * * @param targetClass 目標類 * @return 是否匹配 */ public boolean roughMatches(Class targetClass){ //couldMatchJoinPointsInType比較坑,只能校驗within //不能校驗 (execution(精確到某個類除外), call, get, set),面對無法校驗的表示式,直接返回true return pointcutExpression.couldMatchJoinPointsInType(targetClass); } /** * 判斷傳入的Method物件是否是Aspect的目標代理方法,即匹配Pointcut表示式(精篩) * @param method * @return */ public boolean accurateMatches(Method method){ ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method); if(shadowMatch.alwaysMatches()){ return true; } else{ return false; } } } ``` 總結實現aop的主要流程: **1.定義配置標記** **2.定義預設切面類** **3.定義切面織入器** **核心流程:** 1.獲取所有的切面類 2.遍歷容器管理的類 3.篩選出匹配容器管理類的切面aspectlist 4.嘗試進行Aspect的織入 生成動態代理物件 並替換beancontainer中原先class對應所對應的例項物件 **4.其他類:切面資訊包裝類/InvocationHandler實現類/建立代理例項類/解析Aspect表示式工具類等。** 參考部落格地址:https://www.iteye.com/blog/jinnianshilongnian