新东阳建设集团网站,如何登录网站空间,wordpress 微博模板,生物科技公司网站模板文章目录一、动手实现一个动态代理框架1、初识javassist2、使用javassist实现一个动态代理框架二、JDK动态代理1、编码实现2、基本原理#xff08;1#xff09;getProxyClass0方法#xff08;2#xff09;总结写在后面一、动手实现一个动态代理框架
1、初识javassist
Jav…
文章目录一、动手实现一个动态代理框架1、初识javassist2、使用javassist实现一个动态代理框架二、JDK动态代理1、编码实现2、基本原理1getProxyClass0方法2总结写在后面一、动手实现一个动态代理框架
1、初识javassist
Java操纵字节码最底层一般是使用ASM进行操作的但是ASM上手难度很大我们可以使用javassist对字节码进行操作相对来说简单一些。
下面这个实例就是我们使用javassist对接口动态生成一个实现类 import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;public class JavassistDemo {public static void main(String[] args) throws Exception {TestService proxy createProxy();proxy.sayHello(zhangsan); // hello:zhangsan}/*** 生成一个TestService的实现类*/public static TestService createProxy() throws Exception {// javassist 底层是ASMASM底层是编辑JVM指令码ClassPool classPool new ClassPool();// 添加classLoaderclassPool.appendSystemPath();// 1.创建一个类CtClass class1 classPool.makeClass(TestServiceImpl);class1.addInterface(classPool.get(TestService.class.getName()));// 2.创建一个方法CtMethod satHelloMethod CtNewMethod.make(CtClass.voidType, // void返回值sayHello, // 方法名new CtClass[]{classPool.get(String.class.getName())}, // 方法参数new CtClass[0], // 异常类型{System.out.println(\hello:\$1);}, // 方法体内容$1表示第一个参数class1 // 指定类);class1.addMethod(satHelloMethod);// 3.实例化这个对象Class aClass classPool.toClass(class1);// 强制转换return (TestService) aClass.newInstance();}public interface TestService {void sayHello(String name);}
}
2、使用javassist实现一个动态代理框架 import javassist.*;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Collectors;public class Javassist3Demo {public static void main(String[] args) throws Exception {TestService proxy createProxy(TestService.class, new InvocationHandler() {Overridepublic Object invoke(String methodName, Object[] args) {// 根据方法判断逻辑if(methodName.equals(sayHello3)) {System.out.println(hello args[0]);return aa;} else {System.out.println(hello2 args[0]);return aa2;}}});proxy.sayHello(zhangsan); // hellozhangsanproxy.sayHello2(zz, 1);System.out.println(proxy.sayHello3(qq));}static int count 0;public static T T createProxy(ClassT classInterface, InvocationHandler handler) throws Exception {ClassPool classPool new ClassPool();classPool.appendSystemPath();// 1.创建一个类CtClass impl classPool.makeClass($Proxy count );impl.addInterface(classPool.get(classInterface.getName()));// 2.impl类中 添加属性handlerCtField fie CtField.make(public com.mydemo.Javassast3Demo.InvocationHandler handlernull;, impl);impl.addField(fie);// 有返回值类型和无返回值类型的源码String src return ($r)this.handler.invoke(\%s\, $args);; // $args获取所有参数String voidSrc this.handler.invoke(\%s\,$args);;for (Method method : classInterface.getMethods()) {CtClass returnType classPool.get(method.getReturnType().getName());String name method.getName();CtClass[] parameters toCtClass(classPool, method.getParameterTypes());CtClass[] errors toCtClass(classPool, method.getExceptionTypes());// 2.创建一个方法CtMethod newMethod CtNewMethod.make(returnType, // 返回值name, // 方法名parameters, // 方法参数errors, // 异常类型method.getReturnType().equals(Void.class) ? String.format(voidSrc, method.getName()) : String.format(src, method.getName()), // 方法体内容impl // 指定类);impl.addMethod(newMethod);}// 生成字节码辅助学习用//byte[] bytes impl.toBytecode();//Files.write(Paths.get(System.getProperty(user.dir) /target/ impl.getName() .class), bytes);// 3.实例化这个对象Class aClass classPool.toClass(impl);T t (T) aClass.newInstance();aClass.getField(handler).set(t, handler); // 初始化赋值// 强制转换return t;}private static CtClass[] toCtClass(ClassPool pool, Class[] classes) {return Arrays.stream(classes).map(c - {try {return pool.get(c.getName());} catch (NotFoundException e) {throw new RuntimeException(e);}}).collect(Collectors.toList()).toArray(new CtClass[0]);}public interface InvocationHandler {Object invoke(String methodName, Object args[]);}public class InvocationHandlerImpl implements InvocationHandler {Overridepublic Object invoke(String methodName, Object[] args) {System.out.println(hello);return null;}}public interface TestService {void sayHello(String name);void sayHello2(String name, Integer id);String sayHello3(String name);}
}
这样我们只要实现InvocationHandler 接口就可以自定义代理类的核心逻辑了我们对生成的代理类进行反编译
public class $Proxy0 implements TestService {public InvocationHandler handler null;public String sayHello3(String var1) {return (String)this.handler.invoke(sayHello3, new Object[]{var1});}public void sayHello(String var1) {this.handler.invoke(sayHello, new Object[]{var1});}public void sayHello2(String var1, Integer var2) {this.handler.invoke(sayHello2, new Object[]{var1, var2});}public $Proxy0() {}
}
实现了我们的接口并且重写了接口的所有方法最终执行的是InvocationHandler的invoke方法。
这也正是JDK动态代理的基本思想。
二、JDK动态代理
1、编码实现
public interface EchoService {String echo(String message) throws NullPointerException;
}public class DefaultEchoService implements EchoService {Overridepublic String echo(String message) {return [ECHO] message;}
}import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** JDK动态代理实例*/
public class JDKDynamicProxyDemo {public static void main(String[] args) {ClassLoader classLoader Thread.currentThread().getContextClassLoader();// 真实的对象DefaultEchoService realObj new DefaultEchoService();// 代理的对象Object proxy Proxy.newProxyInstance(classLoader, new Class[]{EchoService.class}, new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(动态前置);Object obj null;if (EchoService.class.isAssignableFrom(method.getDeclaringClass())) {// 执行真实方法obj method.invoke(realObj, args);}System.out.println(动态后置);return obj;}});EchoService echoService (EchoService) proxy;System.out.println(echoService.echo(Hello,World));}
}我们发现使用JDK动态代理基本逻辑和我们上面使用javassist手写的工具类差不多只不过JDK动态代理底层是使用更复杂的方式实现的我们这里取巧使用javassist实现。
注意这里需要DefaultEchoService 实现EchoService接口代理的其实是EchoService接口而不是DefaultEchoService 类。
动态代理对原代码没有侵入性通常可以动态加载。
2、基本原理
为什么 Proxy.newProxyInstance 会生成新的字节码
ClassLoader classLoader Thread.currentThread().getContextClassLoader();Object proxy Proxy.newProxyInstance(classLoader, new Class[]{EchoService.class}, new InvocationHandler() {Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;}
});System.out.println(proxy.getClass());// com.sun.proxy.$Proxy0Object proxy2 Proxy.newProxyInstance(classLoader, new Class[]{Comparable.class}, (proxy1, method, args1) - {return null;});System.out.println(proxy2.getClass());// com.sun.proxy.$Proxy1上面代码我们会发现Java动态代理每生成一个代理它的class总是com.sun.proxy包下的$Proxy*从0开始累加它是如何实现的呢
我们来分析一下Proxy的newProxyInstance方法
// java.lang.reflect.Proxy#newProxyInstance
CallerSensitive
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);}/** Look up or generate the designated proxy class.*/// 先从缓存获取见 1Class? cl getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {if (sm ! null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}// 获取代理对象的构造方法带着InvocationHandler参数的构造方法final Constructor? cons cl.getConstructor(constructorParams);final InvocationHandler ih h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedActionVoid() {public Void run() {cons.setAccessible(true);return null;}});}// 返回Proxy对象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);}
}1getProxyClass0方法
在getProxyClass0方法中从proxyClassCache缓存中获取了这个代理类
// java.lang.reflect.Proxy#getProxyClass0
private static Class? getProxyClass0(ClassLoader loader,Class?... interfaces) {if (interfaces.length 65535) {throw new IllegalArgumentException(interface limit exceeded);}// If the proxy class defined by the given loader implementing// the given interfaces exists, this will simply return the cached copy;// otherwise, it will create the proxy class via the ProxyClassFactoryreturn proxyClassCache.get(loader, interfaces);
}而proxyClassCache在初始化时自动创建了KeyFactory和ProxyClassFactory
private static final WeakCacheClassLoader, Class?[], Class?proxyClassCache new WeakCache(new KeyFactory(), new ProxyClassFactory());ProxyClassFactory的核心方法apply隐藏着代理接口的创建逻辑
// java.lang.reflect.Proxy.ProxyClassFactory
private static final class ProxyClassFactoryimplements BiFunctionClassLoader, Class?[], Class?
{// prefix for all proxy class namesprivate static final String proxyClassNamePrefix $Proxy;// next number to use for generation of unique proxy class namesprivate static final AtomicLong nextUniqueNumber new AtomicLong();Overridepublic Class? apply(ClassLoader loader, Class?[] interfaces) {MapClass?, Boolean interfaceSet new IdentityHashMap(interfaces.length);for (Class? intf : interfaces) { // 遍历我们传入的接口数组/** Verify that the class loader resolves the name of this* interface to the same Class object.*/Class? interfaceClass null;try {// 通过classLoader加载我们的接口interfaceClass Class.forName(intf.getName(), false, loader);} catch (ClassNotFoundException e) {}if (interfaceClass ! intf) {throw new IllegalArgumentException(intf is not visible from class loader);}/** Verify that the Class object actually represents an* interface.*/if (!interfaceClass.isInterface()) {// 只能代理接口非接口直接抛异常throw new IllegalArgumentException(interfaceClass.getName() is not an interface);}/** Verify that this interface is not a duplicate.*/if (interfaceSet.put(interfaceClass, Boolean.TRUE) ! null) {throw new IllegalArgumentException(repeated interface: interfaceClass.getName());}}String proxyPkg null; // package to define proxy class inint accessFlags Modifier.PUBLIC | Modifier.FINAL;/** Record the package of a non-public proxy interface so that the* proxy class will be defined in the same package. Verify that* all non-public proxy interfaces are in the same package.*/for (Class? intf : interfaces) {int flags intf.getModifiers();if (!Modifier.isPublic(flags)) {accessFlags Modifier.FINAL;String name intf.getName();int n name.lastIndexOf(.);String pkg ((n -1) ? : name.substring(0, n 1));if (proxyPkg null) {proxyPkg pkg;} else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException(non-public interfaces from different packages);}}}if (proxyPkg null) { // 包名就是com.sun.proxy// if no non-public proxy interfaces, use com.sun.proxy packageproxyPkg ReflectUtil.PROXY_PACKAGE .;}/** Choose a name for the proxy class to generate.*/long num nextUniqueNumber.getAndIncrement();String proxyName proxyPkg proxyClassNamePrefix num; // 依次递增/** Generate the specified proxy class.*/// 代理类生成器返回字节数组就是字节码byte[] proxyClassFile ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {// classLoader加载类是一个native方法返回一个Class对象return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {/** A ClassFormatError here means that (barring bugs in the* proxy class generation code) there was some other* invalid aspect of the arguments supplied to the proxy* class creation (such as virtual machine limitations* exceeded).*/throw new IllegalArgumentException(e.toString());}}
}2总结
vm options参数设置-Dsun.misc.ProxyGenerator.saveGeneratedFilestrue就可以把生成的代理类的源码保存在com.sun.proxy目录下面或者用arthas来查看运行中的类信息。
JDK动态代理生成的代理类我们通过反编译发现其实是这个样子的
package com.sun.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import com.demo.EchoService;public final class $Proxy0
extends Proxy
implements EchoService {private static Method m1;private static Method m3;private static Method m2;private static Method m0;public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}static {try {m1 Class.forName(java.lang.Object).getMethod(equals, Class.forName(java.lang.Object));m3 Class.forName(com.demo.EchoService).getMethod(echo, Class.forName(java.lang.String));m2 Class.forName(java.lang.Object).getMethod(toString, new Class[0]);m0 Class.forName(java.lang.Object).getMethod(hashCode, new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String echo(String string) throws NullPointerException {try {return (String)this.h.invoke(this, m3, new Object[]{string});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}
}InvocationHandler就是Proxy.newProxyInstance传入的最后一个参数。
当调用代理对象的方法时会执行InvocationHandler的invoke方法。
注意JDK生成的代理类的包名不总是com.sun.proxy,只有当接口为Public时是这样的当接口为非public时生成的代理类与接口所在包名相同。
写在后面
如果本文对你有帮助请点赞收藏关注一下吧 ~ 文章转载自: http://www.morning.wrdpj.cn.gov.cn.wrdpj.cn http://www.morning.splcc.cn.gov.cn.splcc.cn http://www.morning.haolipu.com.gov.cn.haolipu.com http://www.morning.qbjrl.cn.gov.cn.qbjrl.cn http://www.morning.tlrxt.cn.gov.cn.tlrxt.cn http://www.morning.lbhck.cn.gov.cn.lbhck.cn http://www.morning.tsnmt.cn.gov.cn.tsnmt.cn http://www.morning.jrgxx.cn.gov.cn.jrgxx.cn http://www.morning.hgscb.cn.gov.cn.hgscb.cn http://www.morning.cfjyr.cn.gov.cn.cfjyr.cn http://www.morning.hpkgm.cn.gov.cn.hpkgm.cn http://www.morning.kyjyt.cn.gov.cn.kyjyt.cn http://www.morning.wrtbx.cn.gov.cn.wrtbx.cn http://www.morning.mxcgf.cn.gov.cn.mxcgf.cn http://www.morning.lfpdc.cn.gov.cn.lfpdc.cn http://www.morning.kongpie.com.gov.cn.kongpie.com http://www.morning.bgkk.cn.gov.cn.bgkk.cn http://www.morning.yesidu.com.gov.cn.yesidu.com http://www.morning.mqwnz.cn.gov.cn.mqwnz.cn http://www.morning.bchfp.cn.gov.cn.bchfp.cn http://www.morning.sdecsd.cn.gov.cn.sdecsd.cn http://www.morning.fysdt.cn.gov.cn.fysdt.cn http://www.morning.tdxnz.cn.gov.cn.tdxnz.cn http://www.morning.dpmkn.cn.gov.cn.dpmkn.cn http://www.morning.pxspq.cn.gov.cn.pxspq.cn http://www.morning.gqfks.cn.gov.cn.gqfks.cn http://www.morning.qgbfx.cn.gov.cn.qgbfx.cn http://www.morning.lsfzq.cn.gov.cn.lsfzq.cn http://www.morning.glbnc.cn.gov.cn.glbnc.cn http://www.morning.fwnyz.cn.gov.cn.fwnyz.cn http://www.morning.cspwj.cn.gov.cn.cspwj.cn http://www.morning.bauul.com.gov.cn.bauul.com http://www.morning.lmtbl.cn.gov.cn.lmtbl.cn http://www.morning.yfwygl.cn.gov.cn.yfwygl.cn http://www.morning.hpnhl.cn.gov.cn.hpnhl.cn http://www.morning.jxzfg.cn.gov.cn.jxzfg.cn http://www.morning.gqtxz.cn.gov.cn.gqtxz.cn http://www.morning.yntsr.cn.gov.cn.yntsr.cn http://www.morning.sbdqy.cn.gov.cn.sbdqy.cn http://www.morning.lxctl.cn.gov.cn.lxctl.cn http://www.morning.lzrpy.cn.gov.cn.lzrpy.cn http://www.morning.lxbml.cn.gov.cn.lxbml.cn http://www.morning.klrpm.cn.gov.cn.klrpm.cn http://www.morning.xhkgl.cn.gov.cn.xhkgl.cn http://www.morning.qsyyp.cn.gov.cn.qsyyp.cn http://www.morning.vtbtje.cn.gov.cn.vtbtje.cn http://www.morning.fdlyh.cn.gov.cn.fdlyh.cn http://www.morning.fpqsd.cn.gov.cn.fpqsd.cn http://www.morning.gwkwt.cn.gov.cn.gwkwt.cn http://www.morning.ycpnm.cn.gov.cn.ycpnm.cn http://www.morning.fyxr.cn.gov.cn.fyxr.cn http://www.morning.qhjkz.cn.gov.cn.qhjkz.cn http://www.morning.wnrcj.cn.gov.cn.wnrcj.cn http://www.morning.rkypb.cn.gov.cn.rkypb.cn http://www.morning.lgsfb.cn.gov.cn.lgsfb.cn http://www.morning.kjnfs.cn.gov.cn.kjnfs.cn http://www.morning.sqgsx.cn.gov.cn.sqgsx.cn http://www.morning.jgcyn.cn.gov.cn.jgcyn.cn http://www.morning.lmmyl.cn.gov.cn.lmmyl.cn http://www.morning.rlfr.cn.gov.cn.rlfr.cn http://www.morning.rongxiaoman.com.gov.cn.rongxiaoman.com http://www.morning.tfpbm.cn.gov.cn.tfpbm.cn http://www.morning.pjxlg.cn.gov.cn.pjxlg.cn http://www.morning.btrfm.cn.gov.cn.btrfm.cn http://www.morning.jgnst.cn.gov.cn.jgnst.cn http://www.morning.tfrlj.cn.gov.cn.tfrlj.cn http://www.morning.stbhn.cn.gov.cn.stbhn.cn http://www.morning.wgrm.cn.gov.cn.wgrm.cn http://www.morning.ywtbk.cn.gov.cn.ywtbk.cn http://www.morning.zfgh.cn.gov.cn.zfgh.cn http://www.morning.gqdsm.cn.gov.cn.gqdsm.cn http://www.morning.tymnr.cn.gov.cn.tymnr.cn http://www.morning.xtxp.cn.gov.cn.xtxp.cn http://www.morning.tslxr.cn.gov.cn.tslxr.cn http://www.morning.kcsx.cn.gov.cn.kcsx.cn http://www.morning.pcgmw.cn.gov.cn.pcgmw.cn http://www.morning.prddj.cn.gov.cn.prddj.cn http://www.morning.kqrql.cn.gov.cn.kqrql.cn http://www.morning.ktfbl.cn.gov.cn.ktfbl.cn http://www.morning.mfbcs.cn.gov.cn.mfbcs.cn