【设计模式】26.结构型模式-代理模式(Proxy)
一、描述
定义:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。代理模式分为静态代理、动态代理、CGLIB代理等方式,稍后会举例说明。
角色:
1.抽象对象:一般是来声明代理对象和被代理对象之间的公共方法,该类可以是接口,也可以是抽象类。
2.目标对象:又称为被代理类,是真正执行业务逻辑的类
3.代理对象:又称为代理类,此类中一般包含了目标对象的引用,因此具备了目标对象的代理权。并且可以对目标对象进行逻辑增强或控制性操作。
类图:
从定义上来看,其实和中介者模式(行为模式)有相似之处,但是具体区分的话,其实在目的,角色划分上是不同的,
1.目的:中介者模式是为了解耦,将访问者与被访问者解耦,避免直接关联;代理模式是为了控制对某对象的访问。
2.角色:中介者模式分为四种角色:抽象中介类、具体中介类、抽象同事类、具体同事类;代理模式分为三种角色:抽象对象、目标对象、代理对象
二、优点
1.代理模式能将代理对象和目标对象分离
2.在一定程度上降低了系统的耦合,扩展性好
3.可以起到保护目标的作用
4.可以起到增强目标对象的作用
三、缺点
1.请求会先通过代理类,会降低请求效率
2.系统复杂度增加
四、适用场景
最常用的场景便是Spring 的AOP了,相信很多同学都用过。
五、示例
以“点外卖为例”,小王想点一份25块钱西红柿盖浇饭,但是不想出门,于是在外卖平台上点了某个饭店外卖,外卖平台上有满减,满20-2,于是小王只花了23块钱就得到了一份盖浇饭。
分析一下角色分类:
客户:小王;代理类:外卖平台;委托类:饭店
依次举例不同代理分类的代码:
1.静态代理
(1)抽象下单类
public interface AbstractOrder { /** * 下单 * * @return */ Double placeOrder(Integer type); }
(2)菜单枚举
@Getter @AllArgsConstructor public enum OrderList { XI_HONG_SHI_CHAO_DAN(1, "西红柿炒鸡蛋盖饭",new Double(25)), LA_JIAO_CHAO_ROU(2, "辣椒炒肉盖饭",new Double(20)), MU_XU_ROU(3, "木须肉肉盖饭",new Double(18)); /** * 菜单类型 */ private Integer type; /** * 菜单名称 */ private String name; /** * 价格 */ private Double price; private static final Map<Integer, OrderList> MAP = Stream.of(OrderList.values()) .collect(Collectors.toMap(OrderList::getType, Function.identity())); public static OrderList get(Integer type) { return MAP.get(type); } }
(3)小王下单类
public class XiaoWangOrder implements AbstractOrder { @Override public Double placeOrder(Integer type) { OrderList orderList = OrderList.get(type); System.out.println("小王:我想点一份" + orderList.getName() + "(" + orderList.getPrice() + ")"); return orderList.getPrice(); } }
(4)外卖平台
public class DeliveryPlatform implements AbstractOrder { //小王下单 private XiaoWangOrder xiaoWangOrder; public DeliveryPlatform(XiaoWangOrder xiaoWangOrder) { this.xiaoWangOrder = xiaoWangOrder; } @Override public Double placeOrder(Integer type) { System.out.println("使用外卖平台点单:"); Double money = xiaoWangOrder.placeOrder(type); //判断价格是否大于20元,若大于,优惠2元 if (Double.compare(money, new Double(20)) > 0) { System.out.println("外卖平台已满20元,优惠2元"); return money - 2; } return money; } }
(5)测试
public class Client { public static void main(String[] args) { //将小王初始化到外卖平台,小王可以点单 DeliveryPlatform platform = new DeliveryPlatform(new XiaoWangOrder()); Double money = platform.placeOrder(OrderList.XI_HONG_SHI_CHAO_DAN.getType()); System.out.println("最终消费金额:" + money); } }
测试结果:
2.动态代理
抽象类、小王、枚举类不变,变化的是代理类,使用InvocationHandler与Proxy代理类处理,注意包全是java.lang.reflect下面的类。
(1)代理类
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * DeliveryPlatform * * @author zhouxy * @date 2022/8/5 **/ public class DeliveryPlatform implements InvocationHandler { private AbstractOrder abstractOrder; /** * 获取实例对象 * * @param abstractOrder * @return */ public AbstractOrder getInstance(AbstractOrder abstractOrder) { this.abstractOrder = abstractOrder; Class<?> clazz = abstractOrder.getClass(); //将当前代理类加入到处理下单类的逻辑中,当有访问该abstractOrder的时候,会被invoke方法拦截处理。 return (AbstractOrder) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object obj = method.invoke(abstractOrder, args); //根据返回结果处理数据 return after((Double) obj); } public void before() { System.out.println("外卖平台开始点单"); } public Double after(Double price) { //优惠金额大于20时,则优惠2元 if (Double.compare(price, 20) > 0) { System.out.println("消费金额大于20,优惠2元"); price = price - 2; } System.out.println("外卖平台点单完成"); return price; } }
其中,着重解释一下Proxy类,此类为代理类,使用到的为newProxyInstance方法,该方法的作用是:
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler. 百度翻译:返回指定接口的代理类实例,该接口将方法调用分派给指定的调用处理程序。
根据注释可以很清楚的看出该方法的用处,即将指定的调用程序DeliveryPlatform指定给getInstance方法中的abstractOder类中的方法,在访问该类时,会走DeliveryPlatform中的invoke方法。
(2)测试
public class Client { public static void main(String[] args) { DeliveryPlatform deliveryPlatform = new DeliveryPlatform(); AbstractOrder xiaoWangOrder = deliveryPlatform.getInstance(new XiaoWangOrder()); Double price = xiaoWangOrder.placeOrder(OrderList.XI_HONG_SHI_CHAO_DAN.getType()); System.out.println("最终消费:" + price); } }
测试结果
3.CGLIB
(1)CGLIB实现代理类
public class DeliveryPlatformCGLIB implements MethodInterceptor { /** * 获取实例,是一个字节码增强器,可以用来为委托类创建代理 * * @param clazz * @return */ public Object getInstance(Class<?> clazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); //处理委托类方法 Object obj = methodProxy.invokeSuper(o, objects); return after((Double) obj); } public void before() { System.out.println("外卖平台开始点单"); } /** * 优惠 * * @param price * @return */ public Double after(Double price) { //优惠金额大于20时,则优惠2元 if (Double.compare(price, 20) > 0) { System.out.println("消费金额大于20,优惠2元"); price = price - 2; } System.out.println("外卖平台点单完成"); return price; } }
(2)测试
public class Client { public static void main(String[] args) { DeliveryPlatformCGLIB cglib = new DeliveryPlatformCGLIB(); XiaoWangOrder xiaoWangOrder = (XiaoWangOrder) cglib.getInstance(XiaoWangOrder.class); Double price = xiaoWangOrder.placeOrder(OrderList.XI_HONG_SHI_CHAO_DAN.getType()); System.out.println("最终消费:" + price); } }
测试结果