如何简单的实现 AOP

AOP

1. aop 简介

1.1 什么是 aop

AOP (Aspect Oriented Programming,面向切面编程),可以在运行时动态地将代码切入到类中指定方法、指定位置上的一种技术。说白了,就是把 横切逻辑业务逻辑 中抽离出来。哪些属于 横切逻辑 呢?比如,性能监控、日志、事务管理、权限控制等等。

1.2 aop 综述

如何简单的实现 AOP_第1张图片

更详细的介绍请参考(Spring AOP 实现原理)。

1.3 本文目标

依赖 CGLib 实现一个简单 aop 。这里假设大家已经了解了 动态代理(若不清楚,请自行 Google ,其实挺简单的)。

我们的目标是使得下面的代码可行,它可以拦截 Controller 注解修饰的类,为类中方法提供增强逻辑(比如,下面可统计方法执行时间)。

说白了,aop 的终极目标就是:使用 代理对象 替换 目标对象(或称为被代理对象)。代理对象是代理类的实例,它的方法中含有我们的横切逻辑和业务逻辑,代理类是使用 CGLib 动态生成的。我们可以用 map 来存储 “代理对象”,每个 “代理对象” 对应的 key 为 “目标类”。

【可能已经被我绕晕了 +_+ !OK,我的目的达到了!没事,继续往下看,最后你会发现这些也就那么回事~】

    // ControllerAspect.java

    /**
     * 拦截 controller 的所有方法
     */

    @Aspect(Controller.class)
    public class ControllerAspect extends AspectProxy {

        private final static Logger LOGGER = LoggerFactory.getLogger(ControllerAspect.class);
        private long beginTime;

        // 前置增强
        @Override
        protected  void before(Class targetClass, Method method, Object[] params) {
            LOGGER.debug("------ begin ------");
            LOGGER.debug(String.format("class :: %s", targetClass.getName()));
            LOGGER.debug(String.format("method :: %s", method.getName()));
            beginTime = System.currentTimeMillis();
        }

        // 后置增强
        @Override
        protected  void after(Class targetClass, Method method, Object[] params) {
            LOGGER.debug(String.format("time :: %dms", System.currentTimeMillis() - beginTime));
            LOGGER.debug("------  end  ------");
        }
    }

2. aop 实现

2.1 实现要点

  1. 在实际应用中我们往往有多个横切需求(比如,既要日志输出又要权限管理),这就需要我们的 aop 支持 链式代理,那么我们该怎样实现呢?
  2. 到后面我们会发现我们很容易得到 (代理类,被代理类) 的映射关系(我们可以用 map 存储这个关系),而我们的目标是要得到 (目标类,代理对象) 这一映射。怎么由前者转换为后者呢?

2.2 详细实现

1)实现链式代理

【1】首先创建一个代理接口,所有的代理类都直接或间接实现它。其中只有 doProxy() 方法,它配合 ProxyChain 类中的 doProxyChain() 方法实现 链式代理。注意 ProxyChain proxyChain 参数,它存储了 “目标类” 的 “代理链”,其实它泛型参数 T 就是指目标类。
    package top.inotwant.proxy;

    /**
     * 代理接口
     */
    public interface Proxy {

        /**
         * 链式处理操作
         *
         * @param proxyChain 描述 被代理者 对应的代理链
         */
          Object doProxy(ProxyChain proxyChain);
    }
【2】构造 ProxyChain,它总体上描述被代理者(目标类)对应的代理链(代理类集合)。注意,代理的最小单元是方法。所以在类中存在 methodmethodProxy 这两个属性。更精确地说,一个 ProxyChain 实例,封装一个具体方法的代理过程。
    package top.inotwant.proxy;

    import net.sf.cglib.proxy.MethodProxy;

    import java.lang.reflect.Method;
    import java.util.List;

    /**
     * 描述 被代理者 对应的代理链
     */
    public class ProxyChain<T> {

        private final Class targetClass; // 目标类
        private final T targetObject;       // 目标对象
        private final Method method;        // 此次被代理的方法(被代理的最小单元为方法)
        private final MethodProxy methodProxy;  // 所属 cgLib ,由 cgLib 提供,最终由它执行原目标类中的方法
        private final Object[] params;      // 此次被代理的方法的参数

        private List proxyList;      // 代理链
        private int index = 0;              // index 指示将要执行的 “增强(或称为‘横切逻辑’)”

        public ProxyChain(Class targetClass, T targetObject, Method method, MethodProxy methodProxy, Object[] params, List proxyList) {
            this.targetClass = targetClass;
            this.targetObject = targetObject;
            this.method = method;
            this.methodProxy = methodProxy;
            this.params = params;

            this.proxyList = proxyList;
        }

        public Class getTargetClass() {
            return targetClass;
        }

        public Method getMethod() {
            return method;
        }

        public MethodProxy getMethodProxy() {
            return methodProxy;
        }

        public Object[] getParams() {
            return params;
        }

        public T getTargetObject() {
            return targetObject;
        }

        /**
         * 配合 doProxy() 以及利用 index 实现 “链式代理”
         */
        public Object doProxyChain() throws Throwable {
            Object result;
            if (this.index >= proxyList.size()) {
                result = methodProxy.invokeSuper(targetObject, params);
                this.index = 0; // TODO 自己添加,为了实现 链式代理 的多次调用
            } else {
                result = proxyList.get(this.index++).doProxy(this);
            }
            return result;
        }
    }
【3】接下来,我们创建一个 Proxy 的模板类 AspectProxy(很容易理解它为什么被称为模板类)。其中,doProxy() 方法很重要,它与上面的 doProxyChain() 搭配实现链式代理。建议,画一个调用栈模拟一下代理过程。当我们搞清楚后,会发现这个结构很巧妙!
    package top.inotwant.proxy;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import java.lang.reflect.Method;

    /**
     * Proxy 的 “模板类”
     */
    public abstract class AspectProxy implements Proxy {

        private final static Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class);

        @Override
        public  Object doProxy(ProxyChain proxyChain) {
            Class targetClass = proxyChain.getTargetClass();
            Method method = proxyChain.getMethod();
            Object[] params = proxyChain.getParams();
            Object result;
            try {
                if (intercept(targetClass, method, params)) {   // 拦截条件
                    before(targetClass, method, params);    // 前置增强
                    result = proxyChain.doProxyChain();     // 此处很重要,用于实现链式代理
                    after(targetClass, method, params);     // 后置增强
                } else {
                    result = proxyChain.doProxyChain();
                }
            } catch (Throwable e) { // 这里处理了 doProxyChain() 抛出的异常
                error(targetClass, method, params);
                LOGGER.error("aspect proxy fail", e);
                throw new RuntimeException(e);
            } finally {
                end();
            }

            return result;
        }
        // 重写此以实现 “拦截条件”
        protected  boolean intercept(Class targetClass, Method method, Object[] params) {
            return true;
        }
        // 重写此以实现 “前置增强”
        protected  void before(Class targetClass, Method method, Object[] params) {

        }
        // 重写此以实现 “后置增强”
        protected  void after(Class targetClass, Method method, Object[] params) {

        }
        // 重写此以实现 “抛出增强”
        protected  void error(Class targetClass, Method method, Object[] params) {

        }
        // 重写此以实现 "结束增强"
        protected void end() {

        }

    }
【4】假如现在我们已经有了 “目标类” 和 “代理链(加在该目标类上的所有代理,或称为代理类集合)”,那么我们如何结合上面三个类来获取 “代理对象” 呢?这就需要 cgLib 的支持了,我们创建了下面这个类封装了这个过程。阅读后你会发现: ProxyChain 的构造方法所需的大部分参数,cgLib 都给提供了。实际上我们是结合了 cgLib 后才创建的 ProxyChain

我们顺一下代理过程:现在我们已拿到了代理对象,用代理对象调用原目标类的某一方法时,cgLib 会调用 MethodInterceptorintercept() 方法(见下面,Enhancer.create() 方法的第二个参数,这里使用了匿名类)。调用时,cgLib 自然会把参数准备好,这些参数描述了要代理的单元。然后,正如 intercept() 方法中所描述的,我们先创建一个 ProxyChain 的实例。接着调用该实例中的 doProxyChain() 方法,该方法中描述的就是上面用调用栈模拟的过程(具体的链式代理过程)。最后返回原方法的执行结果。

    package top.inotwant.proxy;

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;

    import java.lang.reflect.Method;
    import java.util.List;

    public class ProxyManager {

        /**
         * 获取代理对象
         * @param targetClass 目标类
         * @param proxyList 代理链
         * @return 代理对象
         */
        @SuppressWarnings("unchecked")
        public static  T getProxyInstance(final Class targetClass, final List proxyList) {
            return (T) Enhancer.create(targetClass, new MethodInterceptor() {
                @Override
                public Object intercept(Object targetObject, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
                    return new ProxyChain<>(targetClass, (T) targetObject, method, methodProxy, params, proxyList).doProxyChain();
                }
            });
        }
    }

2)实现映射转换

【1】创建注解 Aspect ,作用于代理类上,用于描述该代理类作用于哪些目标类(用 value 指定)
    package top.inotwant.annocation;

    import java.lang.annotation.*;

    /**
     * AOP 的 切面 注解
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Aspect {

        Class value();

    }
【2】上面已经提到,要想生成 “代理对象” 需要 “目标类” 和 “代理链”(使用 ProxyManager 中的 getProxyInstance() 获取代理对象)。所以下面的 getProxyMap() 就是为了产生(目标类,代理链) 映射。然后,只需在 static 块中使用 “代理对象” 替换 “被代理对象” 即可。后面要使用目标类的对象时,只需要在 BeanHelper 获取即可(获取的对象为 “代理对象” 它的方法中包含了横切逻辑)。
    package top.inotwant.helper;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import top.inotwant.annocation.Aspect;
    import top.inotwant.proxy.AspectProxy;
    import top.inotwant.proxy.Proxy;
    import top.inotwant.proxy.ProxyManager;
    import top.inotwant.proxy.TransactionProxy;

    import java.lang.annotation.Annotation;
    import java.util.*;

    /**
     * AOP 实现类
     */
    public final class AopHelper {

        private final static Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);

        static {
            LOGGER.warn("======================AOP HELPER=========================");

            Map, List> proxyMap;
            try {
                proxyMap = getProxyMap();
                for (Map.Entry, List> entry : proxyMap.entrySet()) {
                    Class sourceClass = entry.getKey();
                    List proxyList = entry.getValue();
                    Object proxyInstance = ProxyManager.getProxyInstance(sourceClass, proxyList);
                    // 使用 “代理对象” 替换 “被代理对象”
                    BeanHelper.putBean(sourceClass, proxyInstance);
                }
            } catch (Exception e) {
                LOGGER.error("aop helper fail", e);
                throw new RuntimeException(e);
            }
        }

        /**
         * 生成 (被代理类,代理类集(或称为代理链)) 映射
         */
        public static Map, List> getProxyMap() throws Exception {
            // 用于存储 (目标类,代理链),即返回结果
            Map, List> result = new HashMap<>();
            // 获取 AspectProxy 的所有子类
            Set> proxySet = ClassHelper.getSubClassSet(AspectProxy.class);
            for (Class proxyClass : proxySet) {
                // 判断子类是否被 Aspect 修饰,若被修饰说明它是一个 代理类
                if (proxyClass.isAnnotationPresent(Aspect.class)) {
                    Aspect aspect = proxyClass.getAnnotation(Aspect.class);
                    // 获取该代理类对应注解标识(下面将使用该标识获取所有的目标类(或称为被代理类))
                    Class value = aspect.value();
                    if (!value.equals(Aspect.class)) {
                        // 获取代理类对应的所有目标类
                        Set> annotationClassSet = ClassHelper.getAnnotationClassSet(value);
                        // 通过遍历代理类集合,不断生成 (目标类,代理链)映射
                        for (Class sourceClass : annotationClassSet) {
                            if (result.get(sourceClass) == null) {
                                List proxyList = new ArrayList<>();
                                proxyList.add((Proxy) proxyClass.newInstance());
                                result.put(sourceClass, proxyList);
                            } else {
                                result.get(sourceClass).add((Proxy) proxyClass.newInstance());
                            }
                        }
                    }
                }
            }
            return result;
        }
    }

3. 参考

  • 源码
  • 架构探险(黄勇)

你可能感兴趣的:(Design,Beauty)