在 Java 动态代理中,代理对象能够通过调用 invoke 方法来增强被代理对象的原始方法。
Java 动态代理 程序为什么需要代理?代理长什么样? 对象如果嫌身上干的事太多,可以通过代理来转移部分职责。对象有什么方法想被代理,代理就一定要有对应的方法。
下面通过一个简单的例子来了解动态代理 现有一个类 BigStar 需要被代理,如何让代理类知道 BigStar 的哪些方法需要被代理呢?那么需要定义一个接口,这个接口将会声明 BigStar 中需要被代理的方法,再让代理类实现这个接口好了。同样,出于代理的规范,BigStar 类也需要实现这个接口。
需要被代理的 BigStar 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class BigStar implements Star{ private String name; public BigStar(String name) { this.name = name; } public String Sing(String name){ System.out.println(this.name + "正在唱" + name); return "谢谢大家!"; } public void Dance(){ System.out.println(this.name + "正在跳舞"); } }
Star 接口中声明了 BigStar 类需要被代理的方法 Sing 和 Dance :
1 2 3 4 public interface Star { String Sing(String name); public void Dance(); }
接下来我们要获取代理对象,通常用到 Proxy 类的 newProxyInstance() 方法。
Proxy.newProxyInstance() 方法 其定义是这样的:
1 2 @CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回类型为 Object
第一个参数用于指定类加载器,开发里都是用当前类的类加载器,这里即 ProxyUtil.class.getClassLoader() 。
第二个参数需要传入一个接口数组,用于指定生成的代理对象继承哪些接口,包含哪些方法。
第三个参数需要传入一个 InvocationHandler 接口的对象,由于接口不能直接实例化对象,所以我们这里需要用到 InvocationHandler 接口的匿名对象。
具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 Star starProxy = (Star) Proxy.newProxyInstance( // 指定类加载器 ProxyUtil.class.getClassLoader(), // 传入接口的Class对象 new Class[]{Star.class}, // 传入InvocationHandler接口的匿名对象 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } } );
在使用 idea 创建 InvocationHandler 的匿名内部类时我们会发现这里自动生成了一个重写的 invoke() 方法,划重点,这个 invoke() 方法很重要。
InvocationHandler 类的 invoke() 方法 代理对象需要做的增强功能(或者说要做的事情),就定义在这个方法里。
我们来看它的定义:
1 public Object invoke(Object proxy, Method method, Object[] args)
这三个参数的含义是什么呢?
我们知道,我们获取到的这个代理对象 starProxy 中是有 Sing 和 Dance 两个方法的,而且将来会被调用,就像这样:
那么此时第一个参数 proxy 获取到的就是对象 starProxy ,第二个参数 method 获取到的就是方法 Sing ,第三个参数 args 获取到的就是参数数组 Object[]{“爱我中华”} ,应该明白了吧。
那么接下来我们可以完善这个 invoke 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 新建一个 BigStar 对象 BigStar bigStar = new BigStar("张三"); // 创建代理对象 Star starProxy = (Star) Proxy.newProxyInstance( // 指定类加载器 ProxyUtil.class.getClassLoader(), // 传入接口的Class对象 new Class[]{Star.class}, // 传入InvocationHandler接口的匿名对象 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 当调用 Sing 方法时,打印 "收钱,准备唱歌场地" if (method.getName().equals("Sing")) { System.out.println("收钱,准备唱歌场地"); // 当调用 Dance 方法时,打印 "收钱,准备跳舞场地" } else if (method.getName().equals("Dance")) { System.out.println("收钱,准备跳舞场地"); } // 最后一定调用 bigStar 的原始方法,并将结果返回 return method.invoke(bigStar, args); } } );
最后调用代理类的 Sing 和 Dance 方法测试:
1 2 3 String sing = starProxy.Sing("爱我中华"); System.out.println(sing); starProxy.Dance();
返回结果如下:
最后我们再梳理一遍执行流程:
1 starProxy.Sing("爱我中华") -> InvocationHandler 的 invoke 方法(打印 "收钱,准备唱歌场地") -> bigStar.Sing("爱我中华")(并将返回值返回)
starProxy.Dance() 同理:
1 starProxy.Dance() -> InvocationHandler 的 invoke 方法(打印 "收钱,准备跳舞场地") -> bigStar.Dance()(无返回)
至此,我们对动态代理有了大概的了解。
不过,实际开发中,我们通常会自定义一个 ProxyUtil 工具类来获取代理对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class ProxyUtil { public static Star createProxy(BigStar bigStar) { Star starProxy = (Star) Proxy.newProxyInstance( // 指定类加载器 ProxyUtil.class.getClassLoader(), // 传入接口的Class对象 new Class[]{Star.class}, // 传入InvocationHandler接口的匿名对象 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 当调用 Sing 方法时,打印 "收钱,准备唱歌场地" if (method.getName().equals("Sing")) { System.out.println("收钱,准备唱歌场地"); // 当调用 Dance 方法时,打印 "收钱,准备跳舞场地" } else if (method.getName().equals("Dance")) { System.out.println("收钱,准备跳舞场地"); } // 最后一定调用 bigStar 的原始方法 return method.invoke(bigStar, args); } } ); return starProxy; } }
如此一来,我们的测试类中就可以这样写:
1 2 3 4 5 6 7 8 9 public class Test { public static void main(String[] args) { // 获取代理对象 Star starProxy = ProxyUtil.createProxy(new BigStar("张三")); String sing = starProxy.Sing("爱我中华"); System.out.println(sing); starProxy.Dance(); } }
这样就简洁很多了。