如何简单的实现 AOP

AOP

1. aop 简介

1.1 什么是 aop

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

1.2 aop 综述

AOP

更详细的介绍请参考(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 <T> void before(Class<T> 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 <T> void after(Class<T> 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<T> proxyChain 参数,它存储了 “目标类” 的 “代理链”,其实它泛型参数 T 就是指目标类。
    package top.inotwant.proxy;

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

        /**
         * 链式处理操作
         *
         * @param proxyChain 描述 被代理者 对应的代理链
         */
         <T> Object doProxy(ProxyChain<T> 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<T> targetClass; // 目标类
        private final T targetObject;       // 目标对象
        private final Method method;        // 此次被代理的方法(被代理的最小单元为方法)
        private final MethodProxy methodProxy;  // 所属 cgLib ,由 cgLib 提供,最终由它执行原目标类中的方法
        private final Object[] params;      // 此次被代理的方法的参数

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

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

            this.proxyList = proxyList;
        }

        public Class<T> 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 <T> Object doProxy(ProxyChain<T> proxyChain) {
            Class<T> 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 <T> boolean intercept(Class<T> targetClass, Method method, Object[] params) {
            return true;
        }
        // 重写此以实现 “前置增强”
        protected <T> void before(Class<T> targetClass, Method method, Object[] params) {

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

        }
        // 重写此以实现 “抛出增强”
        protected <T> void error(Class<T> 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> T getProxyInstance(final Class<T> targetClass, final List<Proxy> 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<? extends Annotation> 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<Class<?>, List<Proxy>> proxyMap;
            try {
                proxyMap = getProxyMap();
                for (Map.Entry<Class<?>, List<Proxy>> entry : proxyMap.entrySet()) {
                    Class<?> sourceClass = entry.getKey();
                    List<Proxy> 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<Class<?>, List<Proxy>> getProxyMap() throws Exception {
            // 用于存储 (目标类,代理链),即返回结果
            Map<Class<?>, List<Proxy>> result = new HashMap<>();
            // 获取 AspectProxy 的所有子类
            Set<Class<?>> proxySet = ClassHelper.getSubClassSet(AspectProxy.class);
            for (Class<?> proxyClass : proxySet) {
                // 判断子类是否被 Aspect 修饰,若被修饰说明它是一个 代理类
                if (proxyClass.isAnnotationPresent(Aspect.class)) {
                    Aspect aspect = proxyClass.getAnnotation(Aspect.class);
                    // 获取该代理类对应注解标识(下面将使用该标识获取所有的目标类(或称为被代理类))
                    Class<? extends Annotation> value = aspect.value();
                    if (!value.equals(Aspect.class)) {
                        // 获取代理类对应的所有目标类
                        Set<Class<?>> annotationClassSet = ClassHelper.getAnnotationClassSet(value);
                        // 通过遍历代理类集合,不断生成 (目标类,代理链)映射
                        for (Class<?> sourceClass : annotationClassSet) {
                            if (result.get(sourceClass) == null) {
                                List<Proxy> proxyList = new ArrayList<>();
                                proxyList.add((Proxy) proxyClass.newInstance());
                                result.put(sourceClass, proxyList);
                            } else {
                                result.get(sourceClass).add((Proxy) proxyClass.newInstance());
                            }
                        }
                    }
                }
            }
            return result;
        }
    }

3. 参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值