为什么会有动态代理

本文探讨了静态代理模式的局限性,介绍了JDK动态代理如何通过接口和InvocationHandler降低耦合,并对比了CGlib动态代理的补充作用。重点讲解了如何在需求变化时,利用抽象和参数传递实现灵活的代理设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

对应的代理业务图:

注(以下关于“代理对象”的图标采用“←”标识)

这里我们将B看做是“被代理对象”,箭头←看做是“代理对象”。

代理模式的uml图:

对应的代码展示:

public class Client 
{
    public static void main(String[] args) 
    {
        Proxy daili=new Proxy();
        daili.GiveDolls();
        daili.GiveFlowers();
        daili.GiveChocolate();
    }
}
public interface IGiveGift {
    void GiveDolls();
    void GiveFlowers();
    void GiveChocolate();
}
public class Pursuit implements IGiveGift {
    public Pursuit(){

    }

    @Override
    public void GiveDolls() {
        System.out.println("送你洋娃娃");
    }

    @Override
    public void GiveFlowers() {
        System.out.println("送你鲜花");
    }

    @Override
    public void GiveChocolate() {
        System.out.println("送你巧克力");
    }
}

 

public class Proxy implements IGiveGift {
    private Pursuit gg;
    public Proxy(){

    }
    public Proxy(){
        gg=new Pursuit();

    }
    @Override
    public void GiveDolls() {
        gg.GiveDolls();
    }

    @Override
    public void GiveFlowers() {
        gg.GiveFlowers();
    }

    @Override
    public void GiveChocolate() {
        gg.GiveChocolate();
    }

}

在Proxy类中将被代理对象Pursuit作为Proxy类的成员变量,从而来控制对Pursuit对象的访问。此时代理对象与被代理对象之间是关联关系,耦合性较高。

思考以上代理模式已经可以完成代理业务了,为什么后面会有动态代理?静态代理的缺点是什么?动态代理如何解决静态代理中的缺点?jdk的动态代理又是如何做的?有了jdk的动态代理怎么还有cglib的动态代理呢?

带着以上问题我们来逐一分析。

在以上代码的基础上,随着客户需求的增加,现在想要增加被代理类,要如何操作?

方式一:一个代理类代理多个被代理对象,在Proxy类中添加被代理类。

这样做会破坏我们的开闭原则,并且会导致代理类庞大。

方式二:为每个被代理类单独创建代理对象。

这样会导致类剧增,并且重复代码过多,存在冗余。

那要如何解决呢?

做架构师的人或者是精通设计模式的人都应该知道,我们解耦的核心在于--抽象!

在面对用户需求变更经常发生变动的代码来说,我们首先要想到的就是将变与不变分离。即将不变的部分抽象出来,将变的部分以参数的形式传入。

以上静态代理中经常要发生变化的是Proxy类中的Pursuit类,也就是被代理类,以及被代理类所实现的接口方法。

而不变的是代理的思想,即代理类在执行方法时,并没有自己的实现方法,而是调取的被代理对象的方法。

这样我们就将变与不变进行了划分。

那具体的代码是如何体现的呢?我们以JDK的动态代理为例。

宏观的uml图(以下uml图中我们可以看到B(被代理对象),而←(代理对象)则是动态生成的,所以在此未展示)

对应代码:

public class Client {
    public static void main(String[] args) {
        //在文件夹中显示自动生成的.class文件
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        //实例化“调用处理程序”,并将“目标对象"作为参数传入,由此可以让“目标对象”去做事情
        SubjectHandler subjectHandler=new SubjectHandler(new RealSubject());
        //实例化代理类,该“代理类”实现与“目标类”同样的接口
        ISubject proxySubject=(ISubject)Proxy.newProxyInstance(
                subjectHandler.getClass().getClassLoader(),
                new RealSubject().getClass().getInterfaces(),
                subjectHandler);
        //通过代理类调用“请求方法”
        proxySubject.request();

    }
}
public interface ISubject {
    /**
     * 发起请求的方法
     */
    void request();
}
public class RealSubject implements ISubject {
    /**
     * 目标对象发起请求的方法
     */
    @Override
    public void request() {
        System.out.println("发起请求");

    }
}
public class SubjectHandler implements InvocationHandler {
    //设置一个用来接收“目标对象”的成员变量
    Object target;
    //实例化“调用处理程序”时,将“目标对象”作为参数传入
    public SubjectHandler(Object target){
        this.target=target;
    }
    //实现“电调用处理程序”的invoke方法,来执行客户端通过“代理类”调用的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //通过反射调取传入的method方法,并将method方法执行后的结果返回
        return method.invoke(this.target,args);
    }
}

接着我们上面的静态代理来说,不变的部分是代理类所执行的方法是通过调取被代理类的方法来运行的,实际还是被代理类去做事情。

不变的体现:

变的体现:

通过上面的代码我们还可以发现,在静态代理里面被代理对象B与代理对象←是关联关系,而到了JDK中则是通过InvocationHandler接口进行了隔离,降低了被代理对象B与代理对象←之间的耦合,这样代理就可以毫无波澜的拥抱变化了。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Elsa~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值