本文说明:在学习《Java EE互联网轻量级框架整合开发》此书时,里面提到了几种设计模式,我在学习这几种设计模式时写了笔记,放上来让大家共同学习,如果有错误,望指出。
本章由两部分组成:
1、基本概念+关键代码讲解
2、完整例子
基本概念讲解
拦截器模式是动态代理的方便写法,将想要插入的逻辑利用我们定义的拦截器接口抛给外部实现而已。所以就是在动态代理的基础上加上拦截器实现即可。我们以JDK动态代理实现拦截器为例。
拦截器模式主要包括2个接口4个类,拦截器接口,拦截器类(继承拦截器接口),目标接口,目标类(实现目标接口),JDK动态代理核心类(涵盖拦截器),测试类。
【拦截器接口和拦截器类】:将想插入的逻辑通过三个方法before、after、around实现;
【目标接口和目标类】:被插入逻辑的目标代码;
【JDK动态代理核心类】:利用拦截器实现核心插入逻辑;
【测试类】:测试逻辑是否实现。
下面主要讲讲【拦截器接口和拦截器类】和【JDK动态代理核心类】。
拦截器接口和拦截器类
拦截器接口我们定义含有三个方法,分别是before、after和around。有读者可能会奇怪before和after已经涵盖了在目标方法的前后插入逻辑的所有可能的地方,那么这个around是来干嘛的?around顾名思义,就是环绕在目标方法的周围,那么就相当于before+after。
好像很有道理,其实是错的。
around,其实是取代了目标方法原有的代码。它的前面可以有before,后面可以有after,是目标方法的替代品,而不是插入品。
拦截器接口我们定义如下:
package com.amiao.design_pattern.dynamic_proxy.interceptor;
import java.lang.reflect.Method;
//含有4个参数,除了我们的invoke方法有3个参数之外,还有一个目标对象的参数。
public interface Interceptor {
public boolean before(Object proxy, Object target, Method method, Object[] args);
public void after(Object proxy, Object target, Method method, Object[] args);
public void around(Object proxy, Object target, Method method, Object[] args);
}
细心的读者会注意到,before方法的返回值不是void而是boolean。什么作用?为了实现around。我们可以在before实现后,根据before的返回值来决定是否实现目标方法原有代码。我们可以定义为:返回true实现目标方法原有代码,返回false实现替代代码,在这里就是调用around方法。然后不管返回值为如何,都要实现after方法。至于实不实现after(其他同),由外部实现我们的接口决定。
我们可以这么实现拦截器接口:
public class InterceptorImpl implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法前逻辑");
return true;
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法后逻辑");
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("取代了被代理对象的方法");
}
}
JDK动态代理核心类
与JDK动态代理实现的一样,核心逻辑没有变,依然是实现bind和invoke方法,不同的是,得利用拦截器来写入插入逻辑,我们之前的invoke的逻辑是这么实现的:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入逻辑方法之前");
System.out.println("在调度真实对象之前的服务");
Object returnObj = method.invoke(target, args);
System.out.println("在调度真实对象之后的服务");
return returnObj;
}
可见插入前后的逻辑我们是直接用打印来验证。这里外部接口实现了拦截器,我们替换成拦截器的三个方法实现即可。代码如下:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(interceptorClass == null) {
return method.invoke(target, args);
}
Object returnObj = null;
Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();
if(interceptor.before(proxy, target, method, args)) {
returnObj = method.invoke(target, args);
}else {
interceptor.around(proxy, target, method, args);
}
interceptor.after(proxy, target, method, args);
return returnObj;
}
另外还有一点要变动的地方:bind方法。bind方法我们之前是这么实现的:
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
这里由于加入了拦截器的实现,拦截器又是外部帮我们实现的,所以我们的bind方法除了需要传入target对象,还需要传入拦截器对象。我们可以用反射得到拦截器对象这样方便利用配置文件动态配置,所以我们只需要把拦截器对象传入一个拦截器类的字符串。我们可以这么写:
public Object bind(Object target, String interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
这样就实现了有拦截器的【JDK动态代理核心类】。
当然,这样我们每次要调用依然得先new一个【JDK动态代理核心类】,再调用它的bind方法得到返回的对象,需要两步。我们可以简单缩减成一步(前面的JDK动态代理也可以简单缩成一步),即将bind方法写成static的,然后调用【JDK动态代理核心类】.bind(target, interceptorClass)返回一个proxy对象,那么我们可以改写成:
public JdkProxyExample(Object target, String interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
}
public static Object bind(Object target, String interceptorClass) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkProxyExample(target, interceptorClass));
}
可以看到我们加了一个构造函数,然后直接把外部传入的两个对象通过new JdkProxyExample(target, interceptorClass)的形式传给newProxyInstance的第三个参数,并把bind改成static方法。
这样一来,在外部可以直接通过下面一行代码得到proxy对象:
HelloWorld proxy = (HelloWorld) JdkProxyExample.bind(new HelloWorldImpl(), interceptorClass);
完整代码
//包含2个接口4个类,拦截器接口,拦截器类,目标接口,目标类,JDK动态代理核心类,测试类
//拦截器接口
package com.amiao.design_pattern.dynamic_proxy.interceptor;
import java.lang.reflect.Method;
public interface Interceptor {
public boolean before(Object proxy, Object target, Method method, Object[] args);
public void after(Object proxy, Object target, Method method, Object[] args);
public void around(Object proxy, Object target, Method method, Object[] args);
}
//拦截器类
package com.amiao.design_pattern.dynamic_proxy.interceptor;
import java.lang.reflect.Method;
public class InterceptorImpl implements Interceptor {
@Override
public boolean before(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法前逻辑");
return false; //这里为false,则不调用目标原有方法,直接调用around的替代方法
}
@Override
public void after(Object proxy, Object target, Method method, Object[] args) {
System.out.println("反射方法后逻辑");
}
@Override
public void around(Object proxy, Object target, Method method, Object[] args) {
System.out.println("取代了被代理对象的方法");
}
}
//目标接口
package com.amiao.design_pattern.dynamic_proxy.jdk;
public interface HelloWorld {
public void sayHelloWorld();
}
//目标类
package com.amiao.design_pattern.dynamic_proxy.jdk;
public class HelloWorldImpl implements HelloWorld {
public void sayHelloWorld() {
System.out.println("Hello World");
}
}
//JDK动态代理核心类
package com.amiao.design_pattern.dynamic_proxy.interceptor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyExample implements InvocationHandler {
private Object target = null;
private String interceptorClass = null;
public JdkProxyExample(Object target, String interceptorClass) {
this.target = target;
this.interceptorClass = interceptorClass;
}
public static Object bind(Object target, String interceptorClass) {
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkProxyExample(target, interceptorClass));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(interceptorClass == null) {
return method.invoke(target, args);
}
Object returnObj = null;
Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();
if(interceptor.before(proxy, target, method, args)) {
returnObj = method.invoke(target, args);
}else {
interceptor.around(proxy, target, method, args);
}
interceptor.after(proxy, target, method, args);
return returnObj;
}
}
//测试类
package com.amiao.design_pattern.dynamic_proxy.interceptor;
import com.amiao.design_pattern.dynamic_proxy.jdk.HelloWorld;
import com.amiao.design_pattern.dynamic_proxy.jdk.HelloWorldImpl;
public class TestMain {
public static void main(String args[]) {
HelloWorld proxy = (HelloWorld) JdkProxyExample.bind(new HelloWorldImpl(), "com.amiao.design_pattern.dynamic_proxy.interceptor.InterceptorImpl");
proxy.sayHelloWorld();
}
}
/*
* 运行结果
* 反射方法前逻辑
* 取代了被代理对象的方法
* 反射方法后逻辑
* */