Java中的动态代理
一、Java中的两种动态代理方式
Java中常用的有两种动态代理方式,分别为:JDK动态代理和Cglib动态代理。
JDK动态代理是通过实现接口的方式完成动态代理。
Cglib动态代理是通过继承目标类或实现接口的方式完成动态代理。
二、JDK动态代理
JDK动态代理中最核心的就是Proxy类和InvocationHandler接口。
Proxy类
通过调用这个类中的静态方法newProxyInstance(),可以返回代理对象。
newProxyInstance()方法需要三个参数,分别是:ClassLoader类加载、目标对象实现的所有接口、InvocationHandler实现类。
InvocationHandler接口
这个接口中有一个invoke方法,代理对象通过调用这个方法对目标方法进行增强。
三、案例
需求
需求:创建计算器类,进行加减乘除运算。同时需要在进行加减乘除之前打印参数信息,在加减乘除之后打印计算结果。
创建类实现功能
创建Calculator计算器接口,提供加减乘除方法,创建实现类,并完成打印参数与计算结果的功能。
接口:
package com.demo.dynamic_proxy.jdk;public interface Calculator { int add(int a, int b); // 加法 int sub(int a, int b); // 减法 int mul(int a, int b); // 乘法 int div(int a, int b); // 除法}
实现类:
public class CalculatorImpl implements Calculator { @Override public int add(int a, int b) { System.out.println("执行add方法,参数信息:" + a + "," + b); int result = a + b; System.out.println("add方法执行完毕,计算结果:" + result); return result; } @Override public int sub(int a, int b) { System.out.println("执sub方法,参数信息:" + a + "," + b); int result = a + b; System.out.println("sub方法执行完毕,计算结果:" + result); return result; } @Override public int mul(int a, int b) { System.out.println("执行mul方法,参数信息:" + a + "," + b); int result = a + b; System.out.println("mul方法执行完毕,计算结果:" + result); return result; } @Override public int div(int a, int b) { System.out.println("执行div方法,参数信息:" + a + "," + b); int result = a + b; System.out.println("div方法执行完毕,计算结果:" + result); return result; }}
分析存在的问题
如果将打印参数信息和打印计算结果的代码都写到实现类中,会出现以下问题:
1)代码比较分散,过于冗余 ,可以将打印参数信息和结果的代码提取到工具类中。
2)代码过于混乱,非核心业务代码(输出语句)与核心业务代码(加减乘除操作)混合到了一起,不好维护。
可以通过动态代理的方式,对功能进行增强,不需要在业务代码(加减乘除的运算)中添加非核心业务代码(打印参数和结果的代码)。
改进代码
将代码提取到工具类中
package com.demo.dynamic_proxy.jdk.util;import java.util.Arrays;public class Logging { public static void beforeMethod(String className, String methodName, Object[] args){ System.out.println("执行" + className + "类中的" + methodName + "方法,参数:" + Arrays.toString(args)); } public static void afterMethod(String className, String methodName, Object result){ System.out.println(className + "类中的" + methodName + "方法执行完毕,结果:" + result); }}
使用动态代理的方式对功能进行增强
增强的功能:方法执行之前打印参数、方法执行之后打印结果。
实现步骤,分为四步:
1)创建类【用来生成代理对象】
2)在类中提供目标对象属性 ,需要被增强的对象。
3)在类中提供有参构造器为目标对象属性赋值 。
4)提供获取代理对象的方法。
package com.demo.dynamic_proxy.jdk;import com.demo.dynamic_proxy.jdk.util.Logging;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class MyProxy { // 目标对象,需要增强的对象 private Object target; // 有参构造器为目标对象进行赋值 public MyProxy(Object target) { this.target = target; } // 获取代理对象的方法 public Object getProxyObject(){ // 代理对象 Object proxyObject = null; // 获取类加载器 ClassLoader classLoader = target.getClass().getClassLoader(); // 获取目标对象实现的所有接口 Class>[] interfaces = target.getClass().getInterfaces(); // 调用Proxy.newProxyInstance()方法,创建代理对象 proxyObject = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 目标对象类名 String targetClassName = target.getClass().getName(); // 要执行的方法名 String targetMethodName = method.getName(); // 在方法调用之前,打印方法参数 Logging.beforeMethod(targetClassName, targetMethodName, args); // 调用目标对象的方法 Object result = method.invoke(target, args); // 调用方法之后,输出执行结果 Logging.afterMethod(targetClassName, targetMethodName, result); // 将执行结果返回 return result; } }); // 将代理对象返回 return proxyObject; }}
测试代码
@Testpublic void test(){ // 创建目标对象,也就是需要被增强的对象 CalculatorImpl calculator = new CalculatorImpl(); // 创建MyProxy并获取代理对象 MyProxy myProxy = new MyProxy(calculator); Calculator proxyObject = (Calculator) myProxy.getProxyObject(); // 调用方法 proxyObject.add(1, 2);}
执行结果:
执行com.demo.dynamic_proxy.jdk.impl.CalculatorImpl类中的add方法,参数:[1, 2]正在执行add方法......com.demo.dynamic_proxy.jdk.impl.CalculatorImpl类中的add方法执行完毕,结果:3
我们通过动态代理的方式完成了参数和执行结果的打印功能,以后需要调整打印信息时,只需要修改一处即可。
四、底层原理分析
JDK动态代理是如何生成代理对象的呢?底层真的没有创建类吗?
底层其实是有创建类的,Java中必须有类才能够使用这个类的实例,JDK动态代理运行时,底层创建代理类实现目标对象的接口,并生成代理类的字节码,通过类加载器将字节码进行加载,最后再生成代理类对象实例。
查看Proxy类的newProxyInstance()方法:
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } // 通过这个方法来生成代理类 Class> cl = getProxyClass0(loader, intfs); try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } // 创建代理类实例 final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
其中,通过getProxyClass0方法来获取代理类
Class> cl = getProxyClass0(loader, intfs);
getProxyClass0方法中有一段核心的代码:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
这段代码就是生成代理类的字节码信息的。
我们试着调用这个方法,并获取到class字节码:
@Testpublic void test01(){ // 要生成的类的名称 String className = "myClass"; // 指定class类名以及代理类要实现的接口 byte[] bytes = ProxyGenerator.generateProxyClass(className, new Class[]{Calculator.class}); FileOutputStream fos = null; try { // 将生成的字节信息输出到 myClass.class fos = new FileOutputStream(new File("D:\\" + className + ".class")); fos.write(bytes); }catch (Exception e){ System.out.println(e.getMessage()); }finally { if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } }}
使用反编译工具打开class文件,这里使用的是IDEA:
字节码中的add方法:
完整代码:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//import com.demo.dynamic_proxy.jdk.Calculator;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class myClass extends Proxy implements Calculator { private static Method m1; private static Method m2; private static Method m4; private static Method m3; private static Method m6; private static Method m5; private static Method m0; public myClass(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 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 mul(int var1, int var2) throws { try { return (Integer)super.h.invoke(this, m4, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final int add(int var1, int var2) throws { try { return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final int sub(int var1, int var2) throws { try { return (Integer)super.h.invoke(this, m6, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } public final int div(int var1, int var2) throws { try { return (Integer)super.h.invoke(this, m5, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } 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")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m4 = Class.forName("com.demo.dynamic_proxy.jdk.Calculator").getMethod("mul", Integer.TYPE, Integer.TYPE); m3 = Class.forName("com.demo.dynamic_proxy.jdk.Calculator").getMethod("add", Integer.TYPE, Integer.TYPE); m6 = Class.forName("com.demo.dynamic_proxy.jdk.Calculator").getMethod("sub", Integer.TYPE, Integer.TYPE); m5 = Class.forName("com.demo.dynamic_proxy.jdk.Calculator").getMethod("div", Integer.TYPE, Integer.TYPE); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }}
五、总结
JDK动态代理是通过实现接口的方式完成对目标对象功能的增强。
JDK动态代理底层创建代理类,实现目标对象所实现的接口,并生成代理类的字节码信息,通过类加载器进行加载,最后创建代理类对象,通过代理对象调用InvocationHandler接口实现类的invoke方法完成功能的增强。
通过调用Proxy类的newProxyInstance方法创建代理对象,代理对象调用InvocationHandler接口中的invoke实现对目标对象的增强功能。
来源地址:https://blog.csdn.net/qingfengmax/article/details/129172456
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341