看过Jdk动态代理类长啥样吗?Jdk动态代理原理源码分析

本文深入剖析JDK动态代理机制,解释为何代理类需实现接口,通过源码解读动态代理全过程,展示代理类内部结构,教授如何自定义代理类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在看Spring的源码,正好遇到几个Aop的问题涉及到Java的动态代理,之前对这个东西大致能理解,但是没有仔细的去看源码,今天我们来扒一扒它的真面目。

阅读本文,你将会收获:

  • 理解为什么Jdk动态代理,代理类必须实现接口
  • 理解Jdk动态代理全过程
  • 看到Jdk的动态代理类的类的内容
    如果只想看这个可以直接翻到最后/(ㄒoㄒ)/
  • 如何自己得到一个代理类的内容
  • 如何看源码
  • 变强

老规矩,上代码观现象

依赖的类,明白上面的过程可以跳过不看public interface HelloService {
 void sayHello();
}
/******************************************************/
public class HelloServiceImpl implements HelloService {
 @Override
 public void sayHello() {
 System.out.println("给掘金大佬们低头 (:");
 }
}
/****************************************************/
public class HelloInvocationHandle implements InvocationHandler {
 private Object target;
 public HelloInvocationHandle(Object target) {
 this.target = target;
 }
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 System.out.println("proxy before=======");
 Object result = method.invoke(target, args);
 System.out.println("proxy end=======");
 return result;
 }
}
复制代码

上面的代码,相信大家在学动态代理的时候,都有写过,我就不赘述了,大家有想过他是怎么实现的吗?为什么调代理类的sayHello()就可以直接使用InvocationHandler里的invoke()逻辑呢?为什么代理类必须要实现一个接口这么麻烦呢?我们继续往下面看。

源码分析

我们看上面的Main方法,可以看出来Proxy.newProxyInstance()这个方法承载了代理类的所有的逻辑,所有的魔法都在这里面

//生成一个代理对象
HelloService proxy = (HelloService) Proxy.newProxyInstance(
 Thread.currentThread().getContextClassLoader(),
 hello.getClass().getInterfaces(),handle);
复制代码

我们点进去,看看里面长啥样

 private static final Class<?>[] constructorParams =
 { InvocationHandler.class };
 public static Object newProxyInstance(ClassLoader loader,
 Class<?>[] interfaces,
 InvocationHandler h) {
 ...
 //这里的interface数组里面放的是 HelloService,就是代理类需要实现的那个接口
 //这边复制了一份等待处理
 final Class<?>[] intfs = interfaces.clone();
 ...
 //得到了代理类的Class
 Class<?> cl = getProxyClass0(loader, intfs);
 ...
 //传入构造对象拿到代理类的构造器
 //从这里我们可以猜出这个代理类一个有一个构造方法是传入InvocationHandler进行初始化的
 final Constructor<?> cons = cl.getConstructor(constructorParams);
 //通过反射传入之前我们定义的那个HelloInvocationHandle,进行构造器实例化代理对象
 return cons.newInstance(new Object[]{h});
复制代码

简单的分析一下上面的代码,流程也很简单

  • 传入HelloService.class类进行代理类的生成,这样代理类就有了原始类的方法信息,例如sayHello()
  • 通过一个InvocationHandler这个构造对象拿到该代理类的构造方法
  • 传入之前我们定义的HelloInvocationHandle,构造器实例化了活生生的代理对象 从这里我们大致就能明白了Jdk动态代理的原理:克隆一个接口,生成一个新类作为代理类,这个类里面有着我们定义的HelloInvocationHandle进行代理逻辑的处理

这里就回答了我们上面提到的一个问题: Jdk动态代理,代理类必须实现接口,是因为跟他的实现有关系,他规定了必须要传一个接口去生成代理类

所以所有的谜团就在生成代理类的getProxyClass0(loader, intfs)这个方法里

private static Class<?> getProxyClass0(ClassLoader loader,
 Class<?>... interfaces) {
 ...
 // If the proxy class defined by the given loader implementing
 // the given interfaces exists, this will simply return the cached copy;
 // otherwise, it will create the proxy class via the ProxyClassFactory
 //这里从一个代理缓存的Cache中得到代理,Cache里面涉及虚引用实引用的东西不在我们讨论的范围里,我们直接看这个类是怎么生成的
 return proxyClassCache.get(loader, interfaces);
 }
//从上面的注释和这里我们都可以看出代理类是从一个叫ProxyClassFactory里生成的
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
 proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); 
复制代码

看上面的注释我们可以看出到ProxyClassFactory负责生成代理类

我这里看的是jdk1.8的源码,用lambda重构过,旧版本看到的可以不一样,主逻辑应该没什么变化

// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
 ...
 //这里定义了代理类的名字,所以你每次看到的代理类都是$Proxy开头的
 String proxyName = proxyPkg + proxyClassNamePrefix + num;
 //关键点在这里,这里生成一个代理类
 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
 proxyName, interfaces, accessFlags);
}
复制代码

ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)这个方法如果感兴趣的大佬点进去可以看到,里面是用StringBuilder拼出了这个代理类的字节码(:,然后转成了Byte数组。

Bingo

关键点来了! 这个代理类长啥样怎么看?Debug没有暴露出来啊亲,看不了,小之看到上面的byte[] proxyClassFile这个变量,露出了一丝坏笑,前面说过,这个变量里存的是代理类字节码内容啊!没错!输出流伺候! 小之一顿操作,QWER!飞起

反编译,哦呵

public final class $Proxy0 extends Proxy implements HelloService {
 private static Method m1; //equals()方法
 private static Method m3; //sayHello()方法
 private static Method m2; //Bingo 我们的sayHello方法
 private static Method m0; //hashCode()方法
 
 //这里就是我们之前提交的InvocationHandler这个构造方法!!
 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})).booleanValue();
 } catch (RuntimeException | Error var3) {
 throw var3;
 } catch (Throwable var4) {
 throw new UndeclaredThrowableException(var4);
 }
 }
 //勇士来吧!看一看我们的代理类的sayHello()方法长什么样子呀!!
 public final void sayHello() throws {
 try {
 super.h.invoke(this, m3, (Object[])null);
 } catch (RuntimeException | Error var2) {
 throw var2;
 } catch (Throwable var3) {
 throw new UndeclaredThrowableException(var3);
 }
 }
 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)).intValue();
 } catch (RuntimeException | Error var2) {
 throw var2;
 } catch (Throwable var3) {
 throw new UndeclaredThrowableException(var3);
 }
 }
 static {
 try {
 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
 m3 = Class.forName("proxy.service.HelloService").getMethod("sayHello", new Class[0]);
 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
 } catch (NoSuchMethodException var2) {
 throw new NoSuchMethodError(var2.getMessage());
 } catch (ClassNotFoundException var3) {
 throw new NoClassDefFoundError(var3.getMessage());
 }
 }
}
复制代码

筒子们,请聚焦上面的代理类sayHello()方法。真相大白了吧?代理类里实现了接口里的方法,全部调用了你的HelloInvocation的invoke()方法啦!

这里说一下上面的super.h.invoke(this, m3, (Object[])null);中super.h是什么东西,明眼的大佬都应该看出来了吧?继承了Proxy类,是Proxy里定义的InvocationHandler变量,也就是你的HelloInvocation

总结

大佬们,是不是觉得Jdk动态代理也不是很复杂?好像自己也能写出来?哈哈,本篇文章还想提供一个Debug代码的思路,其实看源码没那么难,希望可以帮到大家,我们下次再见啦~


转载于:https://juejin.im/post/5cff41a7f265da1b9253d139

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值