设计模式之代理模式

一、什么是代理模式

代理模式,为目标对象提供一个替身,以控制对这个对象的访问,进行附加功能增强,从而达到扩展目标对象的功能。比如:添加日志、事务控制、程序耗时等等。

二、代理模式角色

  • 抽象角色:定义代理角色和真实角色的公共对外方法。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理对象调用。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以增加前置和后置操作。

三、代理模式UML图

设计模式之代理模式插图

四、代理模式实现方式

  • 静态代理:被代理对象(即目标对象)和代理对象需要实现共同的接口或继承共同的抽象类,同时代理对象需要关联目标对象。
  • JDK动态代理:目标对象需要实现接口,代理对象不需要实现接口。利用JDK自带的Proxy类在运行时动态生成代理对象。
  • Cglib动态代理:CGLIB代理也叫子类代理,在JVM运行时,在内存中动态的基于目标类创建一个子类对象,从而实现对目标对象的增强。CGLIB是通过使用字节码处理框架ASM来转换字节码并生成新的类。

五、静态代理优缺点

1、优点

  1. 避免了直接修改目标对象的功能,以代理模式对目标对象进行了功能扩展。

2、缺点

  1. 代理类需要与目标对象一一对应,可能会产生“类爆炸”,维护工作量巨大。
  2. 如果目标对象新增功能,代理类也要同步进行新增升级,可谓牵一发而动全身。

六、JDK动态代理Proxy类详解

JDK自带的代理类包路径java.lang.reflect.Proxy,调用静态方法newProxyInstance方法动态生成代理对象,该方法包括3个形参:

 public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
  • ClassLoader loader: 指定目标对象所使用的类加载器,用于加载代理类。
  • Class<?>[] interfaces: 目标对象实现的接口类,通过泛型确定类型,也是代理类实现的接口类。
  • InvocationHandler h: 代理对象的调用处理程序,执行目标对象的方法,会把当前执行的目标对象 的方法作为参数传入。调用invoke方法进行增强业务处理。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
  • Object proxy:代理对象,在该方法中基本不用。
  • Method method:目标对象的方法,对接口中的方法进行封装的method对象。
  • Object[] args:调用目标对象方法的实际参数。

七、CGLIB动态代理

1、为啥要使用CGLIB

如果需要被代理的目标对象没有实现接口,这时就不能使用静态代理或JDK动态代理来实现了。这时,CGLIB就要登场了,它提供了一个功能强大、高性能的代码生成包,它可以为没有实现接口的普通类提供代理,弥补了JDK动态代理的不足。

2、使用CGLIB代理注意事项

  • 被代理的目标对象不能为final
  • 被代理的目标对象的方法不能被final、static修饰

3、引入CGLIB jar 包

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

4、CGLIB 2个核心类

  • net.sf.cglib.proxy.Enhancer:用于动态生成代理对象,作用类似JDK代理中的Proxy类。
  • net.sf.cglib.proxy.MethodInterceptor:目标对象的拦截器,用于实现对目标对象的功能增强。

八、代理模式demo示例

1、Demo需求

以用户管理为例,对用户功能进行增强,添加方法入口日志和结束日志