boxmoe_header_banner_img

梦中的登神殿,天才的乱葬岗

加载中

文章导读

动态代理篇


avatar
RootReapers 2026年5月14日 74

代理模式

定义:为其他对象提供一种代理以控制这个对象的访问


参考文章:

https://zhuanlan.zhihu.com/p/72644638


例子:Github

我们需要访问Github,在国内需要挂proxy

走向是:

电脑->proxy节点->Github服务器


代理模式可以在不修改被代理对象的基础上,通过拓展代理类,进行一些功能的附加与增强,值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类


首先是静态代理

以租房为例,我们一般用租房软件、找中介或者找房东。这里的中介就是代理者。


首先定义一个提供了租房方法的接口

public interface IRentHouse {
    void rentHouse();
}

定义一个类去实现租房方法(相当于房东)

public class RentHouse implements IRentHouse {
    @Override
    public void rentHouse() {
        System.out.println("租了一间房子。。。");
    }
}

再定义一个代理类(中介)

房东把租房这件事交给中介去办,同时,中介还能负责其他业务(维修/管理)

public class IntermediaryProxy implements IRentHouse {

    private IRentHouse rentHouse;

    public IntermediaryProxy(IRentHouse irentHouse){
        rentHouse = irentHouse;
    }

    @Override
    public void rentHouse() {
        System.out.println("交中介费");
        rentHouse.rentHouse();
        System.out.println("中介负责维修管理");
    }
}

public class Main {

    public static void main(String[] args){
        //定义租房
        IRentHouse rentHouse = new RentHouse();
        //定义中介
        IRentHouse intermediary = new IntermediaryProxy(rentHouse);
        //中介租房
        intermediary.rentHouse();
    }
}

这里主要运用了多态的特性


在main中我们写了

IRentHouse rentHouse = new RentHouse();
IRentHouse intermediary = new IntermediaryProxy(rentHouse);
intermediary.rentHouse();

写法如下:

接口类型 名字 = new 接口实现类的构造方法;


分析一下作用:

IRentHouse是我们定义的接口,指定一个变量名rentHouse

表示它是接口类型的变量

但是右边可以看到,new RentHouse()

调用了RentHouse的构造方法,而RentHouse是IRentHouse接口的实现类


右边构造方法返回的是一个RentHouse类型的对象

而左边预期接收的是一个IRentHouse接口类型的对象

为什么不报错呢?左右两边的对象类型完全都不一样了


看起来就好比:

我要一杯7分糖的冷饮,然后服务器给我一杯咕嘟冒泡的开水


这就涉及到java多态的特性了


右边的类实际是接口的实现类,和接口是有关系的

左边的接口类型是可以接收其实现类对象的


具体细节可以从这个网站学习

https://how2j.cn


这样创建的变量

能够访问到实现类里面实现的接口抽象方法

其他方法无法访问

也就是rentHouse变量能够访问RentHouse类里面写好的那个rentHouse方法


来到下一步

//定义中介
IRentHouse intermediary = new IntermediaryProxy(rentHouse);

创建一个代理类(中介)

看一下具体怎么实现的

public class IntermediaryProxy implements IRentHouse {

    private IRentHouse rentHouse;

    public IntermediaryProxy(IRentHouse irentHouse){
        rentHouse = irentHouse;
    }

    @Override
    public void rentHouse() {
        System.out.println("交中介费");
        rentHouse.rentHouse();
        System.out.println("中介负责维修管理");
    }
}

同样实现了IRentHouse

声明了一个属性,暂时没赋值

private IRentHouse rentHouse;


定义了一个构造方法,接收外部传入的IRentHouse类型参数

然后将该参数赋值给rentHouse

然后在重写的rentHouse方法中,既调用了自己的部分(收中介费&管理),又调用了传入对象自身的rentHouse(租房)


静态方法有什么缺点?

如果方法接口不止一个方法

那么被代理类就要逐个实现方法

并且代理类也需要逐个实现方法


这样就会很麻烦

可不可以自己在代理类写一个方法类似

public void execute(){
  method.invoke(user,args)
}

执行这个方法就能调用被代理的接口实现类的实现方法

但是这个方法咋获取?method从哪拿到接口实现类的实现方法?

这个我们自行开发是很困难的,但是JDK提供了官方的实现

出现了动态代理

代理类不用挨个实现,而是接收一切方法然后完成反射调用


首先第一步:

实现InvocationHandler接口,这是JDK内置的动态代理接口

public class IntermediaryProxy implements InvocationHandler {


    private Object obj;

    public IntermediaryProxy(Object object){
        obj = object;
    }

    /**
     * 调用被代理的方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.obj, args);
        return result;
    }

}

对比静态代理

public class IntermediaryProxy implements IRentHouse {

    private IRentHouse rentHouse;

    public IntermediaryProxy(IRentHouse irentHouse){
        rentHouse = irentHouse;
    }

    @Override
    public void rentHouse() {
        System.out.println("交中介费");
        rentHouse.rentHouse();
        System.out.println("中介负责维修管理");
    }
}

动态代理接收任意Object,静态代理接收固定对象

动态代理不再逐个重写方法,而是写了一个任意方法的反射调用


动态代理的实现首先需要一个Proxy对象

可以看到,Proxy类的构造方法是private私有的,说明不能直接调构造方法创建对象


但是它提供了一个方法

Proxy.newProxyInstance()

通过这个方法即可获取到Proxy实例

这个方法需要三个参数

1.类加载器(需要该代理对象代理什么实现类)

2.需要实现的接口类(需要该代理对象代理什么接口)

3.动态代理的调用处理器(要做的事情)

为什么是这三个


仔细思考一下,这是一个代理类

从静态代理去看

1.需要知道接口类型

对应左边

IRentHouse rentHouse = new RentHouse();

2.需要知道实现类

对应右边

IRentHouse rentHouse = new RentHouse();

3.知道了代理的对象,要干啥

对应

IRentHouse intermediary = new IntermediaryProxy(rentHouse);

前两个很容易理解,稍微学过一点反射的都会

第三个咋实现呢?

这里JDK就已经提供了接口,我们只需要实现方法体即可

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

public class IntermediaryProxy implements InvocationHandler {


    private Object obj;

    public IntermediaryProxy(Object object){
        obj = object;
    }

    /**
     * 调用被代理的方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        method.invoke(this.obj, args);
        return null;
    }

}

如上,重写invoke方法即可,这就是调用处理器


import java.lang.reflect.Proxy;

public class Main {

    public static void main(String[] args){
        //定义租房
        IRentHouse rentHouse = new RentHouse();
        
        //定义中介(创建调用处理器,处理rentHouse这个被代理的对象)
        IntermediaryProxy intermediary = new IntermediaryProxy(rentHouse);
        
        IRentHouse o = (IRentHouse) Proxy.newProxyInstance(rentHouse.getClass().getClassLoader(), rentHouse.getClass().getInterfaces(), intermediary);
        o.rentHouse();
        
    }
}

如上即可完成动态代理调用


在反序列化漏洞中有啥用

理想情况:

假如在B类的f方法触发了危险方法

且A的readObject方法接收一个对象,并且调用该类的f方法

此时传入B的实例化对象即可


但是现实情况是:

A的readObject方法接收一个对象(假设为另一个对象O),并且调用该类的abc方法

就是不让你调用f,气死你

假如,O类是一个动态代理,它的invoke里面调用了f方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    method.invoke(this.obj, args);
    obj.f;
    return null;
}

它对外接收一个obj对象

那么此时对O类传入B

由于是动态代理

外面不管调用什么,invoke方法都会被触发(jvm底层机制)


链子:

A::readObject(O)触发O.abc


O类在创建对象的构造方法时接收一个另外的对象

O o1 = new O(B)

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    method.invoke(B, args);
    B.f;
    return null;
}

从而完成危险方法调用





评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字
插入代码