美文网首页
《架构探险-从零开始写Java Web框架》学习笔记2

《架构探险-从零开始写Java Web框架》学习笔记2

作者: 抬头挺胸才算活着 | 来源:发表于2019-12-02 20:32 被阅读0次

参考资料:
[1]. wukong0503/smart4j
[2]. java动态代理实现
[3]. ThreadLocal与弱引用WeakReference

第4章:使框架具备AOP特性

  1. AOP实现
  2. ThreadLocal
  3. 事务
  4. 事务的实现
  • AOP实现
    前面介绍了java动态代理的实现和spring基于配置和注解两种方式的配置(这部分没有什么营养)

AOP实现建议按照我下面的思路来看,不然一开始可能有点晕。

下面这段代码就从proxyMap中生成targetMap,这段代码不涉及AOP的操作,只是把一个Map的键值反转为另外一个键值的Map。
proxyMap:key为代理类,value为目标类(被代理类)的集合
targetMap:key为目标类,value为代理类的集合

/**
 * 把class和增强它的切面实例进行关联
 * @param proxyMap 切面对应需要增强的class集合
 * @return
 * @throws Exception
 */
private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
    Map<Class<?>, List<Proxy>> targetMap = new HashMap<Class<?>, List<Proxy>>();
    for (Map.Entry<Class<?>, Set<Class<?>>> proxyEntry : proxyMap.entrySet()) {
        // 获取切面class
        Class<?> proxyClass = proxyEntry.getKey();
        // 切面所需要增强的class对象(也就是我们需要把该切面在哪些实例上增强的对象class)
        Set<Class<?>> targetClassSet = proxyEntry.getValue();
        for (Class<?> targetClass : targetClassSet) {
            Proxy proxy = (Proxy) proxyClass.newInstance();
            if (targetMap.containsKey(targetClass)) {
                targetMap.get(targetClass).add(proxy);
            } else {
                List<Proxy> proxyList = new ArrayList<Proxy>();
                proxyList.add(proxy);
                targetMap.put(targetClass, proxyList);
            }
        }
    }
    return targetMap;
}

AopHelper的静态代码块,AOP的功能随着AopHelper的加载而开始初始化,所以下面的代码是AOP功能的入口。
不考虑细节,我们大概可以知道下面的代码根据注解找到目标类和代理类之间的映射关系targetMap之后,为每个目标类调用ProxyManager.createProxy创建代理类之后放入Bean容器中。

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();
            Object proxy = ProxyManager.createProxy(targetClass, proxyList);
            BeanHelper.setBean(targetClass, proxy);
        }
    } catch (Exception e) {
        LOGGER.error("aop failure", e);
    }
}

接下来我们再来看createProxyMap,其中的addAspectProxy中首先获取获取所有继承AspectProxy的类,也就是代理类,因为代理类必须继承AspectProxy且带有Aspect注解,然后对每个代理类获取其Aspect注解,再通过createTargetClassSet扫描全部类,如果类的注解有Aspect.value对应的注解都加入到集合中来,就形成了代理类和目标类集合的映射,也就是ProxyMap。

    /**
     * 创建需要被代理的所有class对象
     * @return
     * @throws Exception
     */
    private static Map<Class<?>, Set<Class<?>>> createProxyMap() throws Exception {
        Map<Class<?>, Set<Class<?>>> proxyMap = new HashMap<Class<?>, Set<Class<?>>>();
        addAspectProxy(proxyMap);
        addTransactionProxy(proxyMap);
        return proxyMap;
    }

    /**
     * 关联普通界面代理类
     * @param proxyMap
     * @throws Exception
     */
    private static void addAspectProxy(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
        // 获取所有的切面代理类的子类
        Set<Class<?>> proxyClassSet = ClassHelper.getClassSetBySuper(AspectProxy.class);
        for (Class<?> proxyClass : proxyClassSet) {
            // 来判断是否使用了某个注解
            if (proxyClass.isAnnotationPresent(Aspect.class)) {
                Aspect aspect = proxyClass.getAnnotation(Aspect.class);
                Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
                proxyMap.put(proxyClass, targetClassSet);
            }
        }
    }

    /**
     * 关联事务代理
     * @param proxyMap
     * @throws Exception
     */
    private static void addTransactionProxy(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
        // 获取所有service类,这里是简单的处理,默认把services注解标识的类,都拿过来换成事务代理对象,
        // 然后在代理执行的时候 会根据是否有Transaction注解,而去对事务进行操作
        Set<Class<?>> servicesProxySet = ClassHelper.getClassSetByAnnotation(Service.class);
        proxyMap.put(TransactionProxy.class, servicesProxySet);
    }

当我们调用的时候发生了什么?
首先我们的目标类已经在前面被替换为代理类,代理类的处理如下,所有对代理类的调用都会进入intercept这个函数,在这个函数中new了一个ProxyChain,这个ProxyChain包含了目标类及其对应的代理类,然后开始顺序调用代理类直至目标类的方法,下面再仔细讲解下这部分,这部分感觉有点意思。

/**
 * 代理管理器,用来创建代理对象(切面类调用)
 * @since 2017-07-11.
 */
public class ProxyManager {

    /**
     * 创建代理
     * @param targetClass
     * @param proxyList
     * @param <T>
     * @return
     */
    public static <T> T createProxy(final Class<T> targetClass, final List<Proxy> proxyList) {
        // 使用CGLib提供的Enhance#create方法来创建代理对象
        // 将intercept的参数传入ProxyChain的构造器中
        return (T) Enhancer.create(targetClass, new MethodInterceptor() {
            public Object intercept(Object targetObj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                return new ProxyChain(targetClass, targetObj, method, methodProxy, objects, proxyList).doProxyChain();
            }
        });
    }
}

这里实际上采用了职责链设计模式,在一个代理类调用完会自动调用下一个代理类,methodResult = proxyList.get(proxyIndex++).doProxy(this);传入的this是ProxyChain,代理类运行完直接的代码就帮ProxyChain调用下一个对象。另外这里用数组存放着职责链的成员,而一半的设计是用链表将他们串联起来。

public class ProxyChain {
//...
    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;
    }
}

接下来进入到代理类的doProxy函数中,可以看到先调用的代理类先执行before函数,但after函数确实比较慢执行。


/**
 * 切面代理, 提供一个模板方法,并在该抽象类的具体实现中扩展相应的抽象方法
 * @since 2017-07-11.
 */
public abstract class AspectProxy implements Proxy {
    private static final Logger LOGGER = LoggerFactory.getLogger(AspectProxy.class);

    @Override
    public Object doProxy(ProxyChain proxyChain) throws Throwable {
        Object result = null;

        Class<?> targetClass = proxyChain.getTargetClass();
        Method targetMethod = proxyChain.getTargetMethod();
        Object[] methodParams = proxyChain.getMethodParams();
        begin();
        try {
            if (intercept(targetClass, targetMethod, methodParams)) {
                before(targetClass, targetMethod, methodParams);
                result = proxyChain.doProxyChain();
                after(targetClass, targetMethod, methodParams);
            } else {
                result = proxyChain.doProxyChain();
            }
        } catch (Exception e) {
            LOGGER.error("proxy fail", e);
            error(targetClass, targetMethod, methodParams, e);
        }
        end();
        return result;
    }

    public void begin() {}

    // 拦截方法
    public boolean intercept(Class<?> clz, Method method, Object[] params) throws Throwable {
        return true;
    }

    public void before(Class<?> clz, Method method, Object[] params) throws Throwable {

    }

    public void after(Class<?> clz, Method method, Object[] params) throws Throwable {

    }

    // 执行代理所抛出的异常
    public void error(Class<?> clz, Method method, Object[] params, Throwable e) {

    }

    public void end() {}
}
  • ThreadLocal
    不知道为什么这里突然就讲到ThreadLocal,可以参考下我之前写的文章[3],作者写的貌似也有误导性,虽然内部实现确实是Map,但是是每个线程一个Map,以变量地址为key得到变量的值。作者自己实现了一个ThreadLocal实际上每种类型只能存放一个变量,不能存放多个变量。
    然后作者就举了一个用static存放数据库连接导致在多线程的时候冲突的bug,最后用ThreadLocal解决了,也就是没线程单独一个连接。

  • 事务





  • 事务的实现
    定义事务的注解如下,只能用在方法上。

/**
 * 定义需要事务控制的方法
 * @since 2017-07-12.
 */
@Target(ElementType.METHOD) // 该注解只能用于方法级别
@Retention(RetentionPolicy.RUNTIME)
public @interface Transaction {
}

事务实际上是AOP编程的一种具体的应用,这里就是基于AOP,下面定义的TransactionProxy是一个切面类,它会在方法运行的前后执行,完整的逻辑要结合前面的AOP实现才能看明白,这里随手说下。AOP将我们自己实现的类进行代理,在每个方法运行转到代理类的实现,代理类从第一个切面类开始执行,执行完后会运行下一个切面类,全部的切面类执行完毕后,运行目标的类的方法,然后返回,再依次反顺序调用代理类的结束方法,而事务只是这些切面类其中的一种。
下面的类中还定义了一个ThreadLocal类型的变量FLAG_HOLDER,我猜是为了防止在同一个线程中进入两次事务。

package org.smart4j.framework.proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.smart4j.framework.annotation.Transaction;
import org.smart4j.framework.helper.DatabaseHelper;

import java.lang.reflect.Method;

/**
 * 事务代理
 * @since 2017-07-13.
 */
public class TransactionProxy implements Proxy {
    private static final Logger LOGGER = LoggerFactory.getLogger(TransactionProxy.class);

    // 用来保证同一线程中事务控制相关逻辑只会执行一次
    private static final ThreadLocal<Boolean> FLAG_HOLDER = new ThreadLocal<Boolean>(){
        @Override
        protected Boolean initialValue() {
            return false;
        }
    };

    @Override
    public Object doProxy(ProxyChain proxyChain) throws Throwable {
        Object result;
        boolean flag = FLAG_HOLDER.get();
        Method method = proxyChain.getTargetMethod();
        if (!flag && method.isAnnotationPresent(Transaction.class)) {
            FLAG_HOLDER.set(true);
            try {
                DatabaseHelper.beginTransaction();
                LOGGER.debug("begin transaction");
                result = proxyChain.doProxyChain();
                DatabaseHelper.commitTransaction();
                LOGGER.debug("commit transaction");
            } catch (Exception e) {
                DatabaseHelper.rollbackTransaction();
                LOGGER.debug("rollback transaction");
                throw e;
            } finally {
                FLAG_HOLDER.remove();
            }
        } else {
            result = proxyChain.doProxyChain();
        }
        return result;
    }
}

在AOPHelper中会将所有的service注解的类都加入到proxyMap,proxyMap的key是代理类,value是目标类,在后面代理类执行目标类的时候会查看目标类的方法是有事务的标注,有的话再执行事务的上下文动作。

    /**
     * 关联事务代理
     * @param proxyMap
     * @throws Exception
     */
    private static void addTransactionProxy(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
        // 获取所有service类,这里是简单的处理,默认把services注解标识的类,都拿过来换成事务代理对象,
        // 然后在代理执行的时候 会根据是否有Transaction注解,而去对事务进行操作
        Set<Class<?>> servicesProxySet = ClassHelper.getClassSetByAnnotation(Service.class);
        proxyMap.put(TransactionProxy.class, servicesProxySet);
    }

相关文章

网友评论

      本文标题:《架构探险-从零开始写Java Web框架》学习笔记2

      本文链接:https://www.haomeiwen.com/subject/heqswctx.html