拦截器就是一个类,其实现的原理是java的动态代理。
在此,首先了解一下java的代理模式。代理模式就是指一个人或一个机构代替另一个人或机构去做一些事情。例如,书店就是出版社的一个代理,而要做的事情就是代理。
为什么会需要代理模式?
一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
在代理模式中有3个重要的角色:
1.抽象主题角色:真实主题与代理主题的共同接口。
2.真实主题角色:定义了代理角色所代表的真实对象
3.代理主题角色:含有对真实主题角色的引用,代理角色通常是在客户端调用真实对象的方法之前或之后执行某些操作,而不是单纯的返回真是对象。
在上面这个例子中,抽象主题角色就是卖书,真实主题角色是出版社,代理主题角色是书店。
使用代理模式的基本步骤如下
1.创建一个抽象类,这个抽象类代表着抽象主题角色。
在此例中,创建抽象类Subject,其有个抽象方法,sailBook,具体实现如下:
package cn.tshining.proxy;
public abstract class Subject {
public abstract void sailBook();
}
2.生成真实主题角色。这个类将继承抽象类
package cn.tshining.proxy;
public class RealSubject extends Subject {
@Override
public void sailBook() {
System.out.println("卖书");
}
}
3.创建代理主题角色,这个类也继承抽象类
package cn.tshining.proxy;
public class ProxySubject extends Subject {
private RealSubject realSubject;
@Override
public void sailBook() {
doDazhe();
if (realSubject == null) {
realSubject = new RealSubject();
}
realSubject.sailBook();
give();
}
public void doDazhe() {
System.out.println("打折");
}
public void give() {
System.out.println("送代金券");
}
}
4.新建一个Client类来进行测试,在该类中实例化一个代理主题角色,然后将引用赋值给其父类接口,然后调用其方法。
package cn.tshining.proxy;
public class Client {
public static void main(String[] args) {
Subject proxy = new ProxySubject();
proxy.sailBook();
}
}
运行后结果显示如下:
打折
卖书
送代金券
这只是一个简单的示例,演示了代理模式的工作原理。
但是在代理模式中,客户端每次调用真实主题角色对象的方法时,就要生成一个真实主题角色对象,还有一个代理主题角色对象,这会造成资源的极大浪费。
所以JDK中提供了一个Proxy类来实现动态代理。动态代理是通过Proxy类动态地生成代理主题角色。
现在用动态代理来重写上面的例子:
实现动态代理的步骤如下:
1.创建抽象主题角色。但是这里不再是一个抽象类,而是一个接口,因为JDK的动态代理只能对实现了接口的实例来生成动态代理。
package cn.tshining.dynamicproxy;
public interface Subject {
public void sailBook();
}
2.创建真实主题角色。该类实现了上面的接口。
package cn.tshining.dynamicproxy;
public class RealSubject implements Subject {
@Override
public void sailBook() {
System.out.println("卖书");
}
}
3.新建处理类,该类实现invocationHandler接口,它可以动态调用目标对象中的方法,在该类代码中同样包含真实主题角色引用。
package cn.tshining.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyHandler implements InvocationHandler {
private Object realSubject;
public void setRealSubject(Object realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result;
doDazhe();
result = method.invoke(realSubject, args);
give();
return result;
}
public void doDazhe(){
System.out.println("打折");
}
public void give(){
System.out.println("送代金券");
}
}
4.新建一个Client类用来测试,在该类中新建一个真实主题对象实例,并将其设置到代理的处理类中,通过Proxy类的newProxyInstance方法来穿件动态代理实例。
package cn.tshining.dynamicproxy;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
MyHandler mh = new MyHandler();
mh.setRealSubject(realSubject);
Subject proxySubject = (Subject) Proxy.newProxyInstance(
realSubject.getClass().getClassLoader(), realSubject.getClass()
.getInterfaces(), mh);
proxySubject.sailBook();
}
}
在这个类中用到了Proxy的newProxyInstance方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
throws IllegalArgumentException
该方法接受3个参数,loader - 定义代理类的类加载器,interfaces - 代理类要实现的接口列表,h - 指派方法调用的调用处理程序
运行该示例,结果为:
打折
卖书
送代金券
在动态代理中,代理对象是系统动态产生的,动态代理必须依靠借口来实现。