设计模式之代理模式

一、什么是代理模式

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

二、代理模式角色

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

三、代理模式UML图

设计模式之代理模式插图

四、代理模式实现方式

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

五、静态代理优缺点

优点:

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

缺点:

  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需求

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

2、静态代理模式代码实现

/**
  * 用户管理接口
  */ public interface IUserService {

     void insert();
 
     void print();
 }
/**
  * 用户管理实现类
  */ public class UserServiceImpl implements IUserService {

     @Override
     public void insert() {
         System.out.println("新增一个用户");
     }
 
     @Override
     public void print() {
         System.out.println("打印用户信息");
     }
 }
/**
  * 用户管理静态代理类
  *
  */ public class UserServiceProxy implements IUserService {

     private IUserService userService;
 
     public UserServiceProxy(IUserService userService) {
         this.userService = userService;
     }
 
     /**
      * 对原有insert功能进行增强,增加日志信息
      */     @Override
     public void insert() {
         System.out.println("新增用户开始");
         userService.insert();
         System.out.println("新增用户成功。");
     }
 
     /**
      * 对原有print功能进行增强,增加日志信息
      */     @Override
     public void print() {
         System.out.println("输出用户信息开始");
         userService.print();
         System.out.println("输出用户信息完成。");
     }
 }
public class ClientTest {
 
     public static void main(String[] args) {
         //创建目标对象
         IUserService userService = new UserServiceImpl();
 
         //创建代理对象
         IUserService userServiceProxy = new UserServiceProxy(userService);
         
         //调用方法
         userServiceProxy.print();
         System.out.println("====================================");
         userServiceProxy.insert();
     }
 }

控制台输入日志:

输出用户信息开始
打印用户信息
输出用户信息完成。
====================================
新增用户开始
新增一个用户
新增用户成功。

3、JDK动态代理模式代码实现

/**
  * 用户管理接口
  */ public interface IUserService {

     void insert();
 
     void print();
 }
/**
  * 用户管理实现类
  */ public class UserServiceImpl implements IUserService {

     @Override
     public void insert() {
         System.out.println("新增一个用户");
     }
 
     @Override
     public void print() {
         System.out.println("打印用户信息");
     }
 }
/**
  * 代理对象的工厂类
  */ public class UserServiceProxyFactory {
 
     private IUserService userService;
 
     public UserServiceProxyFactory(IUserService userService) {
         this.userService = userService;
     }
 
     public IUserService getProxyObject(){
         return (IUserService) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                 userService.getClass().getInterfaces(),
                 new InvocationHandler() {
                     @Override
                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                         System.out.println("进入方法,记录日志");
                         Object result = method.invoke(userService, args);
                         System.out.println("结束方法,记录日志");
                         return result;
                     }
                 });
     }
 }
public class Client {

     public static void main(String[] args) {
         //创建目标对象
         IUserService userService = new UserServiceImpl();
 
         // 创建代理对象
         IUserService proxyObject = new UserServiceProxyFactory(userService).getProxyObject();
 
         System.out.println(proxyObject.getClass());
         System.out.println("========================");
         //调用方法
         proxyObject.insert();
         System.out.println("========================");
         proxyObject.print();
 
     }
}

控制台输入日志:

class com.sun.proxy.$Proxy0
========================
进入方法,记录日志
新增一个用户
结束方法,记录日志
========================
进入方法,记录日志
打印用户信息
结束方法,记录日志

可以看到动态代理对象的class 带有 $Proxy 标识:class com.sun.proxy.$Proxy0

4、CGLIB动态代理模式代码实现

/**
  * 用户管理类,被代理的目标类
  */ public class UserService {
 
     public void add(String name) {
         System.out.println("新增了一个用户: " + name);
     }
 
     public void print() {
         System.out.println("打印用户信息...");
     }
 }
/**
  * 用户代理对象工厂类,用来获取代理对象
  */ public class UserProxyFactory<T> {

     //目标对象,使用泛型
     private T target;
 
     /**
      * 初始化工厂类使,指定被代理的目标对象
      * @param target
      */     public UserProxyFactory(T target) {
         this.target = target;
     }
 
     /**
      * 获取代理对象
      * 
      * @return
      */     public T getProxyObj(){
         // 创建enhancer对象,等价于JDK代理中的Proxy类
         Enhancer enhancer = new Enhancer();
 
         //设置父类的字节码对象
         enhancer.setSuperclass(UserService.class);
 
         //设置回调函数
         enhancer.setCallback(new MethodInterceptor() {
 
             /**
              * 用于实现对目标对象的功能增强,类似于JDK代理中的InvocationHandler中的invoke方法
              *
              * @param o
              * @param method
              * @param objects
              * @param methodProxy
              * @return
              * @throws Throwable
              */             @Override
             public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                 System.out.println("调用方法前,写入日志");
                 Object result = method.invoke(target, objects);
                 System.out.println("调用方法结束,写入日志");
                 return result;
             }
         });
         // 生成代理对象
         return (T) enhancer.create();
     }
 }
 /**
  * CGLIB 客户端测试类
  * 
  */ public class CGLIBClient {
     public static void main(String[] args) {
         UserService userService = new UserProxyFactory<UserService>(new UserService()).getProxyObj();
         System.out.println("代理对象class= " + userService.getClass());
         System.out.println("=======================");
         userService.add("lisi");
     }
 }

控制台输入日志:

代理对象class= class com.ford.proxy.dynamic.cglib.UserService$$EnhancerByCGLIB$$91d7e1dc
=======================
调用方法前,写入日志
新增了一个用户: lisi
调用方法结束,写入日志

可以看到,CGLIB代理对象带有:$$EnhancerByCGLIB$$,而JDK动态代理的代理对象代用:$Proxy,这一点可以判断代理对象到底使用了哪种代理实现方式。

发表评论