润滑油 东莞网站建设,网站快速备案被退回的几种原因分析,广州网页设计公司公司,中华网提到JDK动态代理#xff0c;相信很多人并不陌生。然而#xff0c;对于动态代理的实现原理#xff0c;以及如何编码实现动态代理功能#xff0c;可能知道的人就比较少了。接下一来#xff0c;我们就一起来看看JDK动态代理的基本原理#xff0c;以及如何通过Javassist进行模…提到JDK动态代理相信很多人并不陌生。然而对于动态代理的实现原理以及如何编码实现动态代理功能可能知道的人就比较少了。接下一来我们就一起来看看JDK动态代理的基本原理以及如何通过Javassist进行模拟实现。 JDK动态代理 示例 以下是一个基于JDK动态代理的hello world示例在很多地方都可以看到类似的版本。 public class DynamicProxyTest {interface IHello {void sayHello();}static class Hello implements IHello {Overridepublic void sayHello() {System.out.println(hello world);}}static class DynamicProxy implements InvocationHandler {Object originalObj;Object bind(Object originalObj) {this.originalObj originalObj;return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this);}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(pre method);Object result method.invoke(originalObj, args);System.out.println(post method);return result;}}public static void main(String[] args) {System.getProperties().put(sun.misc.ProxyGenerator.saveGeneratedFiles, true);IHello hello (IHello) new DynamicProxy().bind(new Hello());hello.sayHello();}
}
复制代码生成代理类源码 通过设置参数 sun.misc.ProxyGenerator.saveGeneratedFiles为true在执行main函数之后我们将得到一份$Proxy0.class文件它就是Hello的代理类。 经过反编译得到$Proxy0的源码省略了无关内容如下 final class $Proxy0 extends Proxy implements DynamicProxyTest.IHello {private static Method m1;private static Method m3;private static Method m2;private static Method m0;public $Proxy0(InvocationHandler paramInvocationHandler) {super(paramInvocationHandler);}public final void sayHello() {try {this.h.invoke(this, m3, null);return;} catch (Error | RuntimeException localError) {throw localError;} catch (Throwable localThrowable) {throw new UndeclaredThrowableException(localThrowable);}}static {try {m1 Class.forName(java.lang.Object).getMethod(equals,new Class[] { Class.forName(java.lang.Object) });m3 Class.forName(DynamicProxyTest$IHello).getMethod(sayHello, new Class[0]);m2 Class.forName(java.lang.Object).getMethod(toString, new Class[0]);m0 Class.forName(java.lang.Object).getMethod(hashCode, new Class[0]);} catch (NoSuchMethodException localNoSuchMethodException) {throw new NoSuchMethodError(localNoSuchMethodException.getMessage());} catch (ClassNotFoundException localClassNotFoundException) {throw new NoClassDefFoundError(localClassNotFoundException.getMessage());}}
}
复制代码主要实现原理 动态生成一个代理类实现IHello接口代理类继承Proxy类提供一个实例变量InvocationHandler h用于保存代理逻辑此外Proxy还提供了相关代理类处理逻辑代理类声明一系列Method类变量用于保存接口相关的反射方法代理类实现相关接口方法核心逻辑是调用InvocationHandler的invoke方法并传入3个参数当前代理类对象、接口反射方法对象、实际方法参数。Javassist实现JDK动态代理 前面简单分析了JDK动态代理的基本原理其中最核心的逻辑在于如何生成动态代理类也就是 java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h)方法的实现。 接下来我们将通过Javassist一步步实现newProxyInstance方法。 1. 定义接口 接口基本与Proxy.newProxyInstance相同。为简单说明我们这里只定义了一个接口类型参数Class?而不是数组。 public static Object newProxyInstance(ClassLoader loader, Class? interfaceClass, InvocationHandler h) {...
}
复制代码2. 创建动态代理类 Javassist可以通过简单的Java API来操作源代码这样就可以在不了解Java字节码相关知识的情况下动态生成类或修改类的行为。 创建名称为NewProxyClass的代理类。 ClassPool pool ClassPool.getDefault();
CtClass proxyCc pool.makeClass(NewProxyClass);
复制代码3. 添加实例变量InvocationHandler 添加类型为InvocationHandler的实例变量h。 CtClass handlerCc pool.get(InvocationHandler.class.getName());
/* 生成代码private InvocationHandler h; */
CtField handlerField new CtField(handlerCc, h, proxyCc);
handlerField.setModifiers(AccessFlag.PRIVATE);
proxyCc.addField(handlerField);
复制代码4. 添加构造函数 创建构造函数参数类型为InvocationHandler。 // 生成构造函数public NewProxyClass(InvocationHandler h) { this.h h; }
CtConstructor ctConstructor new CtConstructor(new CtClass[] { handlerCc }, proxyCc);
ctConstructor.setBody($0.h $1;);
proxyCc.addConstructor(ctConstructor);
复制代码其中$0代表this, $1代表构造函数的第1个参数。 5. 实现IHello接口声明 // 生成接口实现声明public class NewProxyClass implements IHello
CtClass interfaceCc pool.get(interfaceClass.getName());
proxyCc.addInterface(interfaceCc);
复制代码6. 实现IHello相关接口方法 6.1 遍历接口方法 CtMethod[] ctMethods interfaceCc.getDeclaredMethods();
for (int i 0; i ctMethods.length; i) {// 核心逻辑在下方
}
复制代码6.2 代理方法实现 由于代理类调用invoke方法需要传入接口的反射方法对象Method因此我们需要为每个方法添加一个可复用的Method类变量。 6.2.1 反射方法对象声明及初始化 /* 构造方法参数如new Class[] { String.class, Boolean.TYPE, Object.class } */
String classParamsStr new Class[0];
if (ctMethods[i].getParameterTypes().length 0) {for (CtClass clazz : ctMethods[i].getParameterTypes()) {classParamsStr ((classParamsStr new Class[0]) ? clazz.getName() : classParamsStr , clazz.getName()) .class;}classParamsStr new Class[] { classParamsStr };
}
// 字段生成模板
String methodFieldTpl private static java.lang.reflect.Method %sClass.forName(\%s\).getDeclaredMethod(\%s\, %s);;
// 根据模板生成方法及参数构造方法字段生成语句
String methodFieldBody String.format(methodFieldTpl, m i, interfaceClass.getName(), ctMethods[i].getName(), classParamsStr);
// 为代理类添加反射方法字段
CtField methodField CtField.make(methodFieldBody, proxyCc);
proxyCc.addField(methodField);
复制代码通过以上逻辑将生成类似代码如下 private static Method m0 Class.forName(chapter9.javassistproxy3.IHello).getDeclaredMethod(sayHello, new Class[0]);
private static Method m1 Class.forName(chapter9.javassistproxy3.IHello).getDeclaredMethod(sayHello2, new Class[] { Integer.TYPE });
private static Method m2 Class.forName(chapter9.javassistproxy3.IHello).getDeclaredMethod(sayHello3, new Class[] { String.class, Boolean.TYPE, Object.class });
复制代码6.2.2 接口方法体实现 // invoke调用逻辑. 其中$args是实际方法传入的参数数组
String methodBody $0.h.invoke($0, methodFieldName , $args);// 如果方法有返回类型则需要转换为相应类型后返回
if (CtPrimitiveType.voidType ! ctMethods[i].getReturnType()) {// 对8个基本类型进行转型// 例如((Integer)this.h.invoke(this, this.m2, new Object[] { paramString, new Boolean(paramBoolean), paramObject })).intValue();if (ctMethods[i].getReturnType() instanceof CtPrimitiveType) {CtPrimitiveType ctPrimitiveType (CtPrimitiveType) ctMethods[i].getReturnType();methodBody return (( ctPrimitiveType.getWrapperName() ) methodBody ). ctPrimitiveType.getGetMethodName() ();}// 对于非基本类型直接转型即可else {methodBody return ( ctMethods[i].getReturnType().getName() ) methodBody;}
}
methodBody ;;/* 为代理类添加方法 */
CtMethod newMethod new CtMethod(ctMethods[i].getReturnType(), ctMethods[i].getName(),ctMethods[i].getParameterTypes(), proxyCc);
newMethod.setBody(methodBody);
proxyCc.addMethod(newMethod);
复制代码通过以上逻辑将生成类似代码如下 public void sayHello() {this.h.invoke(this, m0, new Object[0]);
}public String sayHello2(int paramInt) {return (String)this.h.invoke(this, m1, new Object[] { new Integer(paramInt) });
}public int sayHello3(String paramString, boolean paramBoolean, Object paramObject) {return ((Integer)this.h.invoke(this, m2, new Object[] { paramString, new Boolean(paramBoolean), paramObject })).intValue();
}
复制代码7. 生成代理类字节码 以下语句将生成代理类字节码D:/tmp/NewProxyClass.class proxyCc.writeFile(D:/tmp); // 该步骤可选
复制代码8. 生成代理对象 最后通过调用第3步创建的构造函数传入InvocationHandler对象生成并返回代理类。 Object proxy proxyCc.toClass().getConstructor(InvocationHandler.class).newInstance(h);
return proxy;
复制代码完整代码 public class ProxyFactory {public static Object newProxyInstance(ClassLoader loader, Class? interfaceClass, InvocationHandler h) throws Throwable {ClassPool pool ClassPool.getDefault();// 1.创建代理类public class NewProxyClassCtClass proxyCc pool.makeClass(NewProxyClass);/* 2.给代理类添加字段private InvocationHandler h; */CtClass handlerCc pool.get(InvocationHandler.class.getName());CtField handlerField new CtField(handlerCc, h, proxyCc); // CtField(CtClass fieldType, String fieldName, CtClass addToThisClass)handlerField.setModifiers(AccessFlag.PRIVATE);proxyCc.addField(handlerField);/* 3.添加构造函数public NewProxyClass(InvocationHandler h) { this.h h; } */CtConstructor ctConstructor new CtConstructor(new CtClass[] { handlerCc }, proxyCc);ctConstructor.setBody($0.h $1;); // $0代表this, $1代表构造函数的第1个参数proxyCc.addConstructor(ctConstructor);/* 4.为代理类添加相应接口方法及实现 */CtClass interfaceCc pool.get(interfaceClass.getName());// 4.1 为代理类添加接口public class NewProxyClass implements IHelloproxyCc.addInterface(interfaceCc);// 4.2 为代理类添加相应方法及实现CtMethod[] ctMethods interfaceCc.getDeclaredMethods();for (int i 0; i ctMethods.length; i) {String methodFieldName m i; // 新的方法名// 4.2.1 为代理类添加反射方法字段// 如private static Method m1 Class.forName(chapter9.javassistproxy3.IHello).getDeclaredMethod(sayHello2, new Class[] { Integer.TYPE });/* 构造反射字段声明及赋值语句 */String classParamsStr new Class[0]; // 方法的多个参数类型以英文逗号分隔if (ctMethods[i].getParameterTypes().length 0) { // getParameterTypes获取方法参数类型列表for (CtClass clazz : ctMethods[i].getParameterTypes()) {classParamsStr ((new Class[0].equals(classParamsStr)) ? clazz.getName() : classParamsStr , clazz.getName()) .class;}classParamsStr new Class[] { classParamsStr };}String methodFieldTpl private static java.lang.reflect.Method %sClass.forName(\%s\).getDeclaredMethod(\%s\, %s);;String methodFieldBody String.format(methodFieldTpl, m i, interfaceClass.getName(), ctMethods[i].getName(), classParamsStr);// 为代理类添加反射方法字段. CtField.make(String sourceCodeText, CtClass addToThisClass)CtField methodField CtField.make(methodFieldBody, proxyCc);proxyCc.addField(methodField);System.out.println(methodFieldBody: methodFieldBody);/* 4.2.2 为方法添加方法体 *//* 构造方法体. this.h.invoke(this, 反射字段名, 方法参数列表); */String methodBody $0.h.invoke($0, methodFieldName , $args);// 如果方法有返回类型则需要转换为相应类型后返回因为invoke方法的返回类型为Objectif (CtPrimitiveType.voidType ! ctMethods[i].getReturnType()) {// 对8个基本类型进行转型// 例如((Integer)this.h.invoke(this, this.m2, new Object[] { paramString, new Boolean(paramBoolean), paramObject })).intValue();if (ctMethods[i].getReturnType() instanceof CtPrimitiveType) {CtPrimitiveType ctPrimitiveType (CtPrimitiveType) ctMethods[i].getReturnType();methodBody return (( ctPrimitiveType.getWrapperName() ) methodBody ). ctPrimitiveType.getGetMethodName() ();} else { // 对于非基本类型直接转型即可methodBody return ( ctMethods[i].getReturnType().getName() ) methodBody;}}methodBody ;;/* 为代理类添加方法. CtMethod(CtClass returnType, String methodName, CtClass[] parameterTypes, CtClass addToThisClass) */CtMethod newMethod new CtMethod(ctMethods[i].getReturnType(), ctMethods[i].getName(),ctMethods[i].getParameterTypes(), proxyCc);newMethod.setBody(methodBody);proxyCc.addMethod(newMethod);System.out.println(Invoke method: methodBody);}proxyCc.writeFile(D:/tmp);// 5.生成代理实例. 将入参InvocationHandler h设置到代理类的InvocationHandler h变量SuppressWarnings(unchecked)Object proxy proxyCc.toClass().getConstructor(InvocationHandler.class).newInstance(h);return proxy;}}public interface IHello {int sayHello3(String a, boolean b, Object c);
}public class Hello implements IHello {Overridepublic int sayHello3(String a, boolean b, Object c) {String abc a b c;System.out.println(a b c abc);return abc.hashCode();}
}public class CustomHandler implements InvocationHandler {private Object obj;public CustomHandler(Object obj) {this.obj obj;}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(pre method);Object result method.invoke(obj, args);System.out.println(post method);return result;}}public class ProxyTest {public static void main(String[] args) throws Throwable {IHello hello new Hello();CustomHandler customHandler new CustomHandler(hello);IHello helloProxy (IHello) ProxyFactory.newProxyInstance(hello.getClass().getClassLoader(), IHello.class, customHandler);System.out.println();System.out.println(afalseObject helloProxy.sayHello3(a, false, new Object()));}}
复制代码执行结果 methodFieldBody: private static java.lang.reflect.Method m0Class.forName(chapter9.javassistproxy3.IHello).getDeclaredMethod(sayHello3, new Class[] {java.lang.String.class,boolean.class,java.lang.Object.class}); Invoke method: return ((java.lang.Integer) $0.h.invoke(args)).intValue(); pre method a b cafalsejava.lang.Object504bae78 post method afalseObject-903110407 参考 Javassist官网www.javassist.org/ 个人公众号 更多文章请关注公众号二进制之路