AOP编程(面向切面编程)的原理是动态代理模式,目标对象实现接口时使用JDK动态代理,没有实现接口时使用cglib动态代理。拦截器的实现就是一种面向切面编程的典型。程序设计者设计一个拦截器接口供开发者使用,开发者只要知道拦截器接口的方法、含义和作用即可,无需知道具体实现。
下面写一个拦截器看看它的具体实现。

定义拦截器接口Intercepter

public interface Intercepter {
    boolean before(Object proxy, Method method, Object[] args);
    void around(Object proxy, Method method, Object[] args);
    void after(Object proxy, Method method, Object[] args);
}

这里定义了三个方法,before、around、after,与spring的几个通知逻辑相似。逻辑定义如下:

  • 3个方法的参数为:proxy代理对象、target真实对象、method方法、args方法运行参数
  • before方法返回boolean值,它在目标方法调用之前调用,当它返回true时,则反射真实对象的方法,返回false则调用around方法。
  • 在before方法返回false的情况下,调用around方法代替目标方法。
  • 在反射真实方法或者around方法之后调用after方法。

以上调用逻辑由程序设计者定义,开发者需要实现Intercepter接口,自定义三个方法的具体实现。也就是自定义一个拦截器。

自定义拦截器

下面定义三个拦截器,MyIntercepter1、MyIntercepter2、MyIntercepter3 。结构如下,只是名字不同,只贴出其中一个:

public class MyIntercepter1 implements Intercepter{

    /**
     * 目标方法调用前调用
     * @return true:调用目标方法;false:调用around方法取代目标方法
     */
    @Override
    public boolean before(Object proxy, Method method, Object[] args) {
        System.out.println("MyIntercepter1 ==> before execute!!");
        return true;
    }

    /**
     * before方法返回false调用
     */
    @Override
    public void around(Object proxy, Method method, Object[] args) {
        System.out.println("MyIntercepter1 ==> around execute!!");
    }

    /**
     * 目标方法或around方法调用后调用
     */
    @Override
    public void after(Object proxy, Method method, Object[] args) {
        System.out.println("MyIntercepter1 ==> after execute!!");
    }
}

这就是开发者写的拦截器。看看它是如何生效的:

运用动态代理使用拦截器

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class IntercepterProxyHandler implements InvocationHandler {
    private Object target;//目标对象
    private String intercepterClass;//拦截器全限定名

    private IntercepterProxyHandler(Object target, String intercepterClass){
        this.target = target;
        this.intercepterClass = intercepterClass;
    }

    /**
     * 绑定目标对象,并对外暴露获取代理对象的方法
     * @param target 目标对象
     * @param intercepterClass 拦截器类全限定名称
     * @return 代理对象
     */
    public static Object bind(Object target,String intercepterClass){
        return Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                target.getClass().getInterfaces(),
                new IntercepterProxyHandler(target,intercepterClass));
    }

    /**
     * 拦截器逻辑
     * @param proxy 代理对象
     * @param method 目标方法
     * @param args 方法的参数
     * @return 调用结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        //如果未指定拦截器,则只执行目标方法
        if (this.intercepterClass==null){
            return method.invoke(target,args);
        }
        Intercepter intercepter = (com.youzi.test.proxy.intercepter.Intercepter) Class.forName(intercepterClass).newInstance();
        //执行拦截器before方法,如果返回true,则再执行目标方法;如果返回false,则执行around方法
        if (intercepter.before(proxy, method, args)){
            result = method.invoke(target,args);
        }else{
            intercepter.around(proxy, method, args);
        }
        //再执行after方法
        intercepter.after(proxy, method, args);
        return result;
    }
}

该类继承InvocationHandler类,有两个属性,一个是target真实对象;两一个是intercepterClass,它是一个拦截器的全限定名。
对外提供bind方法,返回一个代理对象;实现InvocationHandler类的invoke方法,定义了拦截器的调用逻辑。
具体执行步骤:

  1. 调用bind方法用jdk动态代理绑定一个对象,然后返回代理对象,对目标对象的目标方法进行增强。
  2. 使用代理对象调用目标方法,会执行invoke方法逻辑。如果没有设置拦截器,则直接反射真实对象的方法。否则进行第三步
  3. 通过反射生成拦截器,并准备使用它。
  4. 调用拦截器的before方法,如果返回true,反射原来的方法;否则运行拦截器的around方法。
  5. 调用拦截器的after方法
  6. 返回结果

拦截器封装了动态代理的代码,进一步简化了动态代理的使用。
下面看看使用拦截器的代码,前面定义了3个拦截器,这里都用上:
目标对象(实现的接口就不贴出来了):

public class IServiceImpl implements IService {
    @Override
    public void doFirst(String string) {
        System.out.println("执行第一个业务方法,参数:"+string);
    }

    @Override
    public void doSecond() {
        System.out.println("执行第二个业务方法");
    }
}

测试类

public class Test2 {
    public static void main(String[] args) {
        IService target = new IServiceImpl();
        IService proxy1 = (IService) IntercepterProxyHandler.bind(target, "com.youzi.test.proxy.intercepter.MyIntercepter1");
        IService proxy2 = (IService) IntercepterProxyHandler.bind(proxy1, "com.youzi.test.proxy.intercepter.MyIntercepter2");
        IService proxy3 = (IService) IntercepterProxyHandler.bind(proxy2, "com.youzi.test.proxy.intercepter.MyIntercepter3");
        proxy3.doFirst("aaaa");
    }
}

这里使用责任链模式,给目标方法有序添加三个拦截器。执行结果:

MyIntercepter3 ==> before execute!!
MyIntercepter2 ==> before execute!!
MyIntercepter1 ==> before execute!!
执行第一个业务方法,参数:aaaa
MyIntercepter1 ==> after execute!!
MyIntercepter2 ==> after execute!!
MyIntercepter3 ==> after execute!!

因为三个拦截器before方法都返回true,所以没有执行around方法。如果修改MyIntercpter2的before方法返回false,则执行结果:

MyIntercepter3 ==> before execute!!
MyIntercepter2 ==> before execute!!
MyIntercepter2 ==> around execute!!
MyIntercepter2 ==> after execute!!
MyIntercepter3 ==> after execute!!