自己手寫一個AOP動態代理框架(2)

之前實現的自己手寫一個AOP動態代理框架(1)只能根據控制器,業務層,DAO層等等註解的形式來進行切面,這裏仿Spring,支持AspectJ的表達式進行定位類方法資源,然後進行代理。

集成AspectJ的語法樹,即複用AspecJ的對資源的定位功能。也就是說可以使用 execution(* com.fuyouj.service..*.*(..))這樣的表達式。

AspectJ簡介

提供了完整的AOP實現。
定義了切面語法和切面語法的解析。
Spring支持方法級別的織入(滿足80%的需求了),AspectJ支持全方位多角度的切面織入。AspectJ學習成本高,所以Spring就沒有完整支持所有織入。

導入AspectJ的包

 <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>

AspectJ的織入時機

  1. 編譯時織入:利用ajc編譯器將源文件編譯成class文件,並且將切面邏輯織入到java文件裏面。
  2. 編譯後織入::利用ajc編譯器修改class文件。
  3. 類加載時織入:在類加載器加載對象的時候,將切面織入到代碼裏。JVM裏面最終只有一個類,不同於Spring的方式,JVM裏面其實時多出來了一個代理類。

從以上3點來說,理論上AspectJ的效率比Spring要高,畢竟在使用前已經準備好了。而Spring需要新生成一個代理去替換。
實際上我們的使用必須依賴於Spring的容器,容器沒有初始化成功,我們就無法使用。但是容器初始化完成,必然AOP也已經完成,我們誰又哪能感受得到這個差距呢?

AOP的實現

改造Aspect註解

//作用在類上
@Target(ElementType.TYPE)
//運行時有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
    //刪除
  //  Class<? extends Annotation> value() default DefaultAspect.class;
  //新增
    String pointCut();
}

一個提供解析表示式能力的類

/**
 * 解析Aspect表達式 並且定位被織入的目標
 */
public class PointCutLocator {
  /**
     *AspectJ的  point 解析器
     */
    //支持AspectJ的所有表達式
    private PointcutParser pointcutParser = PointcutParser
            .getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
                    PointcutParser.getAllSupportedPointcutPrimitives()
            );
    /**
     * AspectJ的表達式解析器
     */
    private PointcutExpression pointcutExpression;

    /**
     * 初始化解析表達式
     * @param expression
     */
    public PointCutLocator(String expression){
     pointcutExpression = pointcutParser.parsePointcutExpression(expression);
    }

    /**粗篩
     * @param targetClass
     * @return 是否匹配
     */
   public boolean roughMatches(Class<?> targetClass){
       //只能校驗 withIn表達式 對於不能校驗的 execution表達式,默認返回true  所以這裏是粗篩
      return pointcutExpression.couldMatchJoinPointsInType(targetClass);
   }
    /**精確篩選
     *判斷傳入的 Method對象是否是Aspect的目標代理方法,即精確匹配PointCut
     * @param method
     * @return
     */
   public boolean accurateMatches(Method method){
       ShadowMatch shadowMatch = pointcutExpression.matchesMethodExecution(method);
       return shadowMatch.alwaysMatches();
   }
}

改寫AOP

先說說思路。
1.獲取容器裏面所有的切面類
2.拼接好切面描述類集合
3.遍歷容器的類,爲符合被代理條件的類創建初篩後的集合
4.對每個類嘗試織入,織入的時候精確篩選。

改造AspectInfo,支持複用AspectJ的解析能力。

@Data
public class AspectInfo {
  private int orderIndex;
  private DefaultAspect aspectObject;
  //複用AspectJ
  private PointCutLocator pointCutLocator;
  public AspectInfo() {

  }

  public AspectInfo(int orderIndex, DefaultAspect aspectObject) {
    this.orderIndex = orderIndex;
    this.aspectObject = aspectObject;
  }

  public AspectInfo(int orderIndex, DefaultAspect aspectObject, PointCutLocator pointCutLocator) {
    this.orderIndex = orderIndex;
    this.aspectObject = aspectObject;
    this.pointCutLocator = pointCutLocator;
  }
}
public class AspectWeaver {
 private BeanContainer beanContainer;
    public AspectWeaver(){
        beanContainer = BeanContainer.getInstance();
    }
      public void doAopByAspectJ(){
        //1.獲取所有的切面類
        Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
        //非空判斷
         if (ValidationUtil.isEmpty(aspectSet)){return;}
         //初始化切面描述類集合
         List<AspectInfo> aspectInfoList = packAspectInfoList(aspectSet);
         //遍歷容器裏面所有的類
          Set<Class<?>> classes = beanContainer.getClasses();
           //初篩  判斷這些切面類的是否是爲這個類服務的 留下每個類粗篩後的結果
            List<AspectInfo> roughMatchAspectList =  collectRoughMatchedAspectListForSpecificClass(aspectInfoList,targetClass);
             //對每個類粗篩後的 嘗試織入
            wrapIfNecessary(roughMatchAspectList,targetClass);

然後補充上出現的方法。


    /**拼接切面描述類List
     * @param aspectSet
     * @return
     */
    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和 @Order 和繼承自DefaultAspect,且符合規範");
            }
        }
        return aspectInfoList;
    }
 /**
     * 初篩
     * @param aspectInfoList
     * @param targetClass
     * @return 返回初篩後的列表
     */
    private List<AspectInfo> collectRoughMatchedAspectListForSpecificClass(List<AspectInfo> aspectInfoList, Class<?> targetClass) {
        List<AspectInfo> roughMatchAspectList = new ArrayList<>();
        if (ValidationUtil.isEmpty(aspectInfoList)){return roughMatchAspectList;}
        for (AspectInfo aspectInfo : aspectInfoList) {
            //初篩
            boolean matches = aspectInfo.getPointCutLocator().roughMatches(targetClass);
            //如果匹配 就加入
            if (matches){
                roughMatchAspectList.add(aspectInfo);
            }
        }
        return roughMatchAspectList;
    }
 /**
     * 嘗試織入邏輯
     * @param roughMatchAspectList
     * @param targetClass
     */
    private void wrapIfNecessary(List<AspectInfo> roughMatchAspectList, Class<?> targetClass) {
        if (ValidationUtil.isEmpty(roughMatchAspectList)){
            return;
        }
        //創建動態代理對象
        AspectListExecutor executor = new AspectListExecutor(targetClass,roughMatchAspectList);
        Object proxyBean = ProxyCreator.createProxy(targetClass, executor);
        //替換被代理的對象
        beanContainer.addBean(targetClass,proxyBean);
    }

精確篩選的邏輯放在AspectListExecutor裏面

/**
 * 網被代理的方法添加橫切邏輯
 */
@Data
@NoArgsConstructor
public class AspectListExecutor implements MethodInterceptor {
    //被代理的class
    private Class<?> targetClass;
    //切面信息集合 照顧多個AOP的 情況
    private List<AspectInfo> sortedAspectInfoList;
    public AspectListExecutor(Class<?> targetClass,List<AspectInfo> aspectInfos){
        this.targetClass = targetClass;
        this.sortedAspectInfoList = sort(aspectInfos);
    }

    private List<AspectInfo> sort(List<AspectInfo> aspectInfos) {
        //升序排列
       Collections.sort(aspectInfos, new Comparator<AspectInfo>() {
            @Override
            public int compare(AspectInfo o1, AspectInfo o2) {
                return o1.getOrderIndex() - o2.getOrderIndex();
            }
        });
       return aspectInfos;
    }

    /**
     *
     * @param o 被增強的對象
     * @param method 需要攔截的方法
     * @param args 方法參數
     * @param methodProxy 代理方法
     * @return 返回值
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // 2.0 代碼 精確篩選
        collectAccurateMatchAspectList(method);
        Object returnValue = null;
        if (ValidationUtil.isEmpty(sortedAspectInfoList)){
            //就算爲空也要執行原來的方法啊
            return methodProxy.invokeSuper(o,args);
        }
        //1.按照order的順序執行完畢所有切面的before方法
        invokeBeforeAdvices(method,args);
        try {
            //2。 執行被代理的方法
            returnValue = methodProxy.invokeSuper(o,args);
            //3. 如果被代理方法正常返回,按照order的順序 逆序執行afterReturning
            returnValue =  invokeAfterReturningAdvices(method,args,returnValue);
        }catch (Exception e){
            //執行異常時
            invokeAfterThrowingAdvices(method,args,e);
        }
        return returnValue;
    }

    /**
     * 精確匹配 留下符合精確匹配的結果
     * @param method
     */
    private void collectAccurateMatchAspectList(Method method) {
        if(ValidationUtil.isEmpty(sortedAspectInfoList)){return;}
        Iterator<AspectInfo> iterator = sortedAspectInfoList.iterator();
        while (iterator.hasNext()){
            AspectInfo aspectInfo = iterator.next();
            //區別兩種方式
            //如果註解不爲空 校驗註解
            //精確校驗
            if (aspectInfo.getPointCutLocator().accurateMatches(method) == false){
                iterator.remove();
            }
        }
    }

    /**
     * 發生異常的時候執行 降序
     * @param method
     * @param args
     * @param e
     */
    private void invokeAfterThrowingAdvices(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);
        }
    }

    /**
     * 如果代理方法正常返回,降序執行afterAdvice
     * @param method
     * @param args
     * @param returnValue
     * @return
     */
    private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable {
        Object res = null;
        for (int i = sortedAspectInfoList.size()-1; i >=0 ; i--) {
           res =  sortedAspectInfoList.get(i).getAspectObject().afterReturning(targetClass, method, args, returnValue);
        }
        return res;
    }

    /**
     * 按照oder的 順序升序執行
     * @param method
     * @param args
     */
    private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable {
        for (AspectInfo info : sortedAspectInfoList) {
            info.getAspectObject().before(targetClass,method,args);
        }
    }
}

改寫原來的測試類

package com.fuyouj.aspect.test;

import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;

/**
 * @Desc
 * @Author FuYouJ
 * @date 2020/6/2 0:40
 */
@Aspect(pointCut = "execution(* com.fuyouj.controller..*.*(..))")
@Order(0)
@Slf4j
public class TestAopOne extends DefaultAspect {
    @Override
    public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
       log.info("我是切面邏輯before order0,我增強的目標類是{},我增強的目標方法是{},方法參數{}"
        ,targetClass.getSimpleName(),method.getName(),args);
    }

    @Override
    public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
        Integer res = (Integer) returnValue;
        log.info("我是切面邏輯after order0,我增強的目標類是{},我增強的目標方法是{},方法參數{}"
                ,targetClass.getSimpleName(),method.getName(),args);
        res++;
        return res;
    }
}
package com.fuyouj.aspect.test;

import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;

/**
 * @Desc
 * @Author FuYouJ
 * @date 2020/6/2 0:40
 */
@Aspect(pointCut = "execution(* com.fuyouj.controller..*.*(..))")
@Order(1)
@Slf4j
public class TestAopTwo extends DefaultAspect {
    @Override
    public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
       log.info("我是切面邏輯before order1,我增強的目標類是{},我增強的目標方法是,方法參數{}"
        ,targetClass.getSimpleName(),method.getName(),args);
    }

    @Override
    public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
        Integer res = (Integer) returnValue;
        log.info("我是切面邏輯after order1,我增強的目標類是{},我增強的目標方法是,方法參數{}"
                ,targetClass.getSimpleName(),method.getName(),args);
        res++;
        return res;
    }
}

測試用例

public class TestAop {
    @DisplayName("測試第二個版本的AOP")
    @Test
    public void testAopOne(){
        BeanContainer beanContainer = BeanContainer.getInstance();
        //加載了所有類
        beanContainer.loadBeans("com.fuyouj");
        //AOP
        new AspectWeaver().doAopByAspectJ();
        // ioc
        new DependencyInjector().doIoc();
        TestAopController controller = (TestAopController) beanContainer.getBean(TestAopController.class);
        int say = controller.say();
        System.out.println("======================================返回結果======="+say+"======");
    }
}

在這裏插入圖片描述

再改進

可以將DefaultAspect這個抽象類改造成接口,因爲java只能繼承一個類,爲了減輕實現的壓力。使用 Default關鍵字。代碼是一樣可以運行的。

package com.framework.aop.aspect;

/**
 * @Desc
 * @Author FuYouJ
 * @date 2020/5/31 21:24
 */

import java.lang.reflect.Method;

/**
 * 定義切面的通用骨架
 * 通過鉤子方法實現
 */
public interface  DefaultAspect {
    /**
     * @param targetClass 被代理的目標類型
     * @param method 被代理的方法
     * @param args 方法的參數
     * @throws Throwable
     */
    default void before(Class<?> targetClass, Method method,Object[] args) throws Throwable{

    } ;

    /**
     *
     * @param targetClass 被代理的目標類型
     * @param method 被代理的方法
     * @param args 方法的參數
     * @param returnValue 返回值
     * @return
     * @throws Throwable
     */
   default  Object afterReturning(Class<?> targetClass,Method method
            ,Object[] args,Object returnValue) throws Throwable{
       return  returnValue;
   }

    /**
     *
     * @param targetClass 被代理的目標類型
     * @param method 被代理的方法
     * @param args 方法的參數
     * @param e 異常
     * @throws Throwable
     */
    default void afterThrowing(Class<?> targetClass,
                              Method method,Object[] args,Throwable e) throws Throwable{

    }
}

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章