设计模式之代理模式

代理模式

概述:由于某些原因需要给某对象提供一个代理以控制该对象的访问,这时,访问者对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

代理模式结构如下:

抽象主题类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

真实主题类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的真实对象

代理类:同样实现了抽象主题类,其内部组合了具体主题类对象,它可以访问,控制,扩展真实主题的功能

代理模式又分为静态代理和动态代理


静态代理

案例实现

通过例子实现静态代理模式,火车票售卖是客户通过各地的售卖点进行购买的,并不是直接去到火车站购买的,所以由此,业务逻辑可以划分出火车站是具体的主题类,各地的火车票代售点是代理类,由此我们的代码实现主要由一下类组成。

SellTickets接口,定义了火车票售卖的规范

public interface SellTickets {
    void sell();
}

TrainStation类,火车站类,实现了售票接口,实现具体的卖票逻辑

public class TrainsStation implements SellTickets{
    @Override
    public void sell() {
        System.out.println("火车站售卖火车票");
    }
}

ProxyPoint类,各地方的的火车票代售点组合了火车站类,并实现了卖票接口,使得其在能够卖票的同时能够提供一些原有的火车站类没有的功能

public class ProxyPoint implements SellTickets{
    private TrainsStation trainsStation=new TrainsStation();

    @Override
    public void sell() {
        System.out.println("代售点出售火车票");
        trainsStation.sell();
    }
}

Test测试类测试静态代理模式

public class Test {
    public static void main(String[] args) {
        ProxyPoint proxyPoint=new ProxyPoint();
        proxyPoint.sell();
    }
}

动态代理

动态代理中可以通过使用jdk动态代理和Cglib动态代理来实现

JDK动态代理

还是上面的售卖火车票的例子,不过我们的代理类对象并不是直接定义好的,而是通过运行时动态生成的。

SellTickets类,定义了火车票售卖的规范

public interface SellTickets {
       void sell();

       String sellInfo(String name);
}

TrainStation类,具体的火车站类,实现了火车票售卖接口

public class TrainsStation implements SellTickets {
       @Override
       public void sell() {
            System.out.println("火车站售卖火车票");
       }

       @Override
       public String sellInfo(String name) {
            return name+"购买了火车票";
       }
}

TrainStatiobProxyFactory类,代售点类工厂,通过该工厂类生产火车站的代理对象:各地的代售点

public class TrainsStationProxyFactory {

       public SellTickets getProxyInstance(SellTickets tickets) {
            InvocationHandler invocationHandler = (proxy, method, args) -> {
                System.out.println("代售点购买火车票");
                return method.invoke(tickets,args);
            };

            return (SellTickets)Proxy.newProxyInstance(this.getClass().getClassLoader(), tickets.getClass().getInterfaces(), invocationHandler);

       }
}

在TrainStationProxyFactory中我们可以看到,使用了JDK自带的Proxy类,该类通过newProxyInstance方法构建了一个类型为SellTickets类型的代理对象,该方法共有三个参数

public static Object newProxyInstance(ClassLoader loader,
                                    Class<?>[] interfaces,
                                    InvocationHandler h)

ClassLoader:是类加载器,可以通过当前对象的字节码对象获取,也可以通过传递进来的目标对象的字节码对象来获取。

Class<?>[ ]:由参数名称可知这是一个接口数组,该参数是目标对象所实现的接口数组,也就是火车站类实现的接口

InvocationHandler:该参数是在使用目标对象中的方法时所需要实现的一个增强方式,由于InvocationHandler是一个接口,我们可以通过使用匿名类的方式实现该接口,在TrainsStationProxyFactory类中我们通过使用 lambda表达式使用匿名类的方式实现了Invocation接口中的invoke方法,该方法共有三个参数

public Object invoke(Object proxy, Method method, Object[] args)
  throws Throwable;

Object:第一个参数是目标对象,基本上不会使用

Method:第二个参数是调用的目标对象的方法

Object[ ]:第三个参数是调用该方法时所传递的参数

Cglib动态代理

Cglib动态代理需要引入第三方依赖进行,还是上面的,买火车票的例子,不过没有了卖票规范接口。

1.导入依赖项

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

2.编写火车站类

public class TrainsStation {

   public void sell() {
          System.out.println("火车站售卖火车票");
   }

   public String sellInfo(String name) {
          return name + "购买了火车票";
   }

   public String sellStation(String stationName){
          return "用户在"+stationName+"购买得火车票";
   }
}

3.编写生产代理类的工厂类

public class TrainsStationProxyFactory implements MethodInterceptor {
   private TrainsStation trainsStation;

   @Override
   public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
          System.out.println("调用"+method.getName()+"方法了");
          return method.invoke(trainsStation,objects);
   }

   public TrainsStation getProxy(TrainsStation station){
          this.trainsStation=station;
          Enhancer enhancer=new Enhancer();
          //设置父类字节码对象,由于生成的代理类本质上来说是属于TrainsStation类的子类
          enhancer.setSuperclass(station.getClass());
          //设置回调方法,通过回调方法对代理类中调用的方法实现增强的作用
          enhancer.setCallback(this);
          return (TrainsStation) enhancer.create();
   }
}

4.编写测试类,测试使用Cglib实现的动态代理模式

public class Test {
public static void main(String[] args) {
  TrainsStationProxyFactory factory=new TrainsStationProxyFactory();

  TrainsStation trainsStation= factory.getProxy(new TrainsStation());
  trainsStation.sell();
}
}

JDK和Cglib实现的动态代理的区别

JDK动态代理代理的并不是某个具体的类,而是某一类接口,而Cglib代理的则是某个具体的类,所以在需要代理的类没有实现任何接口时我们可以选择使用Cglib进行动态代理,如果有实现接口那么则使用JDK动态代理来实现。


JDK动态代理执行流程

1.程序运行时生成代理对象在内存中

2.在测试类中通过代理对象调用sell()方法

3.根据多态的特性,执行的是代理类($Proxy0)中的sell(方法)

4.代理类($Proxy)中的sell()方法中有调用了InvocationHandler接口的子实现类对象的invoca方法

5.invoca方法通过反射执行了真实对象所属类中的sell方法


优缺点

优点

1.代理模式在客户端与目标对象之间起到了一个中介的作用和保护目标对象的作用

2.代理对象可以扩展目标对象的功能

3.代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合

缺点

增加了系统的复杂度


使用场景

远程代理

本地服务通过网络请求远程服务,为了实现本地的远程通信,我们需要实现网络通信,处理其中可能的异常,为良好的代码设计和可维护性,我们讲网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多过多关心通信部分的细节

防火墙代理

当你将浏览器配置成使用代理时,防火前就将你的浏览器请求转发给互联网,当互联网返回响应时,代理服务服务器在把它转给你的浏览器

保护代理

控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值