拦截器

March . 06 . 2019
  • 拦截器的由来
由于动态代理比较难理解, 所以一般都会将他封装为一个接口来使用, 即拦截器, 开发者只要知道拦截器的接口方法、含义和作用即可, 无需知道动态代理的代码是如何实现的。
  • 代码实现

包结构如下

1.JPG

我们用JDK动态代理来实现一个拦截器的逻辑, 为此先定义拦截器接口Interceptor, 如下

Interceptor.java

package interceptor;

import java.lang.reflect.Method;

public interface Interceptor {
    public boolean before(Object proxy, Object target, Method method, Object[] args);

    public void around(Object proxy, Object target, Method method, Object[] args);

    public void after(Object proxy, Object target, Method method, Object[] args);
}

定义了三个方法, before, around, after, 含义如下

  1. 三个参数为: proxy 代理对象, target 真实对象, method 代理方法, args 代理方法的参数列表
  2. before 返回 boolean 值, 他在真实对象被代理前调用. 为 true 时, 则反射真实的对象方法, 反之则反射around 方法
  3. 在 before 方法返回 false 的情况下, 调用 around 方法
  4. 在反射方法, 或者 around 方法执行后, 调用 after 方法

新建 MyInterceptor 类并实现 Interceptor 接口

MyInterceptor.java

package interceptor;

import java.lang.reflect.Method;

public class MyInterceptor implements Interceptor {
    @Override
    public boolean before(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("反射方法前逻辑..");
        return true;    // 不反射被代理对象的原有方法
    }

    @Override
    public void around(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("取代了被代理对象的方法..");
    }

    @Override
    public void after(Object proxy, Object target, Method method, Object[] args) {
        System.out.println("反射方法后的逻辑...");
    }
}

使用 JDK 动态代理, 并在适当时调用接口逻辑, 新建 InterceptorJdkProxy  类

InterceptorJdkProxy .java

package interceptor;

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

public class InterceptorJdkProxy implements InvocationHandler {

private Object target; // 真实对象
private String interceptorClass = null; // 拦截器的全限定名

public InterceptorJdkProxy(Object target, String interceptorClass)
{
this.target = target;
this.interceptorClass = interceptorClass;
}

/**
* 绑定委托对象并返回一个代理
*
* @param target 真实对象
* @param interceptorClass 拦截器类全路径名
* @return 代理对象
*/
public static Object bind(Object target, String interceptorClass)
{
// 取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InterceptorJdkProxy(target, interceptorClass));
}

/**
* 通过代理对象进入方法, 首先进入这个方法
*
* @param proxy 代理对象
* @param method 方法, 真实对象被调用的方法
* @param args 方法的参数
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (interceptorClass == null) {
return method.invoke(target, args);
}

Object result = null;

// 通过反射生成拦截器
Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();

// 调用前置方法
if (interceptor.before(proxy, target, method, args)) {
// 反射原有对象方法
result = method.invoke(target, args);
} else { // 返回 false 执行 around 方法
interceptor.around(proxy, target, method, args);
}

// 调用后置方法
interceptor.after(proxy, target, method, args);
return result;
}
}

这里有两个属性, 一个是 target 真实对象, 另一个是字符串 interceptorClass, 它是一个拦截器的全路径名。代理的步骤如下:

  1. 在 bind 方法中使用 JDK 动态代理绑定了一个对象, 然后返回代理对象
  2. 如果没有设置拦截器, 则直接反射真实对象的方法, 然后结束, 否则执行第三步
  3. 通过反射实例化拦截器, 并准备使用它
  4. 调用拦截器的 before 方法, 如果返回 true, 反射原来的方法, 反之运行拦截器的 around 方法
  5. 调用拦截器的 after 方法
  6. 返回结束

工作流程如下图

3.JPG

                                  

创建真实对象的方法接口 Say

Say.java

package interceptor;

public interface Say {
void sayHello(String hello);
}

创建真实对象并实现方法接口

Hello.java

package interceptor;

public class Hello implements Say{
@Override
public void sayHello(String name) {
System.out.println("Hello: " + name);
}
}

新建测试类 Test

Test.java

package interceptor;

public class Test {

public static void main(String[] args)
{
Say proxy = (Say) InterceptorJdkProxy.bind(new Hello(), "interceptor.MyInterceptor");

proxy.sayHello("123");
}
}
  • 程序运行效果如下

2.JPG

  • 拦截器的好处
  1. 开发者只需要知道拦截器的作用就可以编写拦截器了
  2. 设计者只会把拦截器的接口暴露给开发者, 让动态代理的逻辑在开发者的视野中消失