Java中的动态代理使用
动态代理
动态代理的时候,定义一个接口,需要代理人和被代理类实现这个接口,这样不够灵活,代理类能够代理的类只有实现这个接口的类。
非常不灵活,假如被代理人的类没有实现这个接口,那么就需重新写一个代理类。对于日志、事务这些操作是不区分业务的,即不需要规定都实现某接口。因此,出现了动态代理
java种的动态代理生成的方式大概有三种JDK动态代理、instrument动态代理、cglib动态代理。其中,前两种是JDK自带的,cglib是需要第三方依赖使用的。
- JDK动态代理:在程序运行的过程种动态的生成代理类,这个代理类实现被代理类的接口,因此使用时候被代理类必须实现接口;
- instrument动态代理:是通过Class字节文件在加载至内存的过程种添加拦截器,动态的修改字节文件实现的;
- cglib:在程序运行的过程种动态生成代理类,这个代理类是通过继承被代理类实现的,因此不能代理被final修饰的类
- JDK动态代理和cglib动态代理的底层都是ASM;
JDK动态代理
先看这样一个程序
/** 接口*/ public interface Mobile { void move(); } /** 被代理类*/ public class Car implements Mobile{ @Override public void move() { System.out.println("move..."); } } /** 使用动态代理*/ public class Test { public static void main(String[] args) { Car car = new Car(); System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true); Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long before = System.currentTimeMillis(); System.out.println(method.getName()+"方法被执行前。。。"); Object invoke = method.invoke(car, args); System.out.println(method.getName()+"方法被执行后。。。"); long after = System.currentTimeMillis(); System.out.println("耗时:" + (after - before)); return null; } }); proxyInstance.move(); } }
创建代理类需要调用Proxy.newProxyInstance()方法,其中有三个参数
- 第一个参数:一个类加载器,一般使用被代理类的类加载器
- 第二个参数:被代理类实现的接口
- 第三个参数:一个InvocationHandler接口的实现类
可以把动态代理的创建改成这样:自己写一个类实现InvocationHandler。
public class Test { public static void main(String[] args) { Car car = new Car(); System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true"); Mobile proxyInstance = (Mobile) Proxy.newProxyInstance(Car.class.getClassLoader(), Car.class.getInterfaces(), new MyInvocationHandler(car)); proxyInstance.move(); } } class MyInvocationHandler implements InvocationHandler{ private Car car; public MyInvocationHandler(Car car) { this.car = car; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long before = System.currentTimeMillis(); System.out.println(method.getName()+"方法被执行前。。。"); Object invoke = method.invoke(car, args); System.out.println(method.getName()+"方法被执行后。。。"); long after = System.currentTimeMillis(); System.out.println("耗时:" + (after - before)); return null; } }
要想搞懂JDK动态代理必须查看动态生成代理类,通过设置JDK动态代理生成的类是否保存的一个属性将生成的代理类保存下来,通过在程序启动前加上:
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles",true);
代理类的调用过程
- 生成的代理类
public final class $Proxy0 extends Proxy implements Mobile { 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 void move() 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); } 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.shaoby.basic.proxy.JdkProxy.Mobile").getMethod("move"); 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()); } } }
- 可以看到代理类继承了Proxy,并实现了我们定义的Mobile接口
- 构造方法传入InvocationHandler类型,并赋值给父类Proxy中的h参数,可以猜测这个就在获取代理类时候的第三个参数;
- 代理类中通过反射生成了四个方法,除了Object中的equals、toString、hashCode外另外一个就是我们要代理的方法move;
- 在move方法中调用了 super.h.invoke(this, m3, (Object[])null),即MyInvocationHandler中的invoke方法。
- 因此在调用代理类的invoke方法时,调用的就是MyInvocationHandler中的invoke方法。
Method类
这个类是一个方法的类,说白了就是方法的模板,在这个类中有一个方法invoke,传入两个参数,一个是调用方法的对象,另一个是方法的入参
InvocationHandler中的invoke方法
可以看到在代理类中invoke方法的调用为 super.h.invoke(this, m3, (Object[])null);
MyInvocationHandler中的invoke方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long before = System.currentTimeMillis(); System.out.println(method.getName()+"方法被执行前。。。"); Object invoke = method.invoke(car, args); System.out.println(method.getName()+"方法被执行后。。。"); long after = System.currentTimeMillis(); System.out.println("耗时:" + (after - before)); return null; }
参数:
- 第一个参数:this,即代理类本身
- 第二个参数:m3,即move方法
- 第三个参数:第二个方法的入参
返回值:
这个返回值其实是被代理方法的返回值,如果没有返回值就返回null;
这里最重要的是method.invoke(car, args),就是用car对象调用move方法。
ASM
在上述分析中,动态代理类的构造方法中参数是我们猜测的。其实这里是用ASM实现的。在生成代理类时调用的Proxy.newInstance()传入了InvocationHandler接口的实现类,Proxy.neInstance()会动态的生成代理类,并产生一个代理对象。这里后续再研究。
ASM是Java字节码操控框架,它能够以二进制形式修改已有类,或动态生成类,ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
cglib动态代理
public class Test { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Plane.class); enhancer.setCallback(new MyMethodInterceptor()); Plane o = (Plane)enhancer.create(); o.move(); } } class MyMethodInterceptor implements MethodInterceptor{ @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { long before = System.currentTimeMillis(); System.out.println(method.getName()+"方法被执行前。。。"); Object result = methodProxy.invokeSuper(o, objects); System.out.println(method.getName()+"方法被执行后。。。"); long after = System.currentTimeMillis(); System.out.println("耗时:" + (after - before)); return result; } }
cglib动态代理的原理是:
- 指定代理类的父类
- 生成代理类
- 调用代理类父类的method
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
解决springboot application.yml变灰色的问题
这篇文章主要介绍了解决springboot application.yml变灰色的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2024-07-07mybatis模糊查询之bind标签和concat函数用法详解
大家都知道bind 标签可以使用 OGNL 表达式创建一个变量井将其绑定到上下文中,接下来通过本文给大家介绍了mybatis模糊查询——bind标签和concat函数用法,需要的朋友可以参考下2022-08-08
最新评论