代理模式(萌新角度来理解)

代理模式

一、什么是代理

顾名思义,代理就是代替别人对一些事物进行处理,真实角色将业务交给代理人去完成。举个例子:房东A想要进行房间出租,但他觉得房子出租太麻烦不想等,所以他将房子就委托给中介(Proxy),让中介去完成相关的业务,这个过程就叫代理。
在这里插入图片描述
这个过程中用户直接找中介租房,而不是房东


二、代理模式

我们首先对代理模式中的角色进行解析:

  • 抽象角色:一般使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实对象,代理真实对象后,我们会继续做一些附属操作。

代理模式就是将代理这过程体现的思想运用到Java代码的设计架构中。具体思想:

  • 代理角色和真实角色具有相同联系的业务
  • 用户直接找代理咨询相关业务不直接找真实角色

现在,我就将这租房的过程用Java代码实现,分析一下代理模式到底是什么东西。

租房是一个业务(抽象角色),所以它是一个需要被实现的接口:

public interface Rent {
    public void rent();
}

这个业务(Rent接口)是由真实角色(房东Host类)委托的,所以真实角色(房东Host类)就需要将其实现:

public class Host implements Rent{

    @Override
    public void rent() {
        System.out.println("房东要出租房子!");
    }
}

代理角色(中介RentProxy类)就需要接受这个业务,所以也需要继承业务(Rent接口),并且因为是真实角色(房东Host类)委托的,所以真实角色(房东Host类)这一个类需要被封装为代理角色(中介RentProxy类)的一个属性。

public class RentProxy implements Rent{
    private Host host;				//真实角色
    public RentProxy(){}
    public RentProxy(Host host){
        this.host = host;
    }

    @Override
    public void rent() {      //代理类处理真实角色委托的业务
        host.rent();		  //核心业务
    }

    public void seeHouse(){			//代理类自己扩展的业务
        System.out.println("中介带你看房");
    }
}

而客户(Client类)则直接与代理角色(RentProxy类)进行交互,而不与真实角色进行交互

public class Client {
    public static void main(String[] args) throws Exception{
        Rent host = new Host();//实例真实角色
        RentProxy rentProxy = new RentProxy((Host) host);//实例代理角色
        rentProxy.rent();//代理角色执行委托的业务
    }
}

这个实现过程只是代理模式设计的一个小小的例子,看起来有点晦涩难懂,其实你只需要理解两点

  1. 业务是一个待解决的接口,真实角色和代理角色都需要实现这个业务
  2. 真实角色的业务是交给代理角色来处理的,真实角色就是代理角色的一个属性,并且代理角色会调用真实角色的方法

三、代理模式的好处以及弊端

通过上面一个例子,我们肯定的看不出代理模式的好处,因为一个业务需要一个接口两个类来实现,显然代码量增加了,所以代理模式的缺点就是:

一个真实角色会产生一个代理角色,一个代理只能代理同一类的真实角色所以会导致代码量翻倍

但是你仔细思考一下,当我有许多具有同类业务需要被实现时,我创建的许多真实角色只需要调用一个代理类来执行实现,并且我需要扩展其他业务时,我只需要在代理类中写代码,而不需要去动真实角色的代码。所以代理模式的优点:

  • 使真实角色的操作更加纯粹简单,只专注于核心业务
  • 可以提取公共操作部分(扩展其他公共业务)

四、静态代理

静态代理是在程序被JVM虚拟机加载前就已经编译好的,也就是说程序是被写死了,一但启动就不能改了,我用代码实现的过程就是静态代理,所以当你需要修改业务时,你需要停止程序后才能进行修改。


五、动态代理

动态代理是利用Java反射机制来动态实现代理过程,那么我们要想深入了解动态代理实现,就需要了解一下反射机制,这里我不做过多的描述,只是简单说一下反射机制原理:

对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法;

当我们写的类被编译时,Java虚拟机会自动生成一个Class类,并且将我们写的类的信息全部保存在这个类中,我们能通过这个Class类调用我们所写的类方法。

换句话说,正常程序是由Java源码到.class文件的字节码,而反射则是由.class字节码到Java源码的。(个人理解)

知道反射以后,我们再来聊一聊动态代理:

动态代理不仅需要反射机制而且还要了解两个类:

  1. Proxy:获取代理类
  2. InvocationHandler:调用处理程序

动态代理的三个组成部分

  1. 抽象角色:一般是接口或者抽象类(同静态代理)
  2. 真实角色:真实角色:被代理的角色 (同静态代理)
  3. 代理角色:动态获取代理类的程序类

光看概念肯定是看不懂,所以我来以房东租房问题的实现来介绍动态代理,与静态代理相同的部分代码就不重复写了,想看的见上:

代理角色:动态获取代理类的程序类

public class ProxyInvocationHandler implements InvocationHandler {
    private Rent rent;

    public void setRent(Rent rent){
        this.rent = rent;
    }
    //获取代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
    }
    //调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(rent,args);
        return result;
    }
}

代码中getProxy()方法是用来获取代理类的,可能你会产生疑问,不知道Proxy.newProxyInstance()的作用,其实就是返回一个接口的代理类,而这个代理类包含了Rent接口的所有方法。其中有三个参数

  1. 类加载器(暂时没弄明白)
  2. 需要代理的接口
  3. 指派方法调用的调用处理程序(InvocationHandler或其子类)

客户(调用程序):

public class Client {
    public static void main(String[] args) throws Exception{
        Host host = new Host();//真实角色
        ProxyInvocationHandler pih = new 	ProxyInvocationHandler();//创建代理程序
        pih.setRent(host);//设置需要代理的接口
        Rent proxy = (Rent) pih.getProxy();//获取代理类
        proxy.rent();//代理类执行对应真实角色的方法
    }
}

到这里,个人理解的动态代理就算结束了,可能你们角色动态代理没啥好的,那是因为我们在代理程序类中把需要代理的接口写死了,如果将Rent接口换成Object类型的话,那么就可以动态代理任何类了。所以现奉上万能代理程序类:

public class ProxyInvocationHandler implements InvocationHandler {
    private Object object;

    public void setObject(Object object){
        this.object = object;
    }
    public Object getProxy(){
        return Proxy.newProxyInstance(rent.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(object,args);
        return result;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序猿Halley

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

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

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

打赏作者

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

抵扣说明:

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

余额充值