zoukankan      html  css  js  c++  java
  • 手写AOP实现过程

    一.手写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

  • 相关阅读:
    Flink实例(117):FLINK-SQL应用场景(16)以upsert的方式读写Kafka数据——以Flink1.12为例(二)
    Flink实例(116):FLINK-SQL应用场景(15)以upsert的方式读写Kafka数据——以Flink1.12为例(一)
    数据挖掘实践(17):基础理论(十七)数据挖掘基础(四)模型解释
    数据挖掘实践(16):基础理论(十六)数据挖掘基础(三)特征工程(二)性能度量与评估方法
    rust 可变变量
    Rust学习(32):智能指针-Rc<T>
    rust 高级编程
    rust 所有权
    rust智能指针
    Anbox:容器中的 Android,anboxandroid
  • 原文地址:https://www.cnblogs.com/tc971121/p/13490708.html
Copyright © 2011-2022 走看看