java开发两年!连JDK动态代理-使用及原理都不知道,你加薪无望!

it2024-07-04  42

JDK动态代理

为了引出动态代理,我们看看一个案列!

广东广州,早上9:00,一位靓仔穿着人字拖、提着鸟笼,走进了早茶店。没错,这就是广州典型的包租公!名下几栋楼,只收租为生,没工作,这人身真是无趣至极!

这里就得出一个问题:收租不算工作?好吧,其实正真的包租公不会自己去收租,都是委托给中介去做。为什么呢?这其中可以说牵扯到安全、隐私等等。想一下,假如包租公自己收租,当下租客很多,其他包租公就不爽了,干脆找人去捣乱,比如只问不租,浪费包租公时间。当然不仅仅是这样…

简单使用

好的,租房中介就出来了,租客看房、谈价、签合同、交付钥匙等等都让中介(代理)去做,房东就坐等收钱就行了。

代码中使用输出语句代替真正的业务!

首先,我们定义一个房东接口,和一个房东实现类。(为什么需要接口?后面代理类是根据接口得到的,而且房东类不应该暴露出去的)

public interface ILandlordService { void deliver(); } public class LandlordServiceImpl implements ILandlordService { public void deliver() { try { System.out.println("告知房东出租成功,房东收钱"); } catch (Exception e) { e.printStackTrace(); System.out.println("出现异常,终止"); } } }

接下来创建一个类,实现java.lang.reflect.InvocationHandler接口,该类用来对原来的方法增强。(注意包,被导错)

import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TransactionInvocationHandler implements InvocationHandler { // 需要被代理的对象(房东) private Object target; public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } /* 实现接口需要重写的方法 proxy:代理对象 method:房东接口中被调用的方法 args:房东接口中被调用的方法需要的参数 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object retVal = null; try { System.out.println("交付前与中介的交谈"); // 房东收钱 retVal = method.invoke(target, args); System.out.println("租房成功"); System.out.println("给钥匙"); } catch (Exception e) { e.printStackTrace(); System.out.println("出现异常,终止交付"); } return retVal; } }

最后写测试类(租客)

import org.junit.Test; import java.lang.reflect.Proxy; public class TenantTest { @Test public void test() { //这两个创建对象和设置值应在spring中配置,让spring创建、设值 // 创建一个房东对象 ILandlordService landlordService = new LandlordServiceImpl(); // 创建增强类对象 TransactionInvocationHandler transactionInvocationHandler = new TransactionInvocationHandler(); // 把房东对象设置到增强类对象中使用 transactionInvocationHandler.setTarget(landlordService); /* 使用强转得到房东类代理对象(中介) newProxyInstance方法需要三个参数 ClassLoader loader:类加载器,直接使用本类的类加载器(系统类加载器) Class<?>[] interfaces:接口数组 InvocationHandler h:增强类 */ ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance( this.getClass().getClassLoader(), // 这里landlordService对象暂时在测试类中创建, // 使用spring后,注入到TransactionInvocationHandler(增强类)的字段(target)中, // 直接到增强类中获取,这样测试类(租客)就不知道实现类了(房东) landlordService.getClass().getInterfaces(), transactionInvocationHandler ); // 调用代理对象的deliver(),会执行增强类的invoke方法,参数method为代理对象调用的方法 proxy.deliver(); } } 执行结果: 交付前与中介的交谈 告知房东出租成功,房东收钱 租房成功 给钥匙

此时很多人就疑惑了,怎么创建代理类的?又怎么会调用增强类的invoke方法的?那就接下去看看原理吧!建议截图看,代码有点多!

原理

测试类中的newProxyInstance方法,点进去

ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance( this.getClass().getClassLoader(), landlordService.getClass().getInterfaces(), transactionInvocationHandler );

到proxy类的newProxyInstance方法,省去不必要的代码

@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) { /* * Look up or generate the designated proxy class and its constructor. * 查找或生成 指定的代理类和它的构造函数 */ /* 获取生成类的构造器 loader:类加载器 interfaces:接口数组 */ Constructor<?> cons = getProxyConstructor(caller, loader, interfaces); return newProxyInstance(caller, cons, h); }

往newProxyInstance方法中走,省去不必要的代码,try-catch也省去了

private static Object newProxyInstance(Class<?> caller, Constructor<?> cons, InvocationHandler h) { // caller有关安全的,这里忽略 checkNewProxyPermission(caller, cons.getDeclaringClass()); /* 反射调用构造器创建这个类的对象 cons:代理类构造器 h:增强类 */ return cons.newInstance(new Object[]{h}); }

使用以下代码获取代理类字节码文件

ProxyGenerator 在 JDK 11 已经不是公有的了,我这里的源码是JKD 11 下面获取代理类字节码用的是JDK 8

import sun.misc.ProxyGenerator; import java.io.FileOutputStream; public class DynamicProxyClassGenerator { public static void main(String[] args) throws Exception { // 实现类的class对象 generateClassFile(LandlordServiceImpl.class, "LandlordServiceProxy"); } public static void generateClassFile(Class<?> targetClass, String proxyName) throws Exception { // 根据类信息和提供的代理类名称,生成字节码 byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, targetClass.getInterfaces()); String path = targetClass.getResource(".").getPath(); System.out.println(path); FileOutputStream out = null; // 保留到硬盘中 out = new FileOutputStream(path + proxyName + ".class"); out.write(classFile); out.close(); } }

代理类生成的class文件是这样的,deliver()方法是根据我们定义的类得来的,省去几个Object类得到的方法内容

调用有参构造器创建对象

import com.hao.service.ILandlordService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class LandlordServiceProxy extends Proxy implements ILandlordService { private static Method m1; private static Method m2; private static Method m3; private static Method m0; // 创建对象时调用的方法 public LandlordServiceProxy(InvocationHandler var1) throws { // 调用父类(proxy类)构造器 super(var1); } public final boolean equals(Object var1) throws { } public final String toString() throws { } public final void deliver() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("com.hao.service.ILandlordService").getMethod("deliver"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } } protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); // 把增强类设置到proxy类的字段上 this.h = h; } protected InvocationHandler h;

最终Proxy.newProxyInstance()返回的是代理类(中介)

ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance( ); proxy.deliver();

然后我们调用deliver()方法,进入的是代理类的deliver()方法

public final void deliver() throws { try { // 调用父类的h字段(增强类)中的invoke方法,m3在本类static{}中获取 super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }

m3在static{}中

static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m2 = Class.forName("java.lang.Object").getMethod("toString"); // 通过反射得到deliver方法 m3 = Class.forName("com.hao.service.ILandlordService").getMethod("deliver"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }

最后

可以看出,调用代理类最终会调用增强类的invoke方法。感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

最新回复(0)