零基础学Struts
上QQ阅读APP看书,第一时间看更新

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();
              }
        }

程序运行结果如下所示。

        开门
        搭电梯上楼
        关门

其实所谓的拦截器,就是通过目标对象创建代理对象。在该代理对象中包含了拦截器中的方法和目标中的方法。这样当执行目标方法时,拦截器中的方法会自动执行。