6.2 拦截器介绍以及实现原理
通过前面的代理模式以及动态代理模式的知识,就能很好地理解什么是拦截器以及拦截器的实现原理,下面通过实例来介绍。
6.2.1 拦截器
什么是拦截器呢?它能实现什么功能呢?首先来假设一下吧。假如现在你坐电梯,在你进入电梯之前,电梯要首先开门你才能进入,同样当你进入电梯后,电梯将自动关门。这时电梯就可以看作是一个拦截器,在你进入它之前,它首先会自动开门,同样当你进入之后,它会自动关门。拦截器示意图如图6.2所示。
图6.2 拦截器示意图
拦截器的意思是这样的,在进行一个操作(调用方法)时,它会在用户执行操作前进行一系列操作,同样在用户操作完成后进行一系列操作。现在用程序来简单模拟搭电梯这一操作。
步骤如下。
(1)新建电梯类。在该电梯类中定义两个方法,分别代表“开门”和“关门”操作,代码如下所示。
package net.hncu.interceptor; public class Elevator { public void openDoor() { System.out.println("开门"); } public void closeDoor() { System.out.println("关门"); } }
(2)新建用户类。用户类中含有对电梯类的引用,并定义一个上楼的方法,该方法分别调用电梯的开门和关门方法,代码如下所示。
package net.hncu.interceptor;
public class User {
private Elevator ele = new Elevator();
public void up(){
ele.openDoor();
System.out.println("搭电梯上楼");
ele.closeDoor();
}
}
(3)新建一个测试类Client。在该类中实例化一个用户类并调用它的上楼方法,代码如下所示。
package net.hncu.interceptor; public class Clent { public static void main(String[] args) { User user = new User(); user.up(); } }
程序运行结果。
开门 搭电梯上楼 关门
6.2.2 拦截器实现原理
这里发现了一个问题,电梯应该是自动开门和自动关门的,而程序中是使用User类调用的。既然电梯应该是自动开门和自动关门的,也是就说这里的拦截器中的方法应该是自动执行的。
有同学肯定会问,一个方法要自动执行,这难道也可以实现吗?
其实这里所指的自动执行不是说真正的自动,而是指通过代码驱动来由系统自动执行。这就需要用到前面介绍的动态代理方面的知识。所谓的拦截其实就是动态地生成一个代理对象,而在这个代理对象中包含了对拦截器方法的调用。因此当调用该代理对象的方法同时,会调用拦截器中的方法,通过这样的方法就实现了动态调用拦截器方法的目的。现在通过程序来实现这一拦截器操作。
步骤如下。
(1)新建User接口,因为动态代理只能对实现了接口的实例来生成代理。该接口定义了一个搭电梯上楼的方法,代码如下所示。
package net.hncu.interceptor2;
public interface User {
//搭电梯上楼方法声明
public void up();
}
(2)新建User接口实现类UserImpl。在该类中实现up方法,代码如下所示。
package net.hncu.interceptor2; public class UserImpl implements User{ // 搭电梯上楼方法实现 public void up() { System.out.println("搭电梯上楼"); } }
(3)新建拦截器类,该类和前面定义的电梯类完全一样。在该类中定义两个方法,分别代表“开门”和“关门”操作,代码如下所示。
package net.hncu.interceptor2; //电梯类用来拦截 public class Elevator { //开门方法 public void openDoor() { System.out.println("开门"); } //关门方法 public void closeDoor() { System.out.println("关门"); } }
(4)新建处理类MyHandler,该类实现InvocationHandler接口。InvocationHandler接口是反射中非常重要的一个接口,它可以动态调用目标对象中的方法。在该类中同样包含被代理目标对象引用以及拦截器类的引用。通过该类,系统会实现在执行up()方法之前,调用拦截器中的openDoor()方法以及closeDoor()方法,代码如下所示。
package net.hncu.interceptor2; //处理类 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyHandler implements InvocationHandler { //被代理目标对象 private Object user; //电梯类实例 Elevator ele = new Elevator(); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result; ele.openDoor(); result = method.invoke(user, args); ele.closeDoor(); return result; } // 传入目标对象 public void setUser(Object user){ this.user = user; } }
(5)新建代理工厂类,该代理工厂接受目标代理对象创建一个处理类对象,并放回代理对象实例,代码如下所示。
package net.hncu.interceptor2; //代理工厂 import java.lang.reflect.Proxy; public class ProxyFac { public static Object getProxy(Object user) { //创建处理类实例 MyHandler mh = new MyHandler(); //传入user实例 mh.setUser(user); //返回代理类实例 return Proxy.newProxyInstance(UserImpl.class.getClassLoader(), UserImpl.class.getInterfaces(), mh); } }
(6)新建一个Client类用来测试。在该类中新建一目标对象用来代理,通过调用代理工厂获得代理实例,代码如下所示。
package net.hncu.interceptor2; public class Client { public static void main(String[] args) { //新建一目标对象,用来代理 User user = new UserImpl(); //通过代理对象获得代理实例 User proxyUser = (User) ProxyFac.getProxy(user); //调用代理实例的上楼方法 proxyUser.up(); } }
程序运行结果如下所示。
开门 搭电梯上楼 关门
其实所谓的拦截器,就是通过目标对象创建代理对象。在该代理对象中包含了拦截器中的方法和目标中的方法。这样当执行目标方法时,拦截器中的方法会自动执行。