【从零写javaweb框架】(九)开发AOP框架

上一篇我们完成搭建了一个简单的MVC框架,使其可以通过DispatchServlet来处理所有的请求【从零写javaweb框架】(八)请求转发器,这一篇会实现AOP功能。

一.什么是AOP

AOP(Aspect-Oriented Programming)是对面向对象编程(OOP)的一种补充,可以译为面向切面编程。其中切面是AOP的一个术语,表示从业务逻辑中分离出来的横切逻辑,比如性能监控,日志记录、权限控制等,这些逻辑都可从核心的业务逻辑代码中抽离出去。也就是说,通过AOP可以解决代码耦合问题,是职责更加单一。
我个人的理解:AOP的作用通俗一点说就是可以在方法的前后附加上其他代码,每次调用此方法之前或之后(又或者前后都有),都会先后执行附加的代码。

二.设计模式之代理模式

如果想继续深入了解AOP的实现,我们需要先了解静态代理模式和动态代理模式,这里附上我以前写的两篇博文(如果之前已经了解过代理模式了,那么可以直接跳过看下一步):
静态代理模式:设计模式_4:代理模式


三.开发AOP框架

紧接着我之前写的框架,现在要开始实现AOP了,首先定义一个切面注解:
package org.smart4j.framework.annotation;

import java.lang.annotation.*;

/**
 * desc : 切面注解
 * Created by Lon on 2018/2/3.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {

    /**
     * 注解
     */
    Class<? extends Annotation> value();

}

这个注解的value是一个class,表示我们要对哪个标有此注解类里的方法进行切入(其实就是要代理哪个类),我们可以注解在一个类上面,表示此类是一个切面类。用法:@Aspect(Controller.class),它表示我们要对标有Controller(这个注解是我们之前编写的,如果忘记了可以回头看一看)注解的类进行切入。

然后继续在框架中添加一个名为Proxy的接口:
package org.smart4j.framework.proxy;

/**
 * desc : 代理接口
 * Created by Lon on 2018/2/3.
 */
public interface Proxy {

    /**
     * 执行链式代理
     */
    Object doProxy(ProxyChain proxyChain) throws Throwable;

}
这个Proxy接口中有一个doProxy方法,参数是一条代理链proxyChain,这条代理链的作用是把多个代理串起来,并一个个依次执行,执行顺序取决于添加到链上的先后顺序,ProxyChain代码:
package org.smart4j.framework.proxy;

import net.sf.cglib.proxy.MethodProxy;

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

/**
 * desc : 代理链
 * Created by Lon on 2018/2/3.
 */
public class ProxyChain {
    
    // 要代理的目标类
    private final Class<?> targetClass;
    // 要代理的目标对象
    private final Object targetObject;
    // 要代理的目标方法
    private final Method targetMethod;
    // 目标方法的代理(这个是CGLib为我们提供的)
    private final MethodProxy methodProxy;
    // 要代理的目标方法的方法参数
    private final Object[] methodParams;

    // 代理列表
    private List<Proxy> proxyList = new ArrayList<Proxy>();
    // 代理索引
    private int proxyIndex = 0;

    // ProxyChain构造函数
    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;
    }

    /**
     * 如果尚未达到proxyList上限,则从proxyList上拿取相应的对象,执行它的doProxy方法,
     * 在Proxy接口的实现中会提供相应的横切逻辑,并调用doProxyChain方法,随后将再次调用
     * 当前ProxyChain对象的doProxyChain方法,知道proxyIndex达到proxyList上限为止,
     * 到最后再调用methodProxy的invokeSuper方法,执行目标对象的业务逻辑。
     */
    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;
    }

}
我们下面一步还用到了动态代理,所以要往maven加入CGLib:
    <!-- CGLib -->
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>2.2.2</version>
    </dependency>
现在写一个类,用它来动态创建代理对象,命名为ProxyManager:
package org.smart4j.framework.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;

/**
 * desc : 代理管理器
 * Created by Lon on 2018/2/5.
 */
public class ProxyManager {

    @SuppressWarnings("unchecked")
    public static <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList){
        return (T) Enhancer.create(targetClass, new MethodInterceptor() {
            public Object intercept(Object targetObject, Method targetMethod,
                                    Object[] methodParams, MethodProxy methodProxy) throws Throwable {
                return new ProxyChain(targetClass,targetObject, targetMethod,methodProxy,methodParams,proxyList).doProxyChain();
            }
        });
    }

}

接下来需要通过切面类来调用ProxyManager,写一个抽象类,让它提供一个模板方法,并在该抽象类的具体实现中扩展相应的抽象方法:

package org.smart4j.framework.proxy;

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

import java.lang.reflect.Method;

/**
 * desc : 切面代理类
 * Created by Lon on 2018/2/6.
 */
public abstract class AspectProxy implements Proxy{

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

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

        Class<?> cls = proxyChain.getTargetClass();
        Method method = proxyChain.getTargetMethod();
        Object[] params = proxyChain.getMethodParams();

        begin();
        try {
            if (this.intercept(cls, method, params)){
                this.before(cls, method, params);
                result = proxyChain.doProxyChain();
                this.after(cls, method, params, result);
            } else {
                result = proxyChain.doProxyChain();
            }
        } catch (Throwable e){
            LOGGER.error("proxy failure", e);
            error(cls, method, params, e);
            throw e;
        } finally {
            end();
        }
        return result;
    }

    public void begin(){
    }

    public boolean intercept(Class<?> cls, Method method, Object[] params) throws Throwable{
        return true;
    }


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

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

    public void error(Class<?> cls, Method method, Object[] params, Throwable e) {
    }

    public void end(){

    }

}
在doProxy方法中,我们从proxyChain参数中获取了目标类、目标方法与方法参数、之后用try/catch/finally代码块来实现调用框架,从框架中抽象出一系列的钩子方法,这些抽象方法可在AspectProxy的子类中有选择性地进行实现。

下一篇会写加载AOP框架


©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页