Java 动态代理使用总结

我想在网上查了一些有关 Java 动态代理相关的技术资料,发现讲的都是一些理论或者源码,没有太多实际的应用举例,让人看的云里雾里、似懂非懂。索性我就自己总结一下,方便后续在使用时进行快速查阅。

Java 动态代理技术其实是 AOP 编程思想的实现。AOP 编程思想可以简单的理解为:在不改变原有业务代码情况下,实现对原有业务功能的修改或扩展。

AOP 编程思想在我们的实际开发中经常使用,比如你开发了一个网站系统,某些功能需要有权限才能使用,你肯定不想在每个具体的方法上进行硬编码实现权限验证,这样很不好统一维护,你会编写一个权限过滤器,对用户的请求进行拦截,验证用户权限。这里的权限过滤器其实就是 AOP 编程思想的一个体现,在不改变原有业务功能代码的情况下,实现对原有业务功能的权限验证扩展。

Java 动态代理技术 AOP 编程思想的实现方式,本质上就是利用反射技术,为实现了【具体接口】的实现类对象,创建实现了【具体接口】的代理对象,通过对代理对象的 invoke 方法进行代码修改,从而在不改变原有实现类代码的前提下,对原有实现类实例化后的对象的相关方法进行功能修改或者扩展。

从上面的描述可以发现:要实现 Java 动态代理技术,首先必须要有一个接口,以及该接口的实现类。下面我们还是举一个简单的例子来详细介绍 Java 动态代理技术吧。

// 定义一个接口
public interface PersonInterface {

    //上午
    void Morning();

    //下午
    void Afternoon();

    //傍晚
    void Evening();

    //夜里
    void Night();
}

// 定义 Person 类,实现了 PersonInterface 接口中的方法
public class Person implements PersonInterface {

    @Override
    public void Morning() {
        System.out.println("上午:读书学习...");
    }

    @Override
    public void Afternoon() {
        System.out.println("下午:锻炼身体...");
    }

    @Override
    public void Evening() {
        System.out.println("傍晚:听歌娱乐...");
    }

    @Override
    public void Night() {
        System.out.println("夜里:早点睡觉...");
    }
}

现在我们要实现目标是:在不改变 Person 类中任何源代码的情况下,对 Person 类实例化后的对象中 Morning、Afternoon、Evening 这 3 个方法,进行功能增强或者修改,调用这 3 个方法打印的内容是修改后的内容,不是原有内容。

注意:要求必须使用 Person 类进行实例化对象,然后调用 Morning、Afternoon、Evening 这 3 个方法。

这就导致我们无论是采用一个新的类,去实现 PersonInterface 接口,重新实现 Morning、Afternoon、Evening 这 3 个方法,还是采用一个新的类,继承 Person 去重写 Morning、Afternoon、Evening 这 3 个方法,都是不可行的。因为最终我们实例化 Person 对象后,打印的还是 Person 中这 3 个方法的原有内容。

这里只能采用 Java 动态代理技术来实现,具体代码如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {
    public static void main(String[] args) {

        //实例化一个 Person 对象 wolfer
        Person wolfer = new Person();

        /*
        基于 wolfer 对象和 PersonInterface 接口,创建动态代理对象
        */

        //第一种方式:采用匿名内部类实现
        PersonInterface wolferProxy = (PersonInterface) Proxy.newProxyInstance(
            //wolfer 对象的类加载器,其实就是 Person 类的加载器
            wolfer.getClass().getClassLoader(),
            //wolfer 对象所实现的接口的字节码,其实就是 Person 类所实现的接口的字节码
            wolfer.getClass().getInterfaces(),
            //动态代理的具体实现(匿名内部类实现方式)
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) {
                    try {
                        if ("Morning".equals(method.getName())) {

                            //如果是 wolfer 对象的 Morning 方法
                            //给 wolfer 对象的原有 Morning 方法代码前,增加一行代码
                            System.out.println("上午:睡到自然醒,然后起床...");

                            //调用 Morning 的原有方法
                            return method.invoke(wolfer, args);

                        } else if ("Afternoon".equals(method.getName())) {

                            //调用 Afternoon 的原有方法,获取返回值
                            Object obj = method.invoke(wolfer, args);

                            //如果是 wolfer 对象的 Afternoon 方法
                            //给 wolfer 对象的原有 Afternoon 方法代码后,增加一行代码
                            System.out.println("下午:去吃个大餐,补充能量...");

                            return obj;

                        } else if ("Evening".equals(method.getName())) {

                            //如果是 wolfer 对象的 Evening 方法
                            //直接改变了 Evening 原有方法的实现代码
                            System.out.println("傍晚:灯红酒绿,纸醉金迷...");

                            //由于 Evening 方法不需要返回值,因此这里返回 null 即可
                            return null;

                        } else {

                            //其它方法不进行改变和扩展,直接调用原有方法
                            return method.invoke(wolfer, args);
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        return null;
                    }
                }
        });

        //第二种实现方式:
        //由于动态代理的第 3 个参数 InvocationHandler 是仅有一个方法的接口,
        //因此可以简化为 Lambda 表达式的编写方式
        PersonInterface wolferProxy = (PersonInterface) Proxy.newProxyInstance(
            	wolfer.getClass().getClassLoader(),
            	wolfer.getClass().getInterfaces(),
                (proxy, method, proxyArgs) -> {
                    try {
                        if ("Morning".equals(method.getName())) {
                            System.out.println("上午:睡到自然醒,然后起床...");
                            return method.invoke(wolfer, proxyArgs);
                        } else if ("Afternoon".equals(method.getName())) {
                            Object obj = method.invoke(wolfer, proxyArgs);
                            System.out.println("下午:去吃个大餐,补充能量...");
                            return obj;
                        } else if ("Evening".equals(method.getName())) {
                            System.out.println("傍晚:灯红酒绿,纸醉金迷...");
                            return null;
                        } else {
                            return method.invoke(wolfer, proxyArgs);
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        return null;
                    }
                }
        );

        //上面两种实现方式,只保留一种即可

        //调用 wolfer 的动态代理对象的 Morning 方法,发现:
        //相比 Person 类的原有 Morning 方法,前面增加了一句打印
        wolferProxy.Morning();

        //调用 wolfer 的动态代理对象的 Afternoon 方法,发现:
        //相比 Person 类的原有 Afternoon 方法,发现后面增加了一句打印
        wolferProxy.Afternoon();

        //调用 wolfer 的动态代理对象的 Evening 方法,发现:
        //相比 Person 类的原有 Evening 方法,打印的内容被彻底改变了
        wolferProxy.Evening();

        //调用 wolfer 的动态代理对象的 Night 方法
        //由于动态代理没有对其修改和扩展,因此打印内容跟 Person 类原有的 Night 方法相同
        wolferProxy.Night();
    }
}

/*
打印内容如下所示:
上午:睡到自然醒,然后起床...
上午:读书学习...
下午:锻炼身体...
下午:去吃个大餐,补充能量...
傍晚:灯红酒绿,纸醉金迷...
夜里:早点睡觉...
*/

Ok,通过以上例子,应该能够快速理解并掌握 Java 动态代理技术了。

实际应用场景是:如果你引用了第三方的 jar 包,如果你感觉它的某些类的方法不太满足自身的需求,想要改变或增强的话,而这些类恰好也实现了某些接口,此时就可以使用 Java 动态代理技术就行修改或增强,来满足自身业务的需要。另外在 Java 的各种框架中,比如 Spring 相关框架也广泛使用 Java 动态代理技术。

希望以上内容对大家有用。

posted @ 2022-02-09 19:27  乔京飞  阅读(9668)  评论(0编辑  收藏  举报