手寫AOP實現過程

張天賜的部落格發表於2020-08-12
一.手寫Aop前基礎知識

1.aop是什麼?

面向切面程式設計(AOP):是一種程式設計正規化,提供從另一個角度來考慮程式結構從而完善物件導向程式設計(OOP)。

在進行OOP開發時,都是基於對元件(比如類)進行開發,然後對元件進行組合,OOP最大問題就是無法解耦元件進行開發,而AOP就是為了克服這個問題而出現的,它來進行這種耦合的分離。

​ AOP為開發者提供一種進行橫切關注點(比如日誌關注點橫切了支付關注點)分離並織入的機制,把橫切關注點分離,然後通過某種技術織入到系統中,從而無耦合的完成了我們的功能。

2.aop能幹什麼?

AOP主要用於橫切關注點和織入,因此需要理解橫切關注點和織入:

  • 關注點:可以認為是所關注的任何東西,比如上邊的支付元件;
  • 關注點分離:將問題細化從而單獨部分,即可以理解為不可再分割的元件,如上邊的日誌元件和支付元件;
  • 橫切關注點:一個元件無法完成需要的功能,需要其他元件協作完成,如日誌元件橫切於支付元件;
  • 織入:橫切關注點分離後,需要通過某種技術將橫切關注點融合到系統中從而完成需要的功能,因此需要織入,織入可能在編譯期、載入期、執行期等進行。

橫切關注點可能包含很多,比如非業務的:日誌、事務處理、快取、效能統計、許可權控制等等這些非業務的基礎功能;還可能是業務的:如某個業務元件橫切於多個模組。

面向切面方式,先將橫切關注點分離,再將橫切關注點織入到支付系統中:

3.aop的優點?

  • 完善OOP;
  • 降低元件和模組之間的耦合性;
  • 使系統容易擴充套件;
  • 而且由於關注點分離從而可以獲得元件的更好複用。
二.aop底層實現原理:代理模式

關於靜態代理模式,詳情請參閱我另一片部落格:

https://www.cnblogs.com/tc971121/p/13474638.html

三.手寫aop主要實現過程

1.定義配置標記

@Aspect:配置標記橫切物件(方法)的地址

@Order:配置標記橫切的順序

@Aspect/@Order

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
    String pointcut();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
    /**
     * 控制類的執行順序,值越小優先順序越高
     */
    int value();
}

2.定義預設切面類

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對應所對應的例項物件

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<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
        if(ValidationUtil.isEmpty(aspectSet)){return;}
        //2.拼裝AspectInfoList
        List<AspectInfo> aspectInfoList = packAspectInfoList(aspectSet);
        //3.遍歷容器裡的類
        Set<Class<?>> classSet = beanContainer.getClasses();
        for (Class<?> targetClass: classSet) {
            //排除AspectClass自身
            if(targetClass.isAnnotationPresent(Aspect.class)){
                continue;
            }
            //4.粗篩符合條件的Aspect
            List<AspectInfo> roughMatchedAspectList  = collectRoughMatchedAspectListForSpecificClass(aspectInfoList, targetClass);
            //5.嘗試進行Aspect的織入
            wrapIfNecessary(roughMatchedAspectList,targetClass);
        }

    }

    private void wrapIfNecessary(List<AspectInfo> 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<AspectInfo> collectRoughMatchedAspectListForSpecificClass(List<AspectInfo> aspectInfoList, Class<?> targetClass) {
        List<AspectInfo> roughMatchedAspectList = new ArrayList<>();
        for(AspectInfo aspectInfo : aspectInfoList){
            //粗篩
            if(aspectInfo.getPointcutLocator().roughMatches(targetClass)){
                roughMatchedAspectList.add(aspectInfo);
            }
        }
        return roughMatchedAspectList;
    }

    private List<AspectInfo> packAspectInfoList(Set<Class<?>> aspectSet) {
        List<AspectInfo> 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);
    }
}

切面資訊包裝類(增強的動作/增強物件地址/橫切順序)

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介面)

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 List<AspectInfo>sortedAspectInfoList;

    public AspectListExecutor(Class<?> targetClass, List<AspectInfo> aspectInfoList){
        this.targetClass = targetClass;
        this.sortedAspectInfoList = sortAspectInfoList(aspectInfoList);
    }
    /**
     * 按照order的值進行升序排序,確保order值小的aspect先被織入
     *
     * @param aspectInfoList
     * @return
     */
    private List<AspectInfo> sortAspectInfoList(List<AspectInfo> aspectInfoList) {
        Collections.sort(aspectInfoList, new Comparator<AspectInfo>() {
            @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<AspectInfo> 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);
        }
    }
}

建立代理類:

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表示式並且定位被織入的目標工具類:

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-1418596

相關文章