当前位置: 代码迷 >> 综合 >> jdk的动态代理;cglib
  详细解决方案

jdk的动态代理;cglib

热度:19   发布时间:2023-12-06 23:47:17.0

jdk的动态代理:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Random;/*** 使用代理模式,使用各种代理的组合*/
public class Tank implements Moveable {@Overridepublic void move() {System.out.println("Tank moveing clacalacala....");try{Thread.sleep(new Random().nextInt(3000));}catch (InterruptedException e){e.printStackTrace();}}public static void main(String[] args) {Tank tank = new Tank();System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");Moveable m = (Moveable)Proxy.newProxyInstance(Tank.class.getClassLoader(),new Class[]{Moveable.class},new LogHandler(tank));m.move();}
}class LogHandler implements InvocationHandler{Tank tank;public LogHandler(Tank tank) {this.tank = tank;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("method "+method.getName()+" start...");Object o = method.invoke(tank, args);System.out.println("method "+method.getName()+" end...");return o;}
}interface Moveable {void move();
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;final class $Proxy0 extends Proxy implements Moveable {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.qqlindy.proxy.v5.Moveable").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());}}
}

jdk的动态代理执行过程:
在这里插入图片描述
client 相当于main 调用Proxy的Proxy.newProxyInstance生成 P r o x y 0 这 个 类 , 生 成 了 这 个 对 象 , 往 里 面 传 了 个 参 数 I n v o c a t i o n H a n d l e r 。 接 下 来 调 用 Proxy0这个类,生成了这个对象,往里面传了个参数InvocationHandler。接下来调用 Proxy0InvocationHandlerProxy0的这个move方法。move方法调用InvocationHandler的invoke方法。这里面执行before,然后调用Tank的move方法,再执行after.

通过ASM生成动态代理类。
在这里插入图片描述

public class Proxy implements java.io.Serializable {/** Generate the specified proxy class.*/byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {private byte[] generateClassFile() {this.addProxyMethod(hashCodeMethod, Object.class);this.addProxyMethod(equalsMethod, Object.class);this.addProxyMethod(toStringMethod, Object.class);Class[] var1 = this.interfaces;int var2 = var1.length;加入move方法:
Iterator var11 = this.proxyMethods.values().iterator();List var12;while(var11.hasNext()) {var12 = (List)var11.next();checkReturnTypes(var12);}Iterator var15;try {this.methods.add(this.generateConstructor());var11 = this.proxyMethods.values().iterator();while(var11.hasNext()) {var12 = (List)var11.next();var15 = var12.iterator();while(var15.hasNext()) {ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));this.methods.add(var16.generateMethod());   //调用add方法}}this.methods.add(this.generateStaticInitializer());} catch (IOException var10) {throw new InternalError("unexpected I/O Exception", var10);}

上面add方法本质上调用:所以jdk的动态代理是由ASM操作的。 直接操作二进制操作码。
在这里插入图片描述
所以了用了ASM之后,Java才可以被称为动态语言。所谓的动态语言就是指在运行的时候,动态改变类的属性和方法。动态添加,动态删除。 (而反射只能读出来,不能改。)。 JDK反射生成代理必须面向接口,这是由JDK Proxy的内部实现决定的 ,只有实现了接口, 代理类内部才知道内部需要写哪些方法,不然不知道。

还有一种方式生成动态代理: Instrument , 调琴。 Java自带的方式: 钩子函数, 是一个拦截器, 每一个Class load到内存中,会拦住这个Class. 在拦截的时候可以进行定制。

cglib: code generate 代码生成。cglib底层用的也是ASM,Spring用的也是ASM

下面例子如果Tank类是Final,cglib是生产不了代理的。 asm可以。直接操作二进制码。 cglib底层用的也是asm. Spring用的也是asm.

 <dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.2.12</version></dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;
import java.util.Random;public class Main {public static void main(String[] args) {Enhancer enhancer = new Enhancer();     //Enhancer增强器enhancer.setSuperclass(Tank.class);enhancer.setCallback(new TimeMethodInterceptor());     //TimeMethodInterceptor相当于JDM动态代理的InvocationHandlerTank tank = (Tank) enhancer.create();	//动态代理的对象生成tank.move();}
}class TimeMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println(o.getClass().getSuperclass().getName());System.out.println("before");Object result = null;result = methodProxy.invokeSuper(o, objects);		//调用原来的方法 System.out.println("after");return result;}
}class Tank{     //如果Tank类是Final,cglib是生产不了代理的。 asm可以。直接操作二进制码。 cglib底层用的也是asm. Spring用的也是asm.public void move(){System.out.println("Tank is move....");try{Thread.sleep(new Random().nextInt(3000));}catch (InterruptedException e){e.printStackTrace();}}
}