Java 动态代理
概述
什么是动态代理
- 使用 JDK 的反射机制,创建对象的能力, 创建的是代理类的对象,不用自己创建类文件,不用写 Java 文件。
- 动态:在程序执行时,调用 JDK 提供的方法才能创建代理类的对象。
- JDK 动态代理,必须有接口,目标类必须实现接口,没有接口时,需要使用 cglib 动态代理。
动态代理能做什么
- 可以在不改变原来目标方法功能的前提下,可以在代理中增强自己的功能代码。
动态代理性能怎么样
- 每 100 次动态代理调用,相比原生调用,慢 1 毫秒,性能损耗基本可以忽略不计(JDK8)。
原理
实质上是通过传入的 Class<?>[] interfaces 参数,JDK 会实现一个代理类对象,该对象包装了所有接口函数,函数内不都是将方法调用,转调到了 InvocationHandler 类的 invoke。JDK 会生成代理类的字节码 byte[],并自动通过给定的 Classloader 加载进虚拟机,从而避免手动写代理类的麻烦,函数增强部分,开发人员可以写在 InvocationHandler.invoke 里。
简单示例
public interface Shape { String getName(); }
public class Square implements Shape { @Override public String getName() { return "方形"; } } public class Cycle implements Shape { @Override public String getName() { return "圆形"; } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class DemoInvocationHandler implements InvocationHandler { private Object target; public DemoInvocationHandler(Object target) { this.target = target; } /** * 获取动态代理对象 * * @param target * @return */ public static Object dynamicProxy(final Object target) { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new DemoInvocationHandler(target) ); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("==> 方法执行前"); Object result = method.invoke(this.target); System.out.println(" 执行结果: " + result); System.out.println("<== 方法执行后"); return result; } }
/** * JDK 动态代理,调用100次,比原生方法调用慢了不到1毫秒 * System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); */ public class JDKDynamicProxyTest { public static void main(String[] args) { Shape shape = (Shape) DemoInvocationHandler.dynamicProxy(new Cycle()); shape.getName(); } }
执行结果:
==> 方法执行前
执行结果: 圆形
<== 方法执行后
源码:
生成出来的字节码文件:我们发现代理类其实就是一层很薄的封装,所有方法调用都委托给了 InvocationHandler 实现类的 invoke 方法。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements Shape { private static Method m1; private static Method m3; private static Method m2; private static Method m0; 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}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final String getName() throws { try { return (String)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); } 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")); m3 = Class.forName("com.asiainfo.crm.xxx.service.Shape").getMethod("getName"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
我们完全可以直接调用 ProxyGenerator.generateProxyClass() 讲动态代理类生成出来:
public class JDKDynamicProxyTest { public static void main(String[] args) throws IOException { byte[] data = ProxyGenerator.generateProxyClass("$Proxy9", new Cycle().getClass().getInterfaces(), Modifier.PUBLIC | Modifier.FINAL); FileCopyUtils.copy(data, new File("C:\\Users\\21550\\Desktop\\$Proxy9.class")); } }