代理模式设计 (聊聊定制工作室模式)

导读:代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理来间接的调用实际的对象。如:俱乐部(客户)--经纪人(代理)--球星(实际对象),俱乐部想签约球员并不是与球员直接接触,而是与球星的代理经纪人进行交谈。本文的讨论的内容如下:

  • 代理模式的概念
  • 代理模式的好处
  • 静态代理实现
  • 动态代理(JDK动态代理和cglib代理)实现

一、代理模式概念

代理分为静态代理和动态代理

  • 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。
  • 动态代理:在程序运行时运用反射机制动态创建而成。

为了保持行为的一致性,代理类和委托类通常会实现相同的接口 。

二、代理模式的好处

  • 采用代理模式可以有效的将具体的实现与调用方进行解耦,通过面向接口进行编码将具体实现类的细节向调用方隐藏。
  • 为做不同控制策略预留了空间,在设计上拥有了更大的灵活性。

设计模式代理模式详解,聊聊定制工作室模式

三、静态代理具体实现

设计模式代理模式详解,聊聊定制工作室模式

/**
 * 商品服务接口
 */
public interface ProductService {
 void addProduct();
}
/**
 * 商品服务具体实现类
 */
public class ProductServiceImpl implements ProductService{
 @Override
 public void addProduct() {
 System.out.println("商品添加");
 }
}
/**
 * 商品服务静态代理类
 */
public class ProductServiceProxy implements ProductService{
 ProductServiceImpl productServiceImpl;
//构造方法
 public ProductServiceProxy (ProductServiceImpl productServiceImpl) {
 this.productServiceImpl = productServiceImpl;
 }
 @Override
 public void addProduct() {
 System.out.println("调用实际业务逻辑处理前的操作");
 productServiceImpl.addProduct();
 System.out.println("调用实际业务逻辑处理后的操作,如日志记录");
 }
}
/**
* 测试
*/
@Test
public void test() {
 ProductService service = new ProductServiceProxy(new ProductServiceImpl());
 service.addProduct();
}
//结果打印
调用实际业务逻辑处理前的操作
商品添加
调用实际业务逻辑处理后的操作,如日志记录

解析:

  • 具体实现类(ProductServiceImpl )和代理类(ProductServiceProxy )都实现商品服务接口(ProductService)。
  • 实现类的方法中需要将接口中定义的方法的业务逻辑功能实现。
  • 代理类中的方法只要调用具体类中的对应方法即可。

缺点:静态代理类只能为特定的接口服务。如想要为多个接口服务则需要建立很多个代理类,这样在程序开发中会出现很多的代理类维护起来不方便。(通过使用动态代理可以实现一个代理类完成全部的代理功能)

四、动态代理具体实现

静态代理一个代理只能代理一种类型,而且是在编译器就已经确定被代理的对象,而动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象。在java中,实现动态代理主要有两种方式,一种是jdk动态代理,一种是cglib。

JDK动态代理的实现(必须基于接口)

/**
 * 代理类
 */
public class ServiceDynaProxy implements InvocationHandler {
//用于接收具体实现类的实例对象
 Object object;
//使用带参数的构造器来传递具体实现类的对象
 public ServiceDynaProxy(Object object) {
 this.object = object;
 }
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 System.out.println("调用实际业务逻辑处理前的操作");
 method.invoke(object, args);
 System.out.println("调用实际业务逻辑处理后的操作");
 return null;
 }
}
/**
* 测试
*/
@Test
public void test() {
//生成InvocationHandler实例
InvocationHandler Ih = new ServiceDynaProxy(new ProductServiceImpl());
//代理类实例创建
ProductService proxy = (ProductService)Proxy.newProxyInstance(ProductService.class.getClassLoader(), new Class[]{ProductService.class}, Ih);
//调用方法
proxy.addProduct();
}

解析:

  1. 接口和具体实现类保持不变。
  2. 代理类需要实现Java提供的内置接口InvocationHandler接口。
  3. 重写invoke方法,该方法在调用指定的具体方法时会自动调用。其参数为:代理实例、调用的方法、方法的参数列表.
  4. 实例化InvocationHandler实例作为生成代理实例的参数。
  5. 调用Proxy的静态方法newProxyInstance方法生成代理实例.
  6. 使用生成的代理实例调用方法实现功能。

Cglib动态代理的实现

JDK动态代理拥有局限性,那就是必须面向接口编程,没有接口就无法实现代理。在实际开发中不可能为了代理而为每个需要实现代理的类强行添加无意义的接口,Cglib依靠继承来实现动态代理的方式则可以处理此问题,

<!--cglib 依赖 -->
<dependency>
 <groupId>cglib</groupId>
 <artifactId>cglib</artifactId>
 <version>3.1</version>
</dependency>
/**
 * cglib 代理类
 */
public class CglibDynaProxy implements MethodInterceptor {
 @Override
 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
 System.out.println("调用实际业务逻辑处理前的操作");
 Object object = methodProxy.invokeSuper(o, objects);
 System.out.println("调用实际业务逻辑处理后的操作,如日志记录");
 return object;
 }
}
/**
* 测试
*/
@Test
public void test() {
//创建字节码增强器
Enhancer enhancer = new Enhancer();
//被代理类设置为字节码增强器父类,cglib使用的是继承方式去创建代理类
enhancer.setSuperclass(ProductServiceImpl.class);
//设置字节码增强器回调方法。对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦截
enhancer.setCallback(new CglibDynaProxy());
//创建代理实例
ProductServiceImpl service = (ProductServiceImpl) enhancer.create(); 
//调用方法
service.addProduct();
}

解析:

  1. 添加依赖cglib依赖
  2. 接口和具体实现类保持不变。
  3. 代理类实现MethodInterceptor 接口并重写intercept。
  4. 创建字节码增强器Enhancer并进行设置
  5. 创建代理实例
  6. 调用接口

设计模式代理模式详解,聊聊定制工作室模式

五、总结

使用代理模式的好处是解耦和预留控制空间使更具有灵活性。代理分为了静态代理和动态代理,使用动态代理更简便、易于维护。动态代理分为JDK动态代理和cglib等,cglib解决了必须面向接口编程,没有接口就无法实现代理的问题。

感谢您的阅读,如果喜欢本文欢迎关注和转发,本头条号将持续分享IT技术知识。对于文章内容有其他想法或意见建议等,欢迎提出共同讨论共同进步。