一,代理模式概念
1,什么是代理模式
Proxy Pattern,代理模式是指为其他对象提供一种代理,以控制对这个对象的访问,代理对象在客户端,服务端以及目标对象之间起到中介作用。并且是属于一种结构型模式
2,代理模式的应用场景
1,房产中介,不管是租房还是买房都是可以去找这个中介
2,快递小哥,通过快递小哥实现将商品从商户送到用户手上
3,黄牛党,12306买不到票,或者演唱会之类的,通过这个黄牛购票
4,代理服务器,翻墙等
3,例如一个租房场景
这个房屋和中介他们做一样的事情,就是租房或者卖房给用户
中介是房东的代理,房东是中介的目标。然后用户可以先找中介,中介再找房东
4,开发场景
1,a方法调用c方法不让,增加一个b方法,让a调用b,b去调用c
2,如最近的controller调用service,service调用mapper这种也是利用这种思想
3,如登录时要短信验证,通过这个第三方实现
5,代理模式的作用
1,功能增强,可以在原有的功能上能可以增加额外的功能
2,控制访问,可以通过这个代理类来实现能不能到访问这个目标类
二,静态代理
2.1,静态代理举例
代理类手动实现,并且需要代理的对象是确定的,固定的。例如一个买手机的一个案例来描述这个静态代理。
就是在买手机的时候需要去手机店里面买手机,而不是直接去找厂商。因此这个手机店就是作为一个代理类,这个手机厂商就是对应的目标类,其类图如下。
接下来就编写一个接口,用来统一厂家和这个手机店都去卖手机的一个方法
/** * @Author: zhenghuisheng * @Date: 2022/9/10 17:39 * 代理类和这个目标类都要走的一件事情,卖手机 */ public interface PhoneSell { //卖手机并返回价钱 amount要的数量 public Double sell(Double amount); }
接下来编写一个厂家卖手机的一个方法
/** * @Author: zhenghuisheng * @Date: 2022/9/10 17:54 * 厂商销售手机 */ public class Factory implements PhoneSell { /** * 卖手机,1800一部 */ @Override public Double sell(Double amount) { Double price = amount * 1800; return price; } }
再编写一个手机店的实体类,这个代理类不做具体的事情,会把这个卖手机的事情交给这个厂商去做,并且在这个代理类里面,在原先的基础上可以实现一个增强。
/** * @Author: zhenghuisheng * @Date: 2022/9/10 17:55 * 商店购买,代理类只负责中转,不做具体的买卖,但是可以做增强 */ public class Shop implements PhoneSell { //构建一个厂商对象 private Factory factory = new Factory(); @Override public Double sell(Double amount) { //商家转发给厂家卖手机的信息 double price = factory.sell(amount); //商家需要赚钱 price = price + price * 0.2; // ... 可以做很多事情,都是增强的意思 //返回需要的价钱 return price; } }
然后在写个测试方法,就可以购买一部手机了。
//用户手机店购买手机 Shop shop = new Shop(); Double price = shop.sell(1); System.out.println(price);
这就是简单的实现了一个静态代理。其实就很类似经常写的controller,service,mapper。这个service层就是作一个转发,数据交互还是要交给mapper层处理,并且这个业务逻辑都是在这个service层处理,从而在mapper层基础上多写了一些业务逻辑,得到了增强功能。
2.2,静态代理中代理类的作用
1,目标类中的方法调用
2,功能增强
2.3,静态代理的缺点
1,每个代理类只能代理一个固定的目标类,如果目标类会有多个商店或者厂家,那么就需要增加很多这个代理类
2,如果厂家增加或者修改一个功能,会影响众多的实现类,厂家类和代理类都需要进行一个修改
三,动态代理
为了解决这个代理数量很多,修改接口方法会影响到这个代理类和目标类的问题,从而进入了这个动态代理的这个解决方案。
3.1,什么是动态代理
在程序执行的过程中,利用这个jdk的反射机制,创建这个代理类的对象,并动态指定这个代理目标类。
3.2,动态代理优点
1,不用创建代理类
2,可以给不同的目标随时创建动态代理
3.3,动态代理实现方式
3.3.1,jdk动态代理
通过这个java自带的反射包中的类和接口实现动态代理的功能。里面主要有三个类,分别是InvocationHandler,Method,Proxy三个类,通过这个三个类来实现一个动态代理。要求这个jdk动态代理必须要有接口,如果没有则实现不了。
InvocationHandler接口,里面有一个invoke方法,代理类要完成的这个功能就写在这个方法里面
Method类:主要是获取这个目标类中的方法。Method.invoke()实现
Proxy类:核心的对象,创建代理对象,newProxyInstance()创建代理对象
依旧是使用之前的卖手机的场景,需要用到上面的几个对象。原理相当于将之前写死的一个代理类只能对应一个目标类,改成这个代理类变成一个动态的形式,一个目标类想要一个代理类时,可以通过一个模板来实现,而不需要去重新创建多个代理类。jdk动态代理代码实现如下
1,依旧需要一个主接口,用来规范这个目标类和代理类做一样的事情
/** * @Author: zhenghuisheng * @Date: 2022/9/10 17:39 * 代理类和这个目标类都要走的一件事情,卖手机 */ public interface PhoneSell { //卖手机并返回价钱 amount要的数量 public Double sell(Double amount); }
2,依旧需要一个目标对象
/** * @Author: zhenghuisheng * @Date: 2022/9/10 17:54 * 厂商销售手机 */ public class Factory implements PhoneSell { /** * 卖手机,1800一部 */ @Override public Double sell(Double amount) { Double price = amount * 1800; return price; } }
3,和静态代理不一样的是,动态代理不需要具体的代理类,而是通过实现这个 InvocationHandler这个接口来这个jdk的动态代理。会通过外部的传参来确定成为哪个类的动态代理对象,通过这个方法来确定需要代理这个对象的哪个方法。并且会通过这个invoke方法来获取目标类对象里面的方法,然后获取到结果之后对这个类进行一个功能的增强。最后将执行结果返回
/** * @Author: zhenghuisheng * @Date: 2022/9/10 20:38 * jdk动态代理,必须实现InvocationHandler这个接口,然后获取到一个jdk动态代理类 * 主要是为了调用目标方法,实现功能增强 */ public class ProxyInvocationHandler implements InvocationHandler { //目标对象 private Object target = null; //外部实例化时需要传入这个目标对象 //传入的是谁就给谁创建动态代理 public ProxyInvocationHandler(Object target){ this.target = target; } /** * 这个方法就是代理类里面的代理方法 * @param proxy :代理类,需要外部手动传入需要代理的类 * @param method : 目标方法,可能一个类里面会有多个方法 * @param args : 目标方法参数,对应目标方法里面的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object object = null; //目标方法执行 //第一个是目标对象,第二个是目标参数 //这个目标对象是一个动态的,因此需要外面传入 object = method.invoke(target,args); //在获取到这个目标方法之后,可以对这个目标方法进行一个增强功能 if (object != null){ //外部传入这个sell的这个方法,那么执行这个方法的返回值类型为Double Double price = (Double)object; //手机店获取利润,功能增强 price = price + price* 0.2; object = price; } return object; } }
4,在外部需要向ProxyInvocationHandler的构造方法中传入目标对象,并且通过这个Proxy类的newProxyInstance方法来获取这个代理类,然后通过这个获取的代理类执行对应的方法。
//1,创建目标对象 PhoneSell factory = new Factory(); //2,创建proxyInvocationHandler对象 ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(factory); //3,创建动态代理对象 /** * newProxyInstance(ClassLoader loader, * Class<?>[] interfaces, * InvocationHandler h) */ //获取目标类类加载器 ClassLoader classLoader = factory.getClass().getClassLoader(); //获取类对应的接口 Class<?>[] interfaces = factory.getClass().getInterfaces(); //通过反射的形式获取代理类 //强行转换成对应的接口 PhoneSell proxy = (PhoneSell)Proxy.newProxyInstance(classLoader, interfaces, proxyInvocationHandler); //购买数量 Double sellPrice = proxy.sell(4); System.out.println(sellPrice);
这样的jdk动态代理的就实现好了,里面的步骤都是固定的
3.3.2,cglib动态代理
通过第三方的库来实现动态代理,会创建一个代理对象。
原理:通过继承目标类,创建他的子类,在子类中重写父类中同名的方法,实现功能的修改。cglib目标类不能使用final,只要子类可以继承即可。
cglib相对而言开发中用的比较少,了解原理即可
四,总结
代理模式主要分为静态代理和动态代理。
静态代理中主要有一个目标类和一个代理类,其两个类都要实现一个共同的接口,做一样的事情,代理对象主要负责转发和功能增强,其具体做事情的还是这个目标对象。静态代理不足的地方在于一个代理类只能绑定一个目标类,如果一个目标类或者多个目标类需要多个代理对象,那么就需要创建大量的代理对象,并且如果接口类里面有一点点的功能的改动,那么这个目标类和代理类就会有大量的改动。
动态代理解决了这个静态代理的问题,在要创建很多代理对象方面,通过一个动态代理对象实现。动态代理主要有jdk的动态代理和cglib的动态代理,jdk动态代理是java里面自带的库,而cglib使用的是第三方库。jdk动态代理主要是通过这个InvocationHandler接口来获取这个目标类和实现代理类的功能增强,并通过反射的方式,获取到这个代理类对象,最后通过这个代理对象获取到对应的方法