写点什么

什么是动态代理

用户头像
Rayjun
关注
发布于: 2020 年 10 月 27 日
什么是动态代理

在上一篇文章中,详细介绍了 Java 中的反射机制,反射为 Java 提供了运行时修改程序的能力。


与之相对的,Java 中有另外一个技术也经常被提及,动态代理


这个技术在 Java 中同样在很多框架中同样得到了大量的应用。


那么动态代理到底是什么,和反射又有什么关系?


💡本文基于 OpenJDK11


1. 代理模式


在讲动态代理之前,我们需要先了解一下代理模式。


假设现在有一个 RPC 接口,需要统计每个 RPC 接口的调用时间,但是这些统计执行时间的代码于业务逻辑没有关系,这些代码适合独立出来。


public interface Hello {    void sayHello(String name);}
public class HelloImpl implements Hello { @Override public void sayHello(String name) { System.out.println("Hello " + name); }}
复制代码


使用代理模式就是一个比较好的办法,代理模式通常用于为现有的类添加额外的功能,而且不用修改现有的代码。


代理模式可以分成静态代理动态代理


需要注意,如果使用动态代理,最好现有的类是基于接口来实现的。如果不是基于接口,那么这个类只能实现静态代理,而无法实现基于反射的动态代理。


2. 静态代理实现


静态代理的实现比较简单,核心就是实现这个接口,然后就可以在这个实现中调用目标对象的方法,并且可以做一些额外的事情。


public class HelloStaticProxy implements Hello {
private Hello helloImpl;
public HelloStaticProxy(Hello helloImpl) { this.helloImpl = helloImpl; }
@Override public void sayHello(String name) { long begin = System.currentTimeMillis(); helloImpl.sayHello(name); System.out.println("Invoke time: " + (System.currentTimeMillis() - begin) + " ms"); }}
复制代码


上面的代码就可以统计出 sayHello 这个 rpc 接口的调用时间。


但是静态代理有一个很大的问题,假设有 n 个 rpc 接口,就需要把上面的逻辑重复实现很多遍。这对于大型系统或者通用框架中肯定是不能接受的。


但是静态代理的优点是性能好,在只有个别类需要被代理的时候,静态代理还是首选。


3. 动态代理实现


在 Java 中,动态代理有多种实现。


最直接的一种就是通过反射来实现。代码也不复杂,与上面静态代理不同的地方在于,使用动态代理可以生成任何类的代理。


如果选择使用反射来实现动态代理,那么就要求这个被代理的类是基于接口实现的。


下面代码中 InvocationHandlerProxy 都在 java.lang.reflect 中:

Hello hello = new HelloImpl();HelloDynamicProxyHandler handler = new HelloDynamicProxyHandler(hello);// 生成的动态代理对象Hello helloProxy = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), handler);
// 调用方法helloProxy.sayHello("ray");

public class HelloDynamicProxyHandler implements InvocationHandler {
private Object target;
public HelloDynamicProxyHandler(Object object) { this.target = object; }
@Override public Object invoke(Object o, Method method, Object[] params) throws Throwable {
long begin = System.currentTimeMillis(); Object result = method.invoke(target, params); System.out.println("Invoke time: " + (System.currentTimeMillis() - begin) + " ms");
return result; }}
复制代码


其实这种实现很容易理解,我把上篇文章中利用反射来动态执行方法的代码贴在这里:

// 利用反射动态执行方法public Object methodInvoke(Object o, Method method, Object[] params) {    try {        return method.invoke(o, params);    } catch (IllegalAccessException e) {        e.printStackTrace();    } catch (InvocationTargetException e) {        e.printStackTrace();    }    return null;}
复制代码


与上面动态代理的实现基本一样,这是动态代理最直观,最简单的一种实现方式。


相比于静态代理,动态的性能就要差很多,但是更灵活。


其他的实现就需要引入额外的依赖,比如 cglib 等。cglib 是通过生成被代理类的子类对象来作为动态代理,所有就不要求被代理类是基于接口实现的。


这些具体的实现后续再介绍,这篇文章重点还是放在动态代理本身。


4. 动态代理的应用


动态代理在 Java 最常用的场景就是 AOP 编程和解耦。


在 Spring 中 AOP 的实现有两种,反射和 cglib,可以自由选择。


spring.aop.proxy-target-class=true
复制代码


在 Java8 以前,选择使用 cglib 更多是因为性能的原因,但是在之后,就没必要了。


除了 AOP,动态代理也可以用于解耦,典型的情况就是在 RPC 中,使用动态代理,可以让调用远程的接口和本地方法一样简单。


同时动态代理还可以将很多于业务无关的细节屏蔽,比如权限,统计等等。


文 / Rayjun

本文首发于微信公众号


欢迎关注我的微信公众号


发布于: 2020 年 10 月 27 日阅读数: 49
用户头像

Rayjun

关注

程序员,王小波死忠粉 2017.10.17 加入

非著名程序员,还在学习如何写代码,公众号同名

评论

发布
暂无评论
什么是动态代理