[笔记]架构探险-从零开始写JavaWeb框架-2.1. 之使框架具有aop特性-aop框架加载与切面运行流程分析

http://git.oschina.net/zhuqiang/smart-framework 跟着书上学习的 框架git地址
http://git.oschina.net/zhuqiang/mrweb 依赖上面框架的demo练习

啰嗦一句:本笔记只是自己在学习过程中的一些分析和理解,看的人不一定能看懂.如果有兴趣还是去买这本书看.笔记就当是另外一种解说好了


在本章节中会学习到如下的技术:

  1. 如何理解并使用代理技术
  2. 如何使用Spring提供的AOP技术(忽略,太多知识)
  3. 如何使用动态代理技术实现AOP框架
  4. 如何理解并使用ThreadLocal技术
  5. 如何理解数据库事务管理机制
  6. 如何使用AOp框架实现事务控制

静态代理

顾名思义,我的理解就是,在编译前写死的. 就如同下面这个例子, 写了一个类.然后实现目标类的所有方法,然后再转调目标类的方法.

interface IHello{
    public void say(String msg);
}
class Hello implements IHello{
    public void say(String msg) {
        System.out.println(msg);
    }
}

// 静态代理
class HelloProxy implements IHello{
    private IHello iHello;

    public HelloProxy(IHello iHello) {
        this.iHello = iHello;
    }

    @Override
    public void say(String msg) {
        System.out.println("增加了前置处理");
        iHello.say(msg);
        System.out.println("增加了后置处理");
    }
}

class Demo{
    public static void main(String[] args) {
        HelloProxy proxy = new HelloProxy(new Hello());
        proxy.say("静态代理");
    }
}

JDK动态代理

在运行期间根据代理类的字节码生成代理类的实例.

JDK动态代理:

  1. 使用Proxy类来获取代理类
  2. proxy需要 一个 InvocationHandler 类型的代理实现处理类(就是运行切面逻辑的地方)
  3. 只能代理 有接口的对象实例.强转的话,也只能转换为接口.
/**
 * Created by zhuqiang on 2015/10/24 0024.
 * jdk 动态代理
 */
public class DynamicProxy implements InvocationHandler {
    private Object target;

    public DynamicProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("增加了前置处理");
        Object result = method.invoke(target, args); //转调目标的实际处理类
        System.out.println("增加了后置处理");
        return result;
    }

    public static void main(String[] args) {
        Hello hello = new Hello();
        DynamicProxy proxy = new DynamicProxy(hello);
        IHello o = (IHello) Proxy.newProxyInstance(
                hello.getClass().getClassLoader(), // 类加载器
                hello.getClass().getInterfaces(), //该类所实现的接口
                proxy);  // 代理实现类
        o.say("动态代理 简单使用");
    }
}

//
interface IHello{
    public void say(String msg);
}
class Hello implements IHello{
    public void say(String msg) {
        System.out.println(msg);
    }
}

CGlib动态代理

/**
 * Created by zhuqiang on 2015/10/24 0024.
 */
public class CGlibProxy implements MethodInterceptor {
    private static CGlibProxy cp = new CGlibProxy();
    public static CGlibProxy getInstance(){
        return cp;
    }
    // 获取代理类
    public <T> T getProxy(Class<T> cls){
        return (T)Enhancer.create(cls,this);
    }

    /**
     *
     * @param o 目标类
     * @param method 目标方法
     * @param objects 目标方法参数
     * @param methodProxy 方法代理
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("前置");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("后置");
        return result;
    }
}

class HelloCg{
    public void say(String msg) {
        System.out.println(msg);
    }
}

class CGlibTest{
    public static void main(String[] args) {
        HelloCg proxy = CGlibProxy.getInstance().getProxy(HelloCg.class);
        proxy.say("cglib 动态代理,支持没有接口的类哦");
    }
}

什么是AOP

Aop(Aspect Oriented Programming面向方面编程) : 是对OOP编程的一种补充,也叫面向切面,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. 由Aspect切面 和 Prointcut切点组成.

AOP框架有 aspectJ,前生是AspectWekz.

  1. Aspect切面: 就是横切业务逻辑的代码
  2. Prointcut切点: 用一个条件来匹配在哪些地方使用切面.

Spring AOP

这章节就忽略吧.知识点太多了. 要弄明白,得花一些时间,可时间不多.最重要的你要有个大概的知识体系,到时候用到了,再去深挖. 放上一张书上的 思维导图.
这里写图片描述


加载AOP框架

对 切面@Aspect 注解加载的分析;
1. @Aspect 表示一个类是一个切面,
2. 注解接收一个注解类型的class,表示该切面的切点是 被目标注解类修饰的类中的所有方法(连接点)

AopHelper 的最终目标是: 创建 目标类class 与 增强它的 切面代理类 进行关联
注意:这个操作会让代理类 覆盖掉 之前创建的 普通实例.因为HelperLoader中的加载顺序是:

ClassHelper.class,
                BeanHelper.class,
                AopHelper.class,
                IocHelper.class,
                ControllerHelper.class

通过上面的加载顺序可以看到,aop加载的时候,会把之前BeanHelper中把我们需要管理的bean实例化的对象,对应的class实例对象用当前的代理进行替换掉. 那么这样一来,在外部获取到的 目标类其实就已经是一个代理类了

实现的思路:

  1. Map<Class<?>,Set<Class<?>>> createProxyMap() : 将切面类 和 需要被增强的类class建立关联; key=切面类 value=所有需要被该切面所增强的类class
    思路是:

    1. 通过从ClassHelper中扫描到的class集合中 挑选出AspectProxy的子类(也就是 我们编写的切面类)
    2. 找到之后还不行,还需要从中过滤,拿到被Aspect注解所标注的类(被该注解所标注的类的含义在上面有讲)
    3. 拿到 被Aspect标注的类之后,我们就可以拿到 拿到 该注解中的切点,也就是说,我们能拿到所有需要被该切面所增强的类class
    4. 有类切面类(第2步) , 有了 所有切面类所对应的需要被增强的所有的类,那么就建立关联.返回结果
  2. Map<Class<?>,List<Proxy>> createTargetMap : 根据 createProxyMap 返回的值得,继续建立关联,key = 被增强的类class,value=切面列表实例 (因为一个类,可以被多个切面增强)
    思路是:

    1. 创建切面类的实例
    2. 将 该切面实例所对应增强的class建立关联,因为一个类,可以被多个切面所增强
  3. 使用cglib创建代理类,然后把bean容器中的普通示例类给替换掉.
    思路是:

    1. 使用Enhancer创建方法级别的拦截增强,ProxyManger.createProxy(final Class<?> targetClass,final List<Proxy> proxyList) 把目标类和对应增强它的切面列表在intercept中 让我们的代理链模型去执行增强方法,在适合的切入点委托目标方法:return new ProxyChain(targetClass,targetObj,method,methodProxy,paramsObjects,proxyList).doProxyChain();

    注意: 一定要注意这里,最开始一直不知道 这个代理链有什么用处,然后在实现代理的时候 方法拦截器中intercept忘记了 cglib生成代理的 步骤api.直接返回了我们的代理链.后来就直接抛出了 不能转换的异常.
    debug调试 这个代理链的运作流程.发现.其实就是一个 职责链模式的设计,当我们代理的方法被拦截的之后,就会执行 用cglib生成代理的时候里面的代码,new ProxyChain(..).doProxyChain(). 这里是一个代理链条的入口. 这个里面 会判断,如果 有切面,则执行切面的 doProxy 方法.而 切面的doProxy方法里面 会在合适的时机(也就是我们定义的前后增强等位置)调用切面实现类的增强方法.然后继续调用doProxyChain(),这个时候就相当于 代理连的doProxyChain()方法被切面代理连给递归调用了.这样一来, 就会继续判断是否还有下一个切面增强类,如此的递归调用,最后委托目标方法 返回结果.

    诶.到现在才发现,最开始,就是这个代理链,和 增强类怎么被调用的设计是最难的了. 好吧,啰嗦了这么多话,下面放出一张流程图.来直观的感受下,这个代理链的增强类是怎么运作的.
    这里写图片描述

/**
 * Created by zhuqiang on 2015/10/25 0025.
 * 拦截所有的controller方法
 * 我们想使用这个切面类  来拦截所有controller的方法.
 */
@Aspect(Controller.class)
public class ControllerAspect extends AspectProxy{
    private static final Logger LOGGER = LoggerFactory.getLogger(PropsUtil.class);
    private long begin; //开始时间

    @Override
    public void before(Class<?> cls, Method method, Object[] params) throws Throwable {
        begin = System.currentTimeMillis();
        LOGGER.debug(String.format("begin:class:%s,method:%s",cls.getName(),method.getName()));
    }

    @Override
    public void after(Class<?> cls, Method method, Object[] params) throws Throwable {
        long end = System.currentTimeMillis();
        LOGGER.debug(String.format("end:class:%s,method:%s,耗时:%s", cls.getName(), method.getName(),end - begin));
    }
}

// 加载aop框架的aophelper
/**
 * Created by zhuqiang on 2015/10/26 0026.
 */
public final class AopHelper {
    private static final Logger LOGGER = LoggerFactory.getLogger(AopHelper.class);
    static {
        try {
            Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
            Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);

            for(Map.Entry<Class<?>, List<Proxy>> targetEnt:targetMap.entrySet()){
                Class<?> targetClass = targetEnt.getKey();
                List<Proxy> proxyList = targetEnt.getValue();
                Object proxy = ProxyManger.createProxy(targetClass, proxyList);
                BeanHelper.setBean(targetClass,proxy);
            }
        }catch (Exception e){
            LOGGER.error("aop fail",e);
        }

    }

    /**
     * 获取 aspect 设置的 注解类,类型的所有class 集合
     * @param aspect
     * @return
     * @throws Exception
     */
    private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception{
        Set<Class<?>> targetClassSet = new HashSet<>();
        Class<? extends Annotation> annotation = aspect.value();
        // Aspect 是对一类注解类 进行拦截,这里是 不为空并且不是 aspect注解,就可以获取该注解
        if(annotation != null && !annotation.equals(Aspect.class)){
            targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));
        }
        return targetClassSet;
    }

    /**
     * 获取需要被代理的 所有 class 对象
     * @return key= 切面 ,value = 该切面所增强的所有 class 对象
     * @throws Exception
     */
    private static Map<Class<?>,Set<Class<?>>> createProxyMap() throws Exception{
        Map<Class<?>,Set<Class<?>>> proxyMap = new HashMap<>();
        // 获取 所有的切面代理类 的 子类
        Set<Class<?>> aspectProxySet = ClassHelper.getClassSetBySuper(AspectProxy.class);
        for (Class<?> cls : aspectProxySet) {
            if(cls.isAnnotationPresent(Aspect.class)){// 判断该class 是否有 aspect注解(是否是一个切面标注)
                Aspect aspect = cls.getAnnotation(Aspect.class); // 获取注解类
                Set<Class<?>> targetClassSet = createTargetClassSet(aspect); // 由于 aspect 的值是接收一个 注解类,所以这里是获取到 使用该注解类的所有class
                proxyMap.put(cls,targetClassSet);
            }
        }
        return proxyMap;
    }

    /**
     * 把 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<>();
        for(Map.Entry<Class<?>,Set<Class<?>>> proxyEnt : proxyMap.entrySet()){
            Class<?> proxyClass = proxyEnt.getKey(); // 获取切面class
            Set<Class<?>> targetClassSet = proxyEnt.getValue(); //切面所需要增强的 class 对象(也就是我们需要把该切面在哪些实例上增强的对象class)
            for (Class<?> targetClass : targetClassSet) {
                Proxy proxy = (Proxy)proxyClass.newInstance(); // 创建该切面的实例
                if(targetMap.containsKey(targetClass)){
                    targetMap.get(targetClass).add(proxy);
                }else{
                   List<Proxy>  proxyList = new ArrayList<>();
                    proxyList.add(proxy);
                    targetMap.put(targetClass,proxyList);
                }
            }
        }
        return targetMap;
    }
}



/**
 * Created by zhuqiang on 2015/10/25 0025.
 * 代理链: 可以将多个代理通过一条链子串起来,一个一个地去执行,执行顺序取决于添加到链上的先后顺序(这不会是职责链模式吧)
 */
public class ProxyChain {
    private final Class<?> targetClass; //目标类
    private final Object targetObject; //目标对象
    private final Method targetMethod; //目标方法
    private final MethodProxy methodProxy; //方法代理
    private final Object[] methodParams; //方法参数
    private List<Proxy> proxyList = new ArrayList<>();  //代理列表
    private int proxyIndex = 0; // 代理索引

    public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
        this.targetClass = targetClass;
        this.targetObject = targetObject;
        this.targetMethod = targetMethod;
        this.methodProxy = methodProxy;
        this.methodParams = methodParams;
        this.proxyList = proxyList;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public Method getTargetMethod() {
        return targetMethod;
    }

    public Object[] getMethodParams() {
        return methodParams;
    }

    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;
    }
}

/**
 * Created by zhuqiang on 2015/10/25 0025.
 * 代理接口
 */
public interface Proxy {
    /** 执行链式代理 */
    Object doProxy(ProxyChain proxyChain) throws Throwable;
}
/**
 * Created by zhuqiang on 2015/10/25 0025.
 * 代理链: 可以将多个代理通过一条链子串起来,一个一个地去执行,执行顺序取决于添加到链上的先后顺序(这不会是职责链模式吧)
 */
public class ProxyChain {
    private final Class<?> targetClass; //目标类
    private final Object targetObject; //目标对象
    private final Method targetMethod; //目标方法
    private final MethodProxy methodProxy; //方法代理
    private final Object[] methodParams; //方法参数
    private List<Proxy> proxyList = new ArrayList<>();  //代理列表
    private int proxyIndex = 0; // 代理索引

    public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
        this.targetClass = targetClass;
        this.targetObject = targetObject;
        this.targetMethod = targetMethod;
        this.methodProxy = methodProxy;
        this.methodParams = methodParams;
        this.proxyList = proxyList;
    }

    public Class<?> getTargetClass() {
        return targetClass;
    }

    public Method getTargetMethod() {
        return targetMethod;
    }

    public Object[] getMethodParams() {
        return methodParams;
    }

    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;
    }
}

/**
 * Created by zhuqiang on 2015/10/25 0025.
 * 代理管理器,用来创建代理对象
 */
public class ProxyManger {
    /**
     * 创建代理
     * @param targetClass 目标类
     * @param proxyList 代理列表
     * @param <T>
     * @return
     */
    public static <T>T createProxy(final Class<?> targetClass,final List<Proxy> proxyList){
        return (T)Enhancer.create(targetClass, new MethodInterceptor() {
            @Override
            public Object intercept(Object targetObj, Method method, Object[] paramsObjects, MethodProxy methodProxy) throws Throwable {
                return new ProxyChain(targetClass,targetObj,method,methodProxy,paramsObjects,proxyList).doProxyChain();
            }
        });
    }
}

最后说一句: 上面的思路 也分析了.加载aop比较简单.学过一次后有思路了 基本上向这样简单的aop框架都能弄出来. 我觉得难的点在于, 这个代理链的执行这一个逻辑模版框架的设计, 和 动态代理的创建. 现在还是觉得设计模式有必要学习.

待续..待学习

### 回答1: 《架构探险 从零开始JavaWeb框架》这本书是一本关于如何从零开始构建JavaWeb框架的实践指南。这本书详细介绍了构建JavaWeb框架的基本原理、核心功能以及框架设计的各个方面。下面我将简要回答这个问题。 在这本书中,作者首先介绍了JavaWeb框架的基本概念和开发背景,以及为什么需要自己去构建一个JavaWeb框架。接着,作者分析了现有的开源JavaWeb框架的优缺点,并提出了自己的设计思路。 在框架的设计方面,作者使用了简单、灵活、易扩展的原则。他详细解释了框架的各个组件的功能和交互方式,比如控制器、模型、视图和数据层等。除此之外,作者还介绍了如何实现框架的依赖注入和AOP等功能,以及如何处理请求和响应等核心功能。 在框架的实现过程中,作者采用了优秀的开源框架作为基础,例如使用Servlet作为底层容器,使用Freemarker作为模板引擎,使用MySQL作为数据库等。通过实际的代码示例和详细的解释,读者可以更好地理解每个组件的功能和实现方式。 总的来说,《架构探险 从零开始JavaWeb框架》这本书通过将复杂的JavaWeb框架的设计和实现分解成简单的组件和步骤,帮助读者理解并掌握JavaWeb框架的构建过程。通过学习这本书,读者可以了解到一个完整的JavaWeb框架所需的各个组件和功能,并能够根据自己的需求进行定制和扩展。无论是对于初学者还是有一定经验的开发人员而言,这本书都是一本难得的实践指南。 ### 回答2: 《架构探险从零开始JavaWeb框架》是一本非常实用的书籍,主要介绍了如何从零开始开发一个完整的JavaWeb框架。这本书的作者对于JavaWeb开发有着丰富的经验,通过本书的学习,读者可以掌握开发框架所需的各种核心知识和技术。 在书中,作者首先介绍了JavaWeb框架的概念和作用,以及为什么要从零开始开发一个框架。接着,作者详细讲解了框架的基本原则和设计思路,包括MVC模式、依赖注入、AOP等核心概念和技术。作者通过清晰的代码示例和实际案例,展示了如何使用这些技术来搭建一个可靠、高效的框架。 随后,作者开始逐步实现一个简单的JavaWeb框架。他从处理HTTP请求开始,依次实现了路由解析、参数绑定、控制器调用、视图渲染等功能。在实现的过程中,作者注重详细讲解每个步骤的实现原理和技术细节,读者可以通过跟随书中的示例代码进行实践和理解。 除了核心功能的实现,作者还介绍了一些框架的扩展功能,如连接池、事务管理、日志记录等。这些扩展功能可以使框架更加完善和灵活,提供更好的开发体验和性能优化。 总而言之,此书通过一个完整的JavaWeb框架实例,全面而系统地介绍了框架的设计与实现。读者通过学习本书,不仅可以掌握JavaWeb开发的核心知识和技术,还可以提升自己的架构设计能力和编码水平。无论是初学者还是有一定经验的开发者,都能从中受益匪浅。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值