23种设计模式——最好理解的代理模式

大家好,我是方圆,小小的代理模式


一、 到底啥是代理模式?

官方的话说,代理模式就是为目标对象提供另外一种访问方式,通过访问代理对象来间接访问目标对象。

我们来打个比方,简单点儿描述,比如我是房东,我要出租房屋,以前呢我直接租给住户,这样就是没有代理。但是现在我觉得我直接找客户出租房屋很麻烦,所以我就把房子交给中介,让中介帮我出租房子,我只管提供房子,中介帮我做带客户看房啊、收费等其他事物,这样,房东有了代理,这就是代理模式。

从这也能看出代理模式的一点好处,让房东专注于提供房子,其他事物由代理做,反映到程序中,就是,能让service层专注于提供服务,采用代理模式来实现其他事物,在不改变service层代码的情况下,提供了其他的功能。

二、静态代理模式

我们还是拿租房这个简单的例子来讲解

这是一个接口,房东实现这个接口,来提供租房子的业务
//租房
public interface Rent {
    void RentHouse();
}
这个是房东类,在程序中就位于service层
//房东
public class Host implements Rent{

    public void RentHouse() {
        System.out.println("房东出租房屋给客户");
    }
}
//我是代理,房屋中介
public class Proxy implements Rent{

    private Host host;

    public void setHost(Host host) {
        this.host = host;
    }

    public void RentHouse() {
        host.RentHouse();
    }
}
这个就是客户类
//客户,要租房子的人
public class Client {

    public static void main(String[] args) {

        //在没有中介的时候,直接找房东租房
        Host host = new Host();
        host.RentHouse();
        
    }
}

,房东直接租房子给客户。
下面,房东要找代理了。

public class Client {

    public static void main(String[] args) {

        //在没有中介的时候,直接找房东租房
        Host host = new Host();

        //这里是一个代理
        Proxy proxy = new Proxy();
        proxy.setHost(host);//房东把房子交给中介

        //中介代替房东出租房屋,调用出租房的方法
        //理论上还是房东出租给客户房屋,只不过这件事情被中介给做了
        proxy.RentHouse();
    }
}

在这里插入图片描述
我们这里没有用房东直接调用租房的方法,而是中介调用,同样实现了租房的功能,这样就实现了静态代理模式。
我们这里总结一下代理模式的好处

  1. 可以让service层专注于实现自己的业务,也就是房东只专注于提供房子,中介负责向客户出租和一些细枝末节的事情,我们同样可以在中介代码中,添加一下其他方法,并不影响房东的代码
  2. 要对业务就行扩展时,可修改代理的代码进行添加,而不需要修改service层源码

但是缺点也显而易见,静态嘛,不够灵活,代码冗余,一个service就需要一个代理,使代码翻倍。
为了解决这个缺点就出现了动态代理

三、动态代理模式

我们同样还是需要以上的Rent接口,和Host类
但是这次我们需要创建一个动态代理的类,ProxyInvocationHandler

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

public class ProxyInvocationHandler implements InvocationHandler {
//这个了实现了 InvocationHandler 接口
//每个代理的实例需要实现这个接口,从而来调用处理程序的实现
//invoke handler:翻译为中文就是调用处理程序

	//我们需要实现租房子的这个接口,所以定义一个变量
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成代理类的方法
    public Object getProxy(){
        //newProxyInstance() 创建代理实例方法
        // 这个方法中,第一个参数为Invocation类的类加载器
        //第二个参数,为要让代理实现的类的接口,也就是要实现租房子的接口
        //第三个参数,是Invocation类本身
        Object proxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
        return proxyInstance;

    }
    //这个invoke方法,官方介绍中说:
    //每一个代理实例Proxy都由一个关联的调用处理程序invoke,
    //当在代理实例上调用方法时,方法调用将被编码并分配到其调用处理程序的invoke方法
    
    //也就是说,当用代理调用方法的时候,这个invoke方法就会帮代理实现方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //在这里,invoke会帮助Proxy代理实例来实现方法
    //其中参数 method就是要实现的方法
    //方法调用invoke(rent,args)来返回结果
    //其中参数rent是要被实现方法的接口,args默认填写
        Object result = method.invoke(rent, args);
        return  result;
    }
}

下面我们就用客户类来实现一下这个动态代理

public class Client {
    public static void main(String[] args) {
        //这里还是有一个房东准备要出租房子
        Host host = new Host();

        //创建ProxyInvocationHandler对象,用该对象的方法getProxy()方法创建代理实例
        //并且该对象实现了InvocationHandler接口,就由了调用处理程序实现的功能,也就是invoke()方法
        ProxyInvocationHandler handler = new ProxyInvocationHandler();

        handler.setRent(host); //这里把房东传值给handler中的rent对象,也就是房东把房子交给中介

        //利用handler对象,调用其中我们写好的getProxy方法,来实例代理,也就是中介
        Rent proxy = (Rent) handler.getProxy();

        //这里中介就帮房东出租房子了,也就实现了我们的代理模式
        proxy.RentHouse();
    }
}

动态代理解决了静态代理代码冗余的缺点

如果我们将动态代理类中的代码进行修改

将其中这段代码改为
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }
如下
    private Object object;

    public void setObject(Object object) {
        this.object = object;
    }

那么,这样它就成了一个万能的代理,能传入任何类型的接口,那么他就什么都能代理了。

四、 cglib动态代理代码示例

用cglib我们需要导包,如下

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.12</version>
        </dependency>

cglib动态代理与JDK动态代理的区别:cglib无需强制实现接口,其生成的代理类是被代理类的子类

我们创建一个需要被代理的类Rent

public class Rent {
    public void rentHouse(){
        System.out.println("出租房屋");
    }
}

随后我们创建cglib动态代理,实现的是MethodInterceptor接口

import java.lang.reflect.Method;

public class RentCglib implements MethodInterceptor {

    //被代理的对象
    private Object service;

    //注入被代理的对象
    public void setService(Object service) {
        this.service = service;
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //可以在这里写上增加的功能
        System.out.println("代理带房客看房");
        return method.invoke(service, objects);
    }
}

最后我们写一个测试类,进行测试

import net.sf.cglib.proxy.Enhancer;

public class Test {
    public static void main(String[] args) {
        //创建租房服务
        Rent rent = new Rent();
        //注入租房的类
        RentCglib rentCglib = new RentCglib();
        rentCglib.setService(rent);

        //创建增强器,用来创建动态代理实例
        Enhancer enhancer = new Enhancer();
        //注入动态代理要代理的类
        enhancer.setSuperclass(rent.getClass());
        //设置回调
        enhancer.setCallback(rentCglib);
        //创建动态代理实例
        Rent rentService = (Rent) enhancer.create();

        rentService.rentHouse();
    }
}

输出结果如下
在这里插入图片描述

参考文献

b站狂神说


经过一次修改,可读性比之前好了,但是还需要自己写出来才能掌握

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

方圆想当图灵

嘿嘿,小赏就行,不赏俺也不争你

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

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

打赏作者

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

抵扣说明:

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

余额充值