JDK动态代理是如何通过反射机制实现的?源码解析

什么是Class对象?

说到动态代理必须得先说Class对象。

一个Class对象(可通过Class.forName("")获取),代表正在运行中的java应用的类和接口,通过反射机制可以在运行时获取类信息,如:类名、成员属性、构造方法等。

其中 枚举类(enum)是一种特殊的类,注解 属于 一种接口 ,java中所有注解(@interface)都隐形地继承Annotation接口。

数组类也属于Class类,他的Class类对象被同类型数组(元素类型、数组维度相同)共享,具体看Class#getName()的说明。

什么是反射机制?

Java 反射机制是在运行时分析类、获取类信息、访问类的成员变量和方法的一种机制。可以动态地创建对象、调用方法、修改属性,增加了程序的灵活性和可扩展性。

什么是JDK动态代理?

JDK 动态代理是一种在运行时动态生成代理对象的机制。它基于接口实现,通过反射机制在运行时创建代理类并拦截对目标对象方法的调用,可在方法调用前后添加额外逻辑,如日志记录、权限校验等,实现对目标对象的功能增强,而无需修改目标对象的代码。

假设有这么个接口和实现类

接口IService

 

csharp

代码解读

复制代码

public interface IService { public void hello(); public void say(String name); public void goods(); }

接口IService的实现类 ServiceImpl

 

typescript

代码解读

复制代码

public class ServiceImpl implements IService { @Override public void hello() { System.out.println("Hello China!"); } @Override public void say(String name) { System.out.println("say()"); } @Override public void goods() { System.out.println("goods()"); } }

如果客户要求你在ServiceImpl 中的每个方法 执行前和执行后 分别输出 "目标方法执行前的输出。”和"目标方法执行后的输出。”,你是不是会想到这样:

 

csharp

代码解读

复制代码

public class ServiceProxy { private IService service; public ServiceProxy(IService service){ this.service=service; } public void hello() { System.out.println("目标方法执行前的输出。"); service.hello(); System.out.println("目标方法执行后的输出。"); } @Override public void say(String name) { System.out.println("目标方法执行前的输出。"); service.say(name); System.out.println("目标方法执行后的输出。"); } @Override public void goods() { System.out.println("目标方法执行前的输出。"); service.goods(); System.out.println("目标方法执行后的输出。"); } } //然后再main方法中执行 IService service = new ServiceImpl(); ServiceProxy proxy = new ServiceProxy(service); proxy.hello(); proxy.say("hello"); proxy.goods();

固然可以实现需求,但这无疑造成了代码冗余,也不利于后期代码的维护。

那么我们是不是可以在运行时拦截目标方法,然后将额外的需求织入其中并生成代理对象,最后通过反射机制,使用Method对象、目标对象以及方法参数来定位目标方法并执行?这就是接下来讲的JDK动态代理。

实现步骤

首先我们的代理类要实现 InvocationHandler 接口

 

typescript

代码解读

复制代码

public class ServiceProxy implements InvocationHandler { private IService realService; public ServiceProxy(IService realService) { this.realService = realService; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目标方法执行前的输出。"); //通过反射来动态的调用目标方法 Object result = method.invoke(realService, args); System.out.println("目标方法执行后的输出。"); return result; } //获取代理对象 public Object getProxy() { return Proxy.newProxyInstance(realService.getClass().getClassLoader(), realService.getClass().getInterfaces(), this); } }

然后编写测试类

 

csharp

代码解读

复制代码

public class ProxyTest { public static void main(String[] args) { //实例化目标对象 IService real = new ServiceImpl(); //传入目标对象,创建代理类 ServiceProxy proxy = new ServiceProxy(real); //生成代理对象 IService service = (IService) proxy.getProxy(); //通过代理对象调用增强后的方法以及目标方法 service.hello(); //将生成的代理类的 class 文件保存到本地,方便反编译后查看 createProxyClassFile(); } @SuppressWarnings("unused") private static void createProxyClassFile() { // 将jdk生成的动态代理类的.class文件存放到本地 String name = "$ServiceProxy"; byte[] data = ProxyGenerator.generateProxyClass(name, new Class[] { IService.class }); FileOutputStream out = null; try { out = new FileOutputStream(name + ".class"); System.out.println((new File(name)).getAbsolutePath()); out.write(data); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (null != out) try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }

源码解析

查看反编译后的代理类代码

 

typescript

代码解读

复制代码

public final class $ServiceProxy extends Proxy implements IService { private static Method m1; private static Method m5; private static Method m4; private static Method m2; private static Method m3; private static Method m0; public $ServiceProxy(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void goods() { try { this.h.invoke(this, m5, null); return; } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void hello() { try { this.h.invoke(this, m4, null); return; } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final void say(String paramString) { try { this.h.invoke(this, m3, new Object[] { paramString }); return; } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final int hashCode() { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m5 = Class.forName("com.cen.proxy.IService").getMethod("goods", new Class[0]); m4 = Class.forName("com.cen.proxy.IService").getMethod("hello", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.cen.proxy.IService").getMethod("say", new Class[] { Class.forName("java.lang.String") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException noSuchMethodException) { throw new NoSuchMethodError(noSuchMethodException.getMessage()); } catch (ClassNotFoundException classNotFoundException) { throw new NoClassDefFoundError(classNotFoundException.getMessage()); } } }

我们可以看到,代理类先通过Class.forName 去获取被代理接口的Class对象,然后再调用Class对象的getMethod方法,通过方法名和方法传参来返回一个目标方法的Method对象

 

vbnet

代码解读

复制代码

Class.forName("com.cen.proxy.IService").getMethod("hello", new Class[0])

该Method对象会在$ServiceProxy调用方法的时候使用到,他将会作为参数传入

 

css

代码解读

复制代码

ServiceProxy#invoke(Object proxy, Method method, Object[] args)

并被使用

 

typescript

代码解读

复制代码

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(realService, args); return result; }

例如: hello()的调用,调用链是这样的:

步骤1. main方法

 

scss

代码解读

复制代码

service.hello()

步骤2. $ServiceProxy的static代码块

 

vbnet

代码解读

复制代码

m4 = Class.forName("com.cen.proxy.IService").getMethod("hello", new Class[0])

步骤3. $ServiceProxy#hello()

 

typescript

代码解读

复制代码

public final void hello() { try { this.h.invoke(this, m4, null); return; } catch (Error|RuntimeException error) { throw null; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }

步骤4. ServiceProxy#invoke

 

typescript

代码解读

复制代码

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("目标方法执行前的输出。"); Object proxyObj = method.invoke(realService, args); System.out.println("目标方法执行后的输出。"); return proxyObj; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值