java 静态代理与动态代理学习
0x01 意义
在生产环境中,代理模式有许多实际的好处,能够提升系统的可维护性、扩展性和性能。当然这是 GPT 的回答,可能有点宽泛。举个例子吧,客户希望租一套房子,但由于没有时间和经验,不想自己去寻找和联系房主。中介公司提供服务,帮助客户找到符合要求的房子,安排看房、谈判租金、处理租赁合同等事务。房主通过中介公司租出房子,避免了自己直接接触大量客户的麻烦。
这里说的中介就相当于是 Java 里的代理,Java中的代理模式类似于上面的代理,我们也是为一个类(委托类)创建一个代理类,来代表它来对外提供功能。
0x02 静态代理
代理类在编译时就已经确定,代理类和目标类都需要实现一致的接口。代理类直接持有目标类的引用,通过接口调用目标类的方法。
现在我们通过静态代理来实现上面说的找房的例子,中介代理房主,拥有租房,签合同的权限。
先实现同一个接口,代理类和目标类都需要实现同一个接口。在这里代理类就是中介,目标类就是房主。
我们先定义一个租房接口
1 | public interface RentService{ |
然后考虑实现房主类和中介类,这里房主只要考虑租房就行了,而中介要考虑的事就很多了(沟槽的鸣式
房主类
1 | public static class HouseOwner implements RentService{ |
中介类
1 | public static class HouseProxy implements RentService{ |
测试
1 | public static void main(String[] args) { |

0x03 动态代理
为什么有静态代理了还要用动态代理呢?动态代理相比于静态代理有什么优势呢?当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来。
当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。
实现
首先代理类不用再继承目标类的所有接口了,统一继承 InvocationHandler 类,还要重写 invoke 方法
1 | public static class HouseProxy implements InvocationHandler{ |
可以看到利用了反射技术,难怪可以在类的创建阶段开始代理,然后利用 Proxy.newProxyInstance 创建代理
1 | RentService proxyInstance = (RentService) Proxy.newProxyInstance( |
调用代理方法
1 | proxyInstance.rent();// |
断点调试发现 proxyInstance.rent() 就会进入代理类 HouseProxy 的 invoke 方法,我们可以打印一下方法名
1 | System.out.println(method.getName() + "方法被调用"); |

可以发现代理类的实现确实抽象出来了,只要维护目标类就行了。
0x05 思考
那么动态代理和反序列化漏洞又能扯上什么关系呢?
我们都知道 URLDNS 利用的是 HashMap 调用了 hash 方法然后就会触发传入对象 key hash的 HashCode 方法,从而传入 URL 对象来触发 URL 的 hashCode 方法。假如 key 没有调用 hashCode 方法怎么办呢?比如调用的是 key.aaa 方法
那我们就可以找一个动态代理类,我们可以把动态代理类传给 key ,不过动态代理类执行的是什么方法都会执行 public Object invoke 函数,可能这个 invoke 函数实现了 hashCode 方法,并且这个动态代理类也接受一个类作为参数,invoke 调用了我们传给这个动态代理类的参数(这个参数就是我们传的类)的 hashCode 方法,那我们就可以把 URL 类传给 这个动态代理。