1. 程式人生 > 其它 >自研Spring框架之AOP

自研Spring框架之AOP

技術標籤:手撕原始碼aopspringssm

自研Spring框架之AOP

自研Spring框架第一篇部落格(必讀):自研簡易版Spring框架

自研Spring框架之IoC:自研Spring框架之IoC

完成了Spring容器和SpringIoC之後,我們現在進入到SpringAOP的陣地,開始對SpringAOP來一波猛攻。SpringAOP對應於框架之中的aop包

AOP

Aspect Oriented Programming:面向切面程式設計 Spring實現AOP:JDK動態代理和CGLIB代理 JDK動態代理:其代理物件必須是某個介面的實現,它是通過在執行期間建立一個介面的實現類來完成對目標物件的代理;其核心的兩個類是InvocationHandler和Proxy。 CGLIB代理:實現原理類似於JDK動態代理,只是它在執行期間生成的代理物件是針對目標類擴充套件的子類。CGLIB是高效的程式碼生成包,底層是依靠ASM(開源的java位元組碼編輯類庫)操作位元組碼實現的,效能比JDK強;需要引入包asm.jar和cglib.jar。使用AspectJ注入式切面和@AspectJ註解驅動的切面實際上底層也是通過動態代理實現的。

實現思路

  • 自研框架實現AOP採用CGLIB代理,CGLIB代理支援介面方式也支援非介面方式,且效率比JDK動態代理高。
  • 通過容器獲取所有的切面類
  • 拼接AspectInfoList
  • 遍歷容器中的類 排除AspectClass自身
  • 對符合條件的Aspect進行初步篩選
  • 嘗試進行Aspect的織入

在進行AOP之前需要引入CGLIB的 jar 包用於實現動態代理,引入AspectJ的 jar 包實現對切面語句的解析

<dependency>
	<groupId>org.aspectj</groupId>
	<artifactId>aspectjweaver<
/artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.9</version> </dependency>

程式碼實現

定義註解
(1)切面註解@Aspect

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.
RUNTIME) public @interface Aspect { String pointcut(); }

(2)@Order 控制類的執行順序,值越小優先順序越高

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
   int value();
}

切面類
(1)定義切面類AspectInfo

@AllArgsConstructor
@Getter
public class AspectInfo {
    private int orderIndex;
    private DefaultAspect aspectObject;
    private PointcutLocator pointcutLocator;
}

(2)定義框架支援的advice DefaultAspect

/**
 * @program: simpleframework
 * @description: 定義框架支援的advice
 * @author: 十字街頭的守候
 **/
public abstract class DefaultAspect {
    /**
    * @Description: 事前攔截
    * @Param: targetClass 被代理的目標類
    * @param method 被代理的目標方法
    * @param args 被代理目標方法對應的引數列表
    * @return:
    * @Date: 2021/1/24
    */
   public void before(Class<?> targetClass, Method method, Object[] args)throws Throwable{
   }
    /**
     * @Description:事後攔截
     * @param targetClass: 被代理的目標類
     * @param method: 被代理的目標方法
     * @param arge: 被代理的目標方法對應的引數列表
     * @param returnValue: 被代理的目標方法執行後返回值
     * @return: java.lang.Object
     */
    public Object afterReturning(Class<?> targetClass,Method method,Object[] args,Object returnValue)throws Throwable{
       return returnValue;
    }
    /**
     * @Description:
     * @param targetClass: 被代理的目標類
     * @param method: 被代理的目標方法
     * @param arge: 被代理的目標方法對應的引數列表
     * @param e: 被代理的目標方法丟擲的異常
     * @return: void
     */
    public void afterThrowing(Class<?> targetClass,Method method,Object[] args,Throwable e)throws Throwable{
        
    }
}

核心程式碼

(1)AspectWeaver 完成對切面的織入

public class AspectWeaver {
    private BeanContainer beanContainer;
    public AspectWeaver() {
       this.beanContainer=BeanContainer.getInstance();
    }
    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 proxy = ProxyCreator.createProxy(targetClass, aspectListExecutor);
        beanContainer.addBean(targetClass,proxy);
    }

    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)){
                Aspect aspectTag = aspectClass.getAnnotation(Aspect.class);
                Order orderTag=aspectClass.getAnnotation(Order.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);
    }
}

(2)AspectListExecutor 像被代理類中新增橫切邏輯,其中運用模板方法設計模式

public class AspectListExecutor implements MethodInterceptor {
    //被代理的類
    private Class<?> targetClass;
    @Getter
    private List<AspectInfo> aspectInfoList;

    public AspectListExecutor(Class<?> targetClass, List<AspectInfo> aspectInfoList) {
        this.targetClass = targetClass;
        aspectInfoList = sortedAspectInfoList(aspectInfoList);
        this.aspectInfoList = aspectInfoList;
    }
    /**
     * @Description:按照order的值進行升序排序,確保order值小的aspect先織入
     * @param aspectInfoList:
     * @return: void
     */
       private List<AspectInfo> sortedAspectInfoList(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(aspectInfoList)){
         returnValue=  methodProxy.invokeSuper(proxy,args);
           return returnValue;
       }
        //1、按照orderd的順序升序執行完所有aspect的before方法
        invokeBeforeAdvices(method,args);
       try {
            //2、執行被代理類的方法(用代理物件和被代理方法引數)
             methodProxy.invokeSuper(proxy,args);
           //3、如果被代理方法正常返回,則按照order的降序執行完所有asoect中的afterReturning方法
          returnValue = invokeAfterReturningAdvices(method,args,returnValue);
       }catch (Exception e){
           //4、如果被代理方法丟擲異常,則按照order的順序降序執行完所有Aspect的afterThrowing方法
            invokeAfterThrowingAdvices(method,args,e);
       }
           return returnValue;
    }

    private void collectAccurateMatchedAspectList(Method method) {
        if(ValidationUtil.isEmpty(aspectInfoList)){return ;}
        Iterator<AspectInfo> it=aspectInfoList.iterator();
        while(it.hasNext()){
            AspectInfo aspectInfo=it.next();
            if(!aspectInfo.getPointcutLocator().accurateMatches(method)){
                it.remove();
            }
        }
    }

    //1、按照orderd的順序升序執行完所有aspect的before方法
    private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable {
           for(AspectInfo aspectInfo:aspectInfoList){
               aspectInfo.getAspectObject().before(targetClass,method,args);
           }
    }
    //3、如果被代理方法正常返回,則按照order的降序執行完所有asoect中的afterReturning方法
    @SneakyThrows
    private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable {
       Object result=null;
        for(int i= aspectInfoList.size()-1;i>=0;i--){
           return result = aspectInfoList.get(i).getAspectObject().afterReturning(targetClass,method,args,returnValue);
        }
        return result;
       }
    //4、如果被代理方法丟擲異常,則按照order的順序降序執行完所有Aspect的afterThrowing方法
    private void invokeAfterThrowingAdvices(Method method, Object[] args, Exception e) throws Throwable {
        for(int i= aspectInfoList.size()-1;i>=0;i--){
          aspectInfoList.get(i).getAspectObject().afterThrowing(targetClass,method,args,e);
        }
    }

}

(3)ProxyCreator建立代理物件

public class ProxyCreator {
    /**
     * @Description:建立動態代理物件並返回
     * @param targetClass: 被代理的class物件
     * @param methodInterceptor: 方法攔截器
     * @return: java.lang.Object
     */
    public static Object createProxy(Class<?> targetClass, MethodInterceptor methodInterceptor){
        return Enhancer.create(targetClass,methodInterceptor);
    }
}

(4)PointcutLocator 解析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);
    }
    /**
     * @Description:判斷傳入的class物件是否是Aspect的目標代理類,即匹配Pointcut表示式(初篩)
     * @param targetClass: 目標類
     * @return: boolean
     */
    public boolean roughMatches(Class<?> targetClass){
        //couldMatchJoinPointsInType比較坑,只能效驗within
        //不能效驗(execution,call,get,set),面對無法效驗的表示式,直接返回true
        return pointcutExpression.couldMatchJoinPointsInType(targetClass);
    }
    /**
     * @Description:判斷傳入的Method物件是否是Aspect的目標dialing方法,即匹配Pointcut表示式(精篩選)
     * @param method:
     * @return: boolean
     */
    public boolean accurateMatches(Method method){
        ShadowMatch shadowMatch = pointcutExpression.matchesAdviceExecution(method);
        if(shadowMatch.alwaysMatches()){
            return true;
        }else {
            return false;
        }

    }
}

到此為止,SpringAOP有成功被你攻克了是不是很開心,不要忘了做下測試,看看功能是否真的可以用。在使用自研AOP的時候我們對切面類必須繼承DefaultAspect類,同樣也需要用@Order和@Aspect註解進行標記,在Aspect中需要輸入Aspect表示式。其中對切面表示式的支援是基於AspectJ實現的

注意:自研框架只支援withinexecute 表示式

接下來我們繼續圍攻SpringMVC

自研Spring框架之SpringMVC