从源码学习Java动态代理

亦凉 2023-10-11 15:08 80阅读 0赞

这里写目录标题

  • 前言
  • RMI
    • 接口定义
    • 远程对象定义
    • 服务端
    • 客户端
  • 动态代理
    • 提出问题
    • 动态代理Demo
      • 接口定义
      • 真实角色定义
      • 调用处理类定义
      • 执行类
    • 猜想
  • 在困惑的日子里学会拥抱源码
    • 调用流程图
    • newProxyInstance()
      • getProxyClass0()
  • WeakCache
    • 弱引用
      • WeakCache的结构
      • WeakCache的get()
      • 生成二级缓存Key
      • 生成二级缓存Value
      • Factory.get()生成弱引用value
    • Class文件的生成
      • 包名类名的定义与验证
      • class文件写入本地
      • 生成class文件二进制流
    • 构建$Proxy对象
  • $Proxy.class文件
  • 结语

前言

最近,看了一下关于RMI(Remote Method Invocation)相关的知识,遇到了一个动态代理的问题,然后就决定探究一下动态代理。

这里先科普一下RMI。

RMI

像我们平时写的程序,对象之间互相调用方法都是在同一个JVM中进行,而RMI可以实现一个JVM上的对象调用另一个JVM上对象的方法,即远程调用。

接口定义

定义一个远程对象接口,实现Remote接口来进行标记。

  1. public interface UserInterface extends Remote {
  2. void sayHello() throws RemoteException;
  3. }

远程对象定义

定义一个远程对象类,继承UnicastRemoteObject来实现Serializable和Remote接口,并实现接口方法。

  1. public class User extends UnicastRemoteObject implements UserInterface {
  2. public User() throws RemoteException {
  3. }
  4. @Override
  5. public void sayHello() {
  6. System.out.println("Hello World");
  7. }
  8. }

服务端

启动服务端,将user对象在注册表上进行注册。

  1. public class RmiServer {
  2. public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
  3. User user = new User();
  4. LocateRegistry.createRegistry(8888);
  5. Naming.bind("rmi://127.0.0.1:8888/user", user);
  6. System.out.println("rmi server is starting...");
  7. }
  8. }

启动服务端:
在这里插入图片描述

客户端

从服务端注册表获取远程对象,在服务端调用sayHello()方法。

  1. public class RmiClient {
  2. public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException {
  3. UserInterface user = (UserInterface) Naming.lookup("rmi://127.0.0.1:8888/user");
  4. user.sayHello();
  5. }
  6. }

服务端运行结果:
在这里插入图片描述
至此,一个简单的RMI demo完成。

动态代理

提出问题

看了看RMI代码,觉得UserInterface这个接口有点多余,如果客户端使用Naming.lookup()获取的对象不强转成UserInterface,直接强转成User是不是也可以,于是试了一下,就报了以下错误:
在这里插入图片描述
似曾相识又有点陌生的$Proxy0,翻了翻尘封的笔记找到了是动态代理的知识点,寥寥几笔带过,所以决定梳理一下动态代理,重新整理一份笔记。

动态代理Demo

接口定义

  1. public interface UserInterface {
  2. void sayHello();
  3. }

真实角色定义

  1. public class User implements UserInterface {
  2. @Override
  3. public void sayHello() {
  4. System.out.println("Hello World");
  5. }
  6. }

调用处理类定义

代理类调用真实角色的方法时,其实是调用与真实角色绑定的处理类对象的invoke()方法,而invoke()调用的是真实角色的方法。

这里需要实现 InvocationHandler 接口以及invoke()方法。

  1. public class UserHandler implements InvocationHandler {
  2. private User user;
  3. public UserProxy(User user) {
  4. this.user = user;
  5. }
  6. @Override
  7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8. System.out.println("invoking start....");
  9. method.invoke(user);
  10. System.out.println("invoking stop....");
  11. return user;
  12. }
  13. }

执行类

  1. public class Main {
  2. public static void main(String[] args) {
  3. User user = new User();
  4. // 处理类和真实角色绑定
  5. UserHandler userHandler = new UserHandler(user);
  6. // 开启将代理类class文件保存到本地模式,平时可以省略
  7. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
  8. // 动态代理生成代理对象$Proxy0
  9. Object o = Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{
  10. UserInterface.class}, userHandler);
  11. // 调用的其实是invoke()
  12. ((UserInterface)o).sayHello();
  13. }

运行结果:
动态代理运行结果
这样动态代理的基本用法就学完了,可是还有好多问题不明白。

  1. 动态代理是怎么调用的invoke()方法?
  2. 处理类UserHandler有什么作用?
  3. 为什么要将类加载器和接口类数组当作参数传入newProxyInstance?

假如让你去实现动态代理,你有什么设计思路?

猜想

动态代理,是不是和静态代理,即设计模式的代理模式有相同之处呢?

简单捋一捋代理模式实现原理:真实角色和代理角色共同实现一个接口并实现抽象方法A,代理类持有真实角色对象,代理类在A方法中调用真实角色对象的A方法。在Main中实例化代理对象,调用其A方法,间接调用了真实角色的A方法。

实现代码

  1. // 接口和真实角色对象就用上面代码
  2. // 代理类,实现UserInterface接口
  3. public class UserProxy implements UserInterface {
  4. // 持有真实角色对象
  5. private User user = new User();
  6. @Override
  7. public void sayHello() {
  8. System.out.println("invoking start....");
  9. // 在代理对象的sayHello()里调用真实角色的sayHello()
  10. user.sayHello();
  11. System.out.println("invoking stop....");
  12. }
  13. }
  14. // 运行类
  15. public class Main {
  16. public static void main(String[] args) {
  17. // 实例化代理角色对象
  18. UserInterface userProxy = new UserProxy();
  19. // 调用了代理对象的sayHello(),其实是调用了真实角色的sayHello()
  20. userProxy.sayHello();
  21. }

拿开始的动态代理代码和静态代理比较,接口、真实角色都有了,区别就是多了一个UserHandler处理类,少了一个UserProxy代理类。

接着对比一下两者的处理类和代理类,发现UserHandler的invoke()和UserProxy的sayHello()这两个方法的代码都是一样的。那么,是不是新建一个UserProxy类,然后实现UserInterface接口并持有UserHandler的对象,在sayHello()方法中调用UserHandler的invoke()方法,就可以动态代理了。

代码大概就是这样的

  1. // 猜想的代理类结构,动态代理生成的代理是com.sun.proxy.$Proxy0
  2. public class UserProxy implements UserInterface{
  3. // 持有处理类的对象
  4. private InvocationHandler handler;
  5. public UserProxy(InvocationHandler handler) {
  6. this.handler = handler;
  7. }
  8. // 实现sayHello()方法,并调用invoke()
  9. @Override
  10. public void sayHello() {
  11. try {
  12. handler.invoke(this, UserInterface.class.getMethod("sayHello"), null);
  13. } catch (Throwable throwable) {
  14. throwable.printStackTrace();
  15. }
  16. }
  17. }
  18. // 执行类
  19. public static void main(String[] args) {
  20. User user = new User();
  21. UserHandler userHandler = new UserHandler(user);
  22. UserProxy proxy = new UserProxy(userHandler);
  23. proxy.sayHello();
  24. }

输出结果:
在这里插入图片描述

上面的代理类代码是写死的,而动态代理是当你调用Proxy.newProxyInstance()时,会根据你传入的参数来动态生成这个代理类代码,如果让我实现,会是以下这个流程。

  1. 根据你传入的Class[]接口数组,代理类会来实现这些接口及其方法(这里就是sayHello()),并且持有你传入的userHandler对象,使用文件流将预先设定的包名、类名、方法名等一行行代码写到本地磁盘,生成$Proxy0.java文件
  2. 使用编译器将$Proxy0.java编译成$Proxy0.class
  3. 根据你传入的ClassLoader将$Proxy0.class加载到JMV中
  4. 调用Proxy.newProxyInstance()就会返回一个$Proxy0的对象,然后调用sayHello(),就执行了里面userHandler的invoke()

以上就是对动态代理的一个猜想过程,下面就通过debug看看源码是怎么实现的。

在困惑的日子里学会拥抱源码

拥抱源码

调用流程图

这里先用PPT画一个流程图,可以跟着流程图来看后面的源码。

流程图

从newProxyInstance()设置断点
main

newProxyInstance()

newProxyInstance()代码分为上下两部分,上部分是获取类$Proxy0.class,下部分是通过反射构建$Proxy0对象。

上部分代码

newProxyInstance()

从名字看就知道getProxyClass0()是核心方法,step into

getProxyClass0()

getProxyClass()

里面调用了WeakCache对象的get()方法,这里暂停一下debug,先讲讲WeakCache类。

WeakCache

顾名思义,它是一个弱引用缓存。那什么是是弱引用呢,是不是还有强引用呢?

弱引用

WeakReference就是弱引用类,作为包装类来包装其他对象,在进行GC时,其中的包装对象会被回收,而WeakReference对象会被放到引用队列中。

举个栗子:

  1. // 这就是强引用,只要不写str1 = null,str1指向的这个字符串不就会被垃圾回收
  2. String str1 = new String("hello");
  3. ReferenceQueue referenceQueue = new ReferenceQueue();
  4. // 只要垃圾回收,这个str2里面包装的对象就会被回收,但是这个弱引用对象不会被回收,即word会被回收,但是str2指向的弱引用对象不会
  5. // 每个弱引用关联一个ReferenceQueue,当包装的对象被回收,这个弱引用对象会被放入引用队列中
  6. WeakReference<String> str2 = new WeakReference<>(new String("world"), referenceQueue);
  7. // 执行gc
  8. System.gc();
  9. Thread.sleep(3);
  10. // 输出被回收包装对象的弱引用对象:java.lang.ref.WeakReference@2077d4de
  11. // 可以debug看一下,弱引用对象的referent变量指向的包装对象已经为null
  12. System.out.println(referenceQueue.poll());

WeakCache的结构

其实整个WeakCache的都是围绕着成员变量map来工作的,构建了一个一个>格式的二级缓存,在动态代理中对应的类型是<类加载器, <接口Class, 代理Class>>,它们都使用了弱引用进行包装,这样在垃圾回收的时候就可以直接回收,减少了堆内存占用。

  1. // 存放已回收弱引用的队列
  2. private final ReferenceQueue<K> refQueue = new ReferenceQueue<>();
  3. // 使用ConcurrentMap实现的二级缓存结构
  4. private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map = new ConcurrentHashMap<>();
  5. // 可以不关注这个,这个是用来标识二级缓存中的value是否存在的,即Supplier是否被回收
  6. private final ConcurrentMap<Supplier<V>, Boolean> reverseMap = new ConcurrentHashMap<>();
  7. // 包装传入的接口class,生成二级缓存的Key
  8. private final BiFunction<K, P, ?> subKeyFactory = new KeyFactory();
  9. // 包装$Proxy0,生成二级缓存的Value
  10. private final BiFunction<K, P, V> valueFactory = new ProxyClassFactory();

WeakCache的get()

回到debug,接着进入get()方法,看看map二级缓存是怎么生成KV的。

  1. public V get(K key, P parameter) {
  2. Objects.requireNonNull(parameter);
  3. // 遍历refQueue,然后将缓存map中对应的失效value删除
  4. expungeStaleEntries();
  5. // 以ClassLoader为key,构建map的一级缓存的Key,是CacheKey对象
  6. Object cacheKey = CacheK.valueOf(key, refQueue);
  7. // 通过Key从map中获取一级缓存的value,即ConcurrentMap
  8. ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
  9. if (valuesMap == null) {
  10. // 如果Key不存在,就新建一个ConCurrentMap放入map,这里使用的是putIfAbsent
  11. // 如果key已经存在了,就不覆盖并返回里面的value,不存在就返回null并放入Key
  12. // 现在缓存map的结构就是ConCurrentMap<CacheKey, ConCurrentMap<Object, Supplier>>
  13. ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>());
  14. // 如果其他线程已经创建了这个Key并放入就可以复用了
  15. if (oldValuesMap != null) {
  16. valuesMap = oldValuesMap;
  17. }
  18. }
  19. // 生成二级缓存的subKey,现在缓存map的结构就是ConCurrentMap<CacheKey, ConCurrentMap<Key1, Supplier>>
  20. // 看后面的<生成二级缓存Key>!!!
  21. Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
  22. // 根据二级缓存的subKey获取value
  23. Supplier<V> supplier = valuesMap.get(subKey);
  24. Factory factory = null;
  25. // !!!直到完成二级缓存Value的构建才结束,Value是弱引用的$Proxy0.class!!!
  26. while (true) {
  27. // 第一次循环:suppiler肯定是null,因为还没有将放入二级缓存的KV值
  28. // 第二次循环:这里suppiler不为null了!!!进入if
  29. if (supplier != null) {
  30. // 第二次循环:真正生成代理对象,
  31. // 往后翻,看<生成二级缓存Value>,核心!!!!!
  32. // 看完后面回到这里:value就是弱引用后的$Proxy0.class
  33. V value = supplier.get();
  34. if (value != null) {
  35. // 本方法及上部分的最后一行代码,跳转最后的<构建$Proxy对象>
  36. return value;
  37. }
  38. }
  39. // 第一次循环:factory肯定为null,生成二级缓存的Value
  40. if (factory == null) {
  41. factory = new Factory(key, parameter, subKey, valuesMap);
  42. }
  43. // 第一次循环:将subKey和factory作为KV放入二级缓存
  44. if (supplier == null) {
  45. supplier = valuesMap.putIfAbsent(subKey, factory);
  46. if (supplier == null) {
  47. // 第一次循环:赋值之后suppiler就不为空了,记住!!!!!
  48. supplier = factory;
  49. }
  50. }
  51. }
  52. }
  53. }

生成二级缓存Key

在get()中调用subKeyFactory.apply(key, parameter),根据你newProxyInstance()传入的接口Class[]的个数来生成二级缓存的Key,这里我们就传入了一个UserInterface.class,所以就返回了Key1对象。

KeyFactory.apply()

不论是Key1、Key2还是KeyX,他们都继承了WeakReference,都是包装对象是Class的弱引用类。这里看看Key1的代码。

Key1

生成二级缓存Value

在上面的while循环中,第一次循环只是生成了一个空的Factory对象放入了二级缓存的ConcurrentMap中。

在第二次循环中,才开始通过get()方法来真正的构建value。

别回头,接着往下看。

Factory.get()生成弱引用value

CacheValue类是一个弱引用,是二级缓存的Value值,包装的是class,在这里就是$Proxy0.class,至于这个类如何生成的,根据下面代码注释一直看完Class文件的生成

  1. public synchronized V get() {
  2. // 检查是否被回收,如果被回收,会继续执行上面的while循环,重新生成Factory
  3. Supplier<V> supplier = valuesMap.get(subKey);
  4. if (supplier != this) {
  5. return null;
  6. }
  7. // 这里的V的类型是Class
  8. V value = null;
  9. // 这行是核心代码,看后面<class文件的生成>,记住这里返回的是Class
  10. value = Objects.requireNonNull(valueFactory.apply(key, parameter));
  11. // 将Class对象包装成弱引用
  12. CacheValue<V> cacheValue = new CacheValue<>(value);
  13. // 回到上面<WeakCache的get()方法>V value = supplier.get();
  14. return value;
  15. }
  16. }

CacheValue

Class文件的生成

包名类名的定义与验证

进入valueFactory.apply(key, parameter)方法,看看class文件是怎么生成的。

  1. private static final String proxyClassNamePrefix = "$Proxy";
  2. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  3. Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
  4. // 遍历你传入的Class[],我们只传入了UserInterface.class
  5. for (Class<?> intf : interfaces) {
  6. Class<?> interfaceClass = null;
  7. // 获取接口类
  8. interfaceClass = Class.forName(intf.getName(), false, loader);
  9. // 这里就很明确为什么只能传入接口类,不是接口类会报错
  10. if (!interfaceClass.isInterface()) {
  11. throw new IllegalArgumentException(
  12. interfaceClass.getName() + " is not an interface");
  13. }
  14. String proxyPkg = null;
  15. int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
  16. for (Class<?> intf : interfaces) {
  17. int flags = intf.getModifiers();
  18. // 验证接口是否是public,不是public代理类会用接口的package,因为只有在同一包内才能继承
  19. // 我们的UserInterface是public,所以跳过
  20. if (!Modifier.isPublic(flags)) {
  21. accessFlags = Modifier.FINAL;
  22. String name = intf.getName();
  23. int n = name.lastIndexOf('.');
  24. String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  25. if (proxyPkg == null) {
  26. proxyPkg = pkg;
  27. } else if (!pkg.equals(proxyPkg)) {
  28. throw new IllegalArgumentException(
  29. "non-public interfaces from different packages");
  30. }
  31. }
  32. }
  33. // 如果接口类是public,则用默认的包
  34. if (proxyPkg == null) {
  35. // PROXY_PACKAGE = "com.sun.proxy";
  36. proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  37. }
  38. // 原子Int,此时num = 0
  39. long num = nextUniqueNumber.getAndIncrement();
  40. // com.sun.proxy.$Proxy0,这里包名和类名就出现了!!!
  41. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  42. // !!!!生成class文件,查看后面<class文件写入本地> 核心!!!!
  43. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
  44. // !!!看完下面再回来看这行!!!!
  45. // 获取了字节数组之后,获取了class的二进制流将类加载到了JVM中
  46. // 并且返回了$Proxy0.class,返回给Factory.get()来包装
  47. return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
  48. }
  49. }
  50. }

defineClass0()是Proxy类自定义的类加载的native方法,会获取class文件的二进制流加载到JVM中,以获取对应的Class对象,这一块可以参考JVM类加载器。

class文件写入本地

generateProxyClass()方法会将class二进制文件写入本地目录,并返回class文件的二进制流,使用你传入的类加载器加载,这里你知道类加载器的作用了么

  1. public static byte[] generateProxyClass(final String name,
  2. Class[] interfaces)
  3. {
  4. ProxyGenerator gen = new ProxyGenerator(name, interfaces);
  5. // 生成class文件的二进制,查看后面<生成class文件二进制>
  6. final byte[] classFile = gen.generateClassFile();
  7. // 将class文件写入本地
  8. if (saveGeneratedFiles) {
  9. java.security.AccessController.doPrivileged(
  10. new java.security.PrivilegedAction<Void>() {
  11. public Void run() {
  12. try {
  13. FileOutputStream file =
  14. new FileOutputStream(dotToSlash(name) + ".class");
  15. file.write(classFile);
  16. file.close();
  17. return null;
  18. } catch (IOException e) {
  19. throw new InternalError(
  20. "I/O exception saving generated file: " + e);
  21. }
  22. }
  23. });
  24. }
  25. // 返回$Proxy0.class字节数组,回到上面<class文件生成>
  26. return classFile;
  27. }

生成class文件二进制流

generateClassFile()生成class文件,并存放到字节数组,可以顺便学一下class结构,这里也体现了你传入的class[]的作用

  1. private byte[] generateClassFile() {
  2. // 将hashcode、equals、toString是三个方法放入代理类中
  3. addProxyMethod(hashCodeMethod, Object.class);
  4. addProxyMethod(equalsMethod, Object.class);
  5. addProxyMethod(toStringMethod, Object.class);
  6. for (int i = 0; i < interfaces.length; i++) {
  7. Method[] methods = interfaces[i].getMethods();
  8. for (int j = 0; j < methods.length; j++) {
  9. // 将接口类的方法放入新建的代理类中,这里就是sayHello()
  10. addProxyMethod(methods[j], interfaces[i]);
  11. }
  12. }
  13. for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
  14. checkReturnTypes(sigmethods);
  15. }
  16. // 给代理类增加构造方法
  17. methods.add(generateConstructor());
  18. for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
  19. for (ProxyMethod pm : sigmethods) {
  20. // 将上面的四个方法都封装成Method类型成员变量
  21. fields.add(new FieldInfo(pm.methodFieldName,
  22. "Ljava/lang/reflect/Method;",
  23. ACC_PRIVATE | ACC_STATIC));
  24. // generate code for proxy method and add it
  25. methods.add(pm.generateMethod());
  26. }
  27. }
  28. // static静态块构造
  29. methods.add(generateStaticInitializer());
  30. cp.getClass(dotToSlash(className));
  31. cp.getClass(superclassName);
  32. for (int i = 0; i < interfaces.length; i++) {
  33. cp.getClass(dotToSlash(interfaces[i].getName()));
  34. }
  35. cp.setReadOnly();
  36. ByteArrayOutputStream bout = new ByteArrayOutputStream();
  37. DataOutputStream dout = new DataOutputStream(bout);
  38. // !!!核心点来了!这里就开始构建class文件了,以下都是class的结构,只写一部分
  39. try {
  40. // u4 magic,class文件的魔数,确认是否为一个能被JVM接受的class
  41. dout.writeInt(0xCAFEBABE);
  42. // u2 minor_version,0
  43. dout.writeShort(CLASSFILE_MINOR_VERSION);
  44. // u2 major_version,主版本号,Java8对应的是52;
  45. dout.writeShort(CLASSFILE_MAJOR_VERSION);
  46. // 常量池
  47. cp.write(dout);
  48. // 其他结构,可参考class文件结构
  49. dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
  50. dout.writeShort(cp.getClass(dotToSlash(className)));
  51. dout.writeShort(cp.getClass(superclassName));
  52. dout.writeShort(interfaces.length);
  53. for (int i = 0; i < interfaces.length; i++) {
  54. dout.writeShort(cp.getClass(
  55. dotToSlash(interfaces[i].getName())));
  56. }
  57. dout.writeShort(fields.size());
  58. for (FieldInfo f : fields) {
  59. f.write(dout);
  60. }
  61. dout.writeShort(methods.size());
  62. for (MethodInfo m : methods) {
  63. m.write(dout);
  64. }
  65. dout.writeShort(0);
  66. } catch (IOException e) {
  67. throw new InternalError("unexpected I/O Exception", e);
  68. }
  69. // 将class文件字节数组返回
  70. return bout.toByteArray();
  71. }

构建$Proxy对象

newProxyInstance()上半部分经过上面层层代码调用,获取了$Proxy0.class,接下来看下部分代码:

newInstance

cl就是上面获取的Proxy0.class,h就是上面传入的userHandler,被当做构造参数来创建$Proxy0对象。然后获取这个动态代理对象,调用sayHello()方法,相当于调用了UserHandler的invoke(),这里就是UserHandler的作用

$Proxy.class文件

我们开启了将代理class写到本地目录的功能,在项目下的com/sum/proxy目录下找到了$Proxy0的class文件。

看一下反编译的class

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

结语

上面就是动态代理源码的调试过程,与之前的猜想的代理类的生成过程比较,动态代理是直接生成class文件,省去了java文件和编译这一块。

刚开始看可能比较绕,跟着注释及跳转指引,耐心多看两遍就明白了。动态代理涉及的知识点比较多,我自己看的时候,在WeakCache这一块纠结了一阵,其实把它当成一个两层的map对待即可,只不过里面所有的KV都被弱引用包装。

发表评论

表情:
评论列表 (有 0 条评论,80人围观)

还没有评论,来说两句吧...

相关阅读

    相关 深入Java动态代理

    背景介绍 什么是动态代理? 动态代理,本质上还是代理模式,只不过代理类是在JDK内部产生并创建实例(字节码编辑)。 再者其内部还维护了一个基于弱引用的缓存结构。总结来说:

    相关 分析JDK动态代理

    引言 动态代理非常的重要,虽然我们在日常的工作中没有非常底层的 编写过动态代理的代码,但是动态代理却起着非常重要的功能,想一下我们经常使用的框架: 日志框架、AOP等等,