JDK与CGLIB动态代理

Posted by xdshent on January 29, 2023

JDK动态代理

  • 实现InovactionHandler接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
      
    /**
     * 耗时统计
     */
    public class LogInvocationHandler implements InvocationHandler {
      
        private UserService userService;
      
        public LogInvocationHandler(UserService userService) {
            this.userService = userService;
        }
      
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            long start = System.currentTimeMillis();
            Object invoke = method.invoke(userService, args);
            long end = System.currentTimeMillis();
            System.out.println(method.getName() + " 耗时: " + (end - start));
            return invoke;
        }
    }
      
    
  • 业务类与接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    public interface UserService {
      
        /**
         * 根据用户id查询用户名称
         *
         * @param userId UserId
         * @return userName
         */
        String getUserNameById(long userId);
    }
      
      
    import java.util.concurrent.TimeUnit;
    public class UserServiceImpl implements UserService {
      
      
        @Override
        public String getUserNameById(long userId) {
            try {
                //模拟数据库查询
                TimeUnit.MICROSECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return "proxy" + userId;
        }
    }
      
    
  • 创建代理对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    public static void main(String[] args) {
            //输出生成的代理类
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
            UserService userService = new UserServiceImpl();
            LogInvocationHandler invocationHandler = new LogInvocationHandler(userService);
            UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
        
            System.out.println(userServiceProxy.getUserNameById(1));
            //输出: getUserNameById 耗时: 2
            //     proxy1
        }
    
  • 生成的代理类Class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    
    package com.sun.proxy;
      
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
      
    public final class $Proxy0 extends Proxy implements UserService {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
      
        //构造方法
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
      
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
      
        public final String getUserNameById(long var1) throws  {
            try {
                return (String)super.h.invoke(this, m3, new Object[]{var1});
            } catch (RuntimeException | Error var4) {
                throw var4;
            } catch (Throwable var5) {
                throw new UndeclaredThrowableException(var5);
            }
        }
      
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
      
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
      
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m3 = Class.forName("com.github.xdshent.design.proxy.UserService").getMethod("getUserNameById", Long.TYPE);
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
  • 原理

    • $Proxy0类实现了业务接口UserService并且继承了java.lang.reflect.Proxy
    • $Proxy0加载时类静态方法中通过反射获取了java.lang.Object中的equalshashCodetoString以及业务对象中的getUserNameById方法对应的Method对象
    • Proxy.newProxyInstance在生成代理对象时会把传入的InvocationHandler放入$Proxy0的构造函数中
    • 生成的代理对象在通过业务接口UserService调用时会调用父类ProxyInvocationHandler h字段, 进而进入用户所写的LogInvocationHandler, 参数为:
      • 当前 $Proxy0对象
      • 第2步获取到的对应Method对象
      • 方法参数
  • 图示

    jdk-dynamic-proxy

CGLIB动态代理

  • 依赖

    1
    2
    3
    4
    5
    
            <dependency>
                <groupId>cglib</groupId>
                <artifactId>cglib</artifactId>
                <version>3.3.0</version>
            </dependency>
    
  • 实现MethodInterceptor

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    public class LogMethodInterceptor implements MethodInterceptor {
      
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
      
            long start = System.currentTimeMillis();
            Object invoke = proxy.invokeSuper(obj, args);
            long end = System.currentTimeMillis();
            System.out.println(method.getName() + " 耗时: " + (end - start));
            return invoke;
        }
    }
    
  • 创建代理对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
           public static void main(String[] args) {
            //设置生成的class对象输出到当前目录
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(UserServiceImpl.class);
            enhancer.setCallback(new LogMethodInterceptor());
      
            UserServiceImpl userService = (UserServiceImpl)enhancer.create();
            System.out.println(userService.getUserNameById(1));
        }
    
  • 生成的Class

    FastClass机制根据方法签名获取对应的索引, 执行时根据索引找到对应的方法直接执行

    cglib-class

    • 代理类
    • 代理类的FastClass
    • 目标类FastClass
  • 整体概览

    cglib-overview

  • 调用流程

    cglib-invoke