使用Java动态代理实现的拦截器

来源:互联网 发布:杜尔清洗机编程 编辑:程序博客网 时间:2024/06/06 03:07

        声明:本文中通过Java的动态代理实现的拦截器只是能够在目标方法执行之前、之后调用拦截器方法,属于比较初级的应用。

        在软件开发领域,有一条很重要的规则:Don’t Repeat Yourself,即DRY规则,意思是不要书写重复的代码。此处所说的重复的代码是指功能性的代码,比如在很多方法里面用到排序,就可以将排序方法提取出来,写成一个功能方法,在需要用到的地方调用一下即可。这样做,比重复书写(或者使用复制、粘贴)相同的代码节省时间,而且更有益于软件后期的升级、维护中。

        拦截器更是对调用方法的改进。拦截器只是一种行为,从代码的角度,拦截器是一个类,包含方法,只是这个方法可以在目标方法调用之前“自动”执行(所谓自动,是指无需开发者关心,由系统来驱动调用)。

        首先定义一个Dog接口,因为JDK动态代理只能对实现了接口的实例生成代理。

public interface Dog {    // info方法声明    public void info();    // run方法声明    public void run();    // stand方法声明    public void stand();}

        为了正常使用该Dog实例,需提供该接口的实现类。

public class DogImpl implements Dog{    public void info() {        System.out.println("猎狗");    }    public void run() {        System.out.println("飞速移动中。。。");    }    public void stand(){        System.out.println("站立不动。");    }}
        上面代码没有丝毫特别,比较常见的定义接口,并实现接口。
        下面实现拦截Dog实例的拦截器类:
public class DogIntercepter {    public void method1() {        System.out.println("=======模拟通用方法一======");    }        public void method2() {        System.out.println("=======模拟通用方法二======");    }        public void method3() {        System.out.println("=======模拟通用方法三======");    }        public void method4() {        System.out.println("=======模拟通用方法四======");    }}
        可以发现,上面的拦截器类只是一个普通的Java类。所以要想该拦截类实现其拦截的目的,就需要通知系统。关键就在下面的ProxyHandler类了。该类需要实现InvocationHandler接口,InvocationHandler是代理实例的调用处理程序实现的接口。每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。

public class ProxyHandler implements InvocationHandler{    // 需要代理的目标对象    private Object target;    // 创建拦截器实例    DogIntercepter di = new DogIntercepter();    // 执行代理的目标方法时,该invoke方法会被自动调用。    public Object invoke(Object proxy, Method method, Object[] args)            throws Throwable {        Object result = null;        // 如果被调用方法的方法名为info        if(method.getName().equals("info")){            di.method1();            result = method.invoke(target, args);            di.method2();        } else if ("run".equals(method.getName())){            di.method3();            result = method.invoke(target, args);            di.method4();        } else {            result = method.invoke(target, args);        }        return result;    }    // 用于设置传入目标对象的方法    public void setTarget(Object o) {        this.target = o;    }}
        可以看到,ProxyHandler类与Dog类没有丝毫关联之处(如果说info是Dog的方法名,但是info方法是否属于Dog实例,却没有限定,因此,可以认为ProxyHandler与Dog实例没有丝毫关联),这样就实现了Dog实例与拦截器类的解耦(解耦这个词用在这个地方只是显得很很牛的样子,实际上在ProxyHandler中的info方法使用的是字符串,相对于直接调用,不易排错,仁者见仁智者见智吧)。
        通过ProxyHandler类,系统实现了在执行info方法之前,调用拦截器method1方法,在执行info方法之后,调用拦截器method2方法(run方法类似)。
        到了此时,系统还需要一个能够根据目标对象生成一个代理对象的代理工厂。代理工厂负责根据目标对象和对应的拦截器生成新的代理对象,代理对象里的方法时目标方法和拦截器方法的组合。
public class MyProxyFactory {    /**     * 实例Service对象     */    public static Object getProxy(Object objext) throws Exception {        // 代理的处理类        ProxyHandler handler = new ProxyHandler();        // 把该Dog实例托付给代理操作        handler.setTarget(objext);        // return Proxy.getProxyClass(DogImpl.class.getClassLoader(), objext.getClass().getInterfaces())        //            .getConstructor(new Class[]{InvocationHandler.class})        //            .newInstance(new Object[]{handler});        // 等价于:        // 返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。        // 第一个参数是用来创建动态代理的ClassLoader对象,只要该对象能访问Dog接口即可        // 第二个参数是接口数组,代理该接口数组。代理类要实现的接口列表。        // 第三个参数是代理包含的处理实例,指派方法调用的调用处理程序 。        return Proxy.newProxyInstance(DogImpl.class.getClassLoader(), objext.getClass().getInterfaces(), handler);    }}
        仔细研读上面代理工厂的返回对象是Proxy.newProxyInstance方法根据数组动态创建代理类实例的,第二个参数是获取代理类的接口数组,创建的代理类是JVM在内存中动态创建,该类实现参数里接口数组中的全部接口。因此,本文中的动态代理要求被代理的必须是接口的实现类,否则无法为其构造相应的动态实例。
        下面编写主程序进行测试:
public class TestDog {    public static void main(String[] args) throws Exception {        // 创建一个Dog实例,该实例将被作为代理的目标对象        Dog targetObject = new DogImpl();        Dog dog = null;        // 以目标对象创建代理        Object proxy = MyProxyFactory.getProxy(targetObject);        if(proxy instanceof Dog){            dog = (Dog)proxy;        }        // 测试代理的方法        dog.info();        dog.run();        dog.stand();    }}
        在主程序中,先创建一个Dog实例作为目标对象,然后以该目标对象为基础,创建该对象的代理对象(将拦截器方法和目标方法拼接在一起),生成的代理对象就能够有拦截器的效果了。
        效果如图:

        以上便是实现拦截器功能的动态代理的简单实现。

原创粉丝点击