代理模式

86 阅读6分钟

Proxy Parttern 别名:Surrogate

定义

为其他对象提供一种代理,以控制对这个对象的访问,并允许在将请求给对象前后进行一些处理。

应用场景

1.延迟初始化(虚拟代理)。如果会有一个偶尔使用的重量级服务对象,一直保持该对运行会系统资源时,可使用代理模式。

无需在程序启动时久创建该对象,可将对象的初始化延迟到真正有需要的时候

2.访问控制(保护代理)。如果只希望特定客户使用服务对象,这里的对象可以是操作系统中非常重要的部分,而客户端则是各种已启动的程序(包括恶意程序),此时可使用代码模式。

代理可仅在客户端凭据满足要求时将请求传递给服务对象

3.本地执行远程服务(远程代理)。适用于服务对象位于远程服务器上的情形。

在这种情形中,代码通过网络传递客户端请求,负责处理所有与网络有关的复杂细节。

4.记录日志请求(日志记录代理)。适用于当你需要保存对于服务对象的请求历史时。代理可以在向服务传递请求前进行记录。

缓存请求结构(缓存代理)。适用于需要缓存客户请求结果并对缓存生命周期进行管理时,特别是当返回结果的体积非常大时,代理可对重复请求所需的相同结果进行缓存,还可使用请求参数作为索引缓存的键值

5.智能引用。可在没有客户端使用某个重量级对象时立即销毁该对象

代理会将所有获取了指向服务对象或其结果的客户端记录在案。代理会时不时地遍历各个客户端,检查它们是否仍在运行。如果相应的客户端列表为空,代理就会销毁该服务对象,释放底层系统资源。【代理还可以记录客户端是否修改了服务对象。其他客户端还可以复用为修改的对象】

实现方法

1.如果没有现成的服务接口,你就需要创建一个接口来实现代理和服务对象的可交换性。从服务类中抽取接口并非总是可行的,因为你需要对服务的所有客户端进行修改,让它们使用接口。备选计划是将代理模式作为服务类的子类,这样代理就可能继承服务的所有接口

2.创建代理类,其中必须包含一个存储指向服务的引用的成员变量。通常情况下,代理负责创建服务并对其整个生命周期进行管理。在这些特殊情况下,客户端会通过构造函数将服务传递给代理。

3.根据需求实现代理方法。在大部分情况下,代理在完成一些任务后应将工作委托给服务对象

4.可以考虑新建一个构建方法来判断客户端可获取的是代理还是实际服务。你可以在代理类中创建一个简单的静态方法,也可以创建一个完整的工厂方法。

5.可以考虑为服务对象实现延迟初始化

优缺点

优点:

1.可以在客户端毫无察觉的情况下控制服务对象

2.若客户端对服务对象的生命周期没有特殊要求,你可以对生命周期进行管理

3.即使服务对象还未准备好货不存在,代理也可以正常工作

4.开闭原则。可以在不对服务或客户端做出修改的情况下创建新代理

缺点:

1.代码可能会变得复杂,因为需要新建许多类

2.服务响应可以延迟。

结构

UML图

classDiagram
Client-->ServiceInterface
Proxy..|>ServiceInterface
Proxy o--> Service
Service ..|> ServiceInterface
class ServiceInterface{
	<<interface>>
	+operation()
}
class Proxy{
	-resalService:Service
	+Proxy(s:Service)
	+checkAccess()
	+operation()
}
class Service{
	...
	+operation()
}

参与者

1.服务接口( Service Interface ): 声明了服务接口。代理必须遵循该接口才能伪装成服务对象

2.服务( Service ): 类提供一些实用的业务逻辑

3.代理( Proxy ): 类包含一个指向服务对象的引用成员变量。代理完成其任务(例如延迟初始化、记录日志、访问控制和缓存等)后会将请求传递给服务对象。通常情况下,代理会对其服务对象的整个生命周期机进行管理

4.客户端( Client ): 能通过同一接口与服务或代理进行交互。

通用写法

静态代理

通过定义接口 或 采用继承的方式,使被代理对象与代理对象一起实现相同

classDiagram
Client..>Proxy
Proxy o-->Service
Proxy ..|> ServiceInterface
Service ..|>  ServiceInterface
public interface ServiceInterface {
   void operation();
}
​
public class Service implements ServiceInterface{
   @Override
   public void operation() {
       System.out.println("进行相关操作");
  }
}
​
public class Proxy implements ServiceInterface{
   private Service service;
​
   public Proxy(Service service){
       this.service = service;
  }
​
   @Override
   public void operation() {
       System.out.println("代理开始...");
       service.operation();
       System.out.println("代理结束...");
  }
}
​
public class Client {
   public static void main(String[] args) {
       Service service = new Service();
​
       Proxy proxy = new Proxy(service);
       proxy.operation();
  }
}

优点:

在不改变目标对象的前提下,能通过代理对象对目标的功能进行拓展

缺点:

因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类

一旦接口增加了方法,目标对象与代理对象都要维护

动态代理

代理对象依赖JDK的API,动态地在内存中构建代理对象

「代理对象不需要实现接口,而被代理对象需要实现接口」

classDiagram
Client..>Proxy
Client..>Service
Service..|>ServiceOrder
public interface ServiceInterface {
   void operation();
}
​
public class Service implements ServiceInterface{
   @Override
   public void operation() {
       System.out.println("进行相关操作");
  }
}
​
public class Proxy {
   private Object service; //代理对象
​
   public Proxy(Object service){
       this.service = service;
  }
​
​
   //给代理对象生成一个代理对象
   public Object getProxyInstance(){
       return java.lang.reflect.Proxy.newProxyInstance(service.getClass().getClassLoader(),
               service.getClass().getInterfaces(),
               new InvocationHandler() {
                   @Override
                   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                       System.out.println("JDK 代理");
                       //利用反射 调用 代理对象的方法
                       Object result = method.invoke(service,args);
                       System.out.println("----");
​
                       return result;
                  }
              }
      );
  }
}
​
public class Client {
   public static void main(String[] args) {
       ServiceInterface service = new Service();
​
       ServiceInterface proxy = (ServiceInterface) new Proxy(service).getProxyInstance();
       System.out.println(proxy.getClass());
       proxy.operation();
  }
}

补充说明: public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)

ClassLoader loader : 目标对象使用的类加载器,获取加载器的方法 Class<?>[] interfaces: 目标的对象使用的接口类型,使用泛型方法确定类型 InvocationHandler h: 事情处理,执行目标对象的方法时,对触发事情处理器方法,会把当前执行的目标对象方法作为参数传入

Cglib 代理

静态代理和JDK代理模式都要求对象是实现一个接口,但有时被代理对象只是一个单独的类,并没有实现任何的接口,这时便可采用被代理子类来实现代理。

「Cglib是一个强大的高性能代码生成包,可以在运行期扩展java类与实现java接口,它被广泛应用于AOP框架中。」

Cglib底层是使用字节码处理框架ASM来转换字节码并生成新的类.

实现要素:需要导入cglib的jar包

classDiagram
Client..>Proxy
Proxy..|>MethodInterceptor
Client..>Service
public class Service{
   public void operation() {
       System.out.println("进行相关操作");
  }
}
​
public class Proxy implements MethodInterceptor {
   private Object service; //被代理对象
​
   public Proxy(Object service){
       this.service = service;
  }
​
​
   //返回一个 被代理对象
   public Object getProxyInstance(){
       //1.创建工具类
       Enhancer enhancer = new Enhancer();
       //2.设置父类
       enhancer.setSuperclass(service.getClass());
       //3.创建回调函数
       enhancer.setCallback(this);
       //4.创建代理对象
       return enhancer.create();
  }
​
   //重写 intercept() : 调用 被代理对象的方法
​
   @Override
   public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
       System.out.println("cglib 代理模式开始");
       Object result = method.invoke(service, objects);
       System.out.println("cglib 代理模式结束");
​
       return result;
  }
}
​
public class Client {
   public static void main(String[] args) {
       Service service = new Service();
​
       Service proxy = (Service) new Proxy(service).getProxyInstance();
       proxy.operation();
  }
}
​