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

简介: 在软件开发领域,有一条很重要的规则:Don’t Repeat Yourself,即DRY规则,意思是不要书写重复的代码

声明:本文中通过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实例作为目标对象,然后以该目标对象为基础,创建该对象的代理对象(将拦截器方法和目标方法拼接在一起),生成的代理对象就能够有拦截器的效果了。

       效果如图:


image.png


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


目录
相关文章
|
2月前
|
监控 Java 数据管理
java会话跟踪和拦截器过滤器
本文介绍了Web开发中的会话跟踪技术——Cookie与Session,以及过滤器(Filter)和监听器(Listener)的概念和应用。Cookie通过在客户端记录信息来识别用户,而Session则在服务器端保存用户状态。过滤器用于拦截和处理请求及响应,监听器则监控域对象的状态变化。文章详细解释了这些技术的实现方式、应用场景和主要方法,帮助开发者更好地理解和使用这些工具。
51 1
|
3月前
|
Java Spring 数据库连接
[Java]代理模式
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
51 0
[Java]代理模式
|
2月前
|
Java
JAVA 静态代理 & 动态代理
【11月更文挑战第14天】静态代理是一种简单的代理模式实现,其中代理类和被代理类的关系在编译时已确定。代理类实现与被代理类相同的接口,并持有被代理类的实例,通过调用其方法实现功能增强。优点包括代码结构清晰,易于理解和实现;缺点是对于多个被代理类,需为每个类编写相应的代理类,导致代码量大增,维护成本高。动态代理则在运行时动态生成代理类,更加灵活,减少了代码冗余,但可能引入性能损耗和兼容性问题。
|
3月前
|
Java
深入理解Java动态代理
深入理解Java动态代理
97 1
|
3月前
|
Java
Java代码解释静态代理和动态代理的区别
### 静态代理与动态代理简介 **静态代理**:代理类在编译时已确定,目标对象和代理对象都实现同一接口。代理类包含对目标对象的引用,并在调用方法时添加额外操作。 **动态代理**:利用Java反射机制在运行时生成代理类,更加灵活。通过`Proxy`类和`InvocationHandler`接口实现,无需提前知道接口的具体实现细节。 示例代码展示了两种代理方式的实现,静态代理需要手动创建代理对象,而动态代理通过反射机制自动创建。
|
3月前
|
设计模式 缓存 Java
从源码学习Java动态代理|8月更文挑战
从源码学习Java动态代理|8月更文挑战
|
6月前
|
缓存 Java 测试技术
day27:Java零基础 - 动态代理
【7月更文挑战第27天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
48 2
day27:Java零基础 - 动态代理
|
4月前
|
监控 前端开发 Java
Java里的过滤器和拦截器是什么原理,如何选择?
Java里的过滤器和拦截器是什么原理,如何选择?
40 0
|
5月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
337 0
|
5月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
226 0