个人笔记程序员

使用代理链自己实现AOP框架

2018-02-25  本文已影响12人  fanyank

写在前面

今年寒假已经接近尾声,寒假给自己定下的目标--完成一个简单的MVC框架也已经快完成了,在实现一些进阶的功能,比如AOP时,逐渐有了自己的理解,特此写下,帮助自己日后复习。
本文的代码参考 <<架构探险 从零开始写javaweb框架>> 这本书。

完整代码请移步我的 github

框架功能

使用注解形式对切面进行标记,支持前置增强,后置增强,抛出增强,不支持类似于AspectJ切点表达式。

框架结构

├── helper
│   └── AopHelper.java
├── proxy
│   ├── AspectProxy.java
│   ├── Proxy.java
│   ├── ProxyChain.java
│   └── ProxyManager.java

如上图框架图所示,在proxy包中分别有一下四个类:

对代理链的理解

  1. 我们首先从 ProxyManager.createProxy()方法入手,该方法的目的就是创建代理类,使用CGLib框架创建代理类时,需要传入一个callback,这个callback会拦截目标类原方法的执行,转而执行intercept()方法。

  2. 进一步查看intercept()方法,该方法创建了一个ProxyChain()对象并调用了doProxyChain()方法,查看ProxyChain代码可知,当我们拿到一个目标类代理链时(即如下代码中的proxyList对象),判断代理链中的切面是否全部执行完毕,如果没有执行完毕,则执行切面中的doProxy()方法。

public Object doProxyChain() throws Throwable {
      Object methodResult;
      if(proxyIndex < proxyList.size()) {
          //调用代理方法
          methodResult = proxyList.get(proxyIndex++).doProxy(this);
      } else {
          //调用原方法
          methodResult = methodProxy.invokeSuper(targetObject,methodParams);
      }
      return methodResult;
  }
  1. 查看切面类中的doProxy()方法,发现不管有没有符合切点定义,都会执行proxyChain.doProxyChain()方法。
if(intercept(targetClass,method,params)) {
              before(targetClass,method,params);
              result = proxyChain.doProxyChain();
              after(targetClass,method,params,result);
          } else {
              result = proxyChain.doProxyChain();
          }

举例子来说,假如我们代理链中有两个切面A和B,首先执行切面A的doProxy(),执行完前置增强(假如没有定义切点,默认对所有方法进行拦截)后,执行proxyChain.doProxyChain()方法,这时代理链中还有一个切面B,继续执行切面B的增强,切面B完成前置之后,仍然调用proxyChain.doProxyChain()方法,这时代理链中所有切面都执行了一遍,开始执行目标类的原方法(即doProxyChain()的else分支),然后依次执行各个切面的后置增强。

总之,执行过程就是等待所有切面的所有前置增强执行完毕之后执行目标类的原方法,原方法执行完毕之后,依次执行切面的后置增强。

框架的初始化

上文为我们介绍了框架是如何进行代理的,简单来说就是拿着一个目标类和其对应的代理链,然后使用CGLib框架为我们提供的方法进行代理。

那么初始化的工作就是返回一个目标类和代理链的映射。

我们将这块工作交个 AOPHelper 来完成。

以下为AOPHelper类的声明:

public final class AopHelper {
    static {
        try {
            Map<Class<?>,Set<Class<?>>> proxyMap = createProxyMap();
            Map<Class<?>,List<Proxy>> targetMap = createTargetMap(proxyMap);
            for(Map.Entry<Class<?>,List<Proxy>> targetEntry : targetMap.entrySet()) {
                Class<?> targetClass = targetEntry.getKey();
                List<Proxy> proxyList = targetEntry.getValue();
                //通过CGLib框架生成代理类
                Object proxy = ProxyManager.createProxy(targetClass,proxyList);
                //使用代理类替换容器中的普通类
                BeanHelper.setBean(targetClass,proxy);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    /**
     * 返回目标类的集合,根据@Aspect中的注解查找目标类
     * @param aspect
     * @return
     */
    private static Set<Class<?>> createTargetClassSet(Aspect aspect) {
        Set<Class<?>> targetClassSet = new HashSet<>();
        //获取Aspect注解中的值(被代理的类)
        Class<? extends Annotation> annotation = aspect.value();
        if(annotation != null && !annotation.equals(Aspect.class)) {
            //获取被代理的类的集合
            targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));
        }
        return targetClassSet;
    }

    /**
     * 返回切面类和目标类的映射关系
     * key->继承自AspectProxy的类(切面),value->目标类集合
     * @return
     */
    private static Map<Class<?>,Set<Class<?>>> createProxyMap() {
        Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<>();
        Set<Class<?>> classSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
        for(Class<?> proxyClass : classSet) {
            if(proxyClass.isAnnotationPresent(Aspect.class)) {
                Aspect aspect = proxyClass.getAnnotation(Aspect.class);
                Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
                //key->切面类,value->切面要切入的类的集合(目标类的集合)
                proxyMap.put(proxyClass,targetClassSet);
            }
        }
        return proxyMap;
    }

    /**
     * 返回目标类和代理类的映射关系,一个目标类可能由多个代理类
     * key->目标类,value->代理类的集合
     * @param proxyMap
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap) throws IllegalAccessException, InstantiationException {
        Map<Class<?>,List<Proxy>> targetMap = new HashMap<>();
        for(Map.Entry<Class<?>,Set<Class<?>>> proxyEntry : proxyMap.entrySet()) {
            //切面类
            Class<?> proxyClass = proxyEntry.getKey();
            //目标类的集合
            Set<Class<?>> targetClassSet = proxyEntry.getValue();
            for(Class<?> targetClass : targetClassSet) {
                Proxy proxy = (Proxy) proxyClass.newInstance();
                if(targetMap.containsKey(targetClass)) {
                    //targetMap中含有目标类,直接增加proxy至proxyList中
                    targetMap.get(targetClass).add(proxy);
                } else {
                    //targetMap中没有目标类,新建proxyList,并将proxy添加至proxyList中
                    List<Proxy> proxyList = new ArrayList<>();
                    proxyList.add(proxy);
                    targetMap.put(targetClass,proxyList);
                }
            }
        }
        return targetMap;
    }

}

然后将AopHelper设置为随着tomcat的启动而加载即可。
如上代码注解较为详细,这里不再详细讲解。

框架的使用

框架的使用很简单,只需定义我们想要的切面和要代理的目标类即可。如下为自定义切面示例。

//代理所有被@Controller注解标记的类
@Aspect(Controller.class)
public class ControllerAspect extends AspectProxy {
    private static final Logger logger = LoggerFactory.getLogger(ControllerAspect.class);

    private long begin;
    public void before(Class<?> cls, Method method,Object[] params) {
        logger.debug("--------------");
        logger.debug(cls.getName());
        logger.debug(method.getName());
        begin = System.currentTimeMillis();
    }

    public void after(Class<?> cls,Method method,Object[] params,Object result) {
        logger.debug("time : " + (System.currentTimeMillis() - begin));
        logger.debug("-----end------");
    }

    public boolean intercept(Class<?> cls,Method method,Object[] params) {
        //对controller类中的所有被@Action注解标注的方法进行拦截
        if(method.isAnnotationPresent(Action.class)) {
            return true;
        }
        return false;
    }
}
上一篇下一篇

猜你喜欢

热点阅读