重要的方法笔者已经写上注释了 。本文会用到的方法主要是addTransformer() 。它可以用来添加Class转换器 。JVM在加载Class前 。会先经过这些转换器进行加工 。
3.2 ClassFileTransformerClass文件转换器 。JVM加载某个Class前 。会先经过它转换 。我们可以在这里去修改字节码以达到功能增强的目的 。它只有一个方法transform():
publicinterfaceClassFileTransformer{/***转换Class*@paramloader类加载器*@paramclassName类名*@paramclassBeingRedefined原始Class*@paramProtectionDomain*@paramclassfileBufferClass文件字节数组*/byte[]transform(ClassLoaderloader,StringclassName,Class<?>classBeingRedefined,ProtectionDomainprotectionDomain,byte[]classfileBuffer)throwsIllegalClassFormatException;}
本文主要用到的就是classfileBuffer 。有了Class的字节数组 。只要把它导出到磁盘 。通过IDEA反编译就能看到源码了 。
4. 实战【需求】
支持将任意Java对象的Class文件导出到磁盘 。通过反编译查看源码 。包括动态生成的类 。
【实现】
1、编写InstrumentationHolder 。持有Instrumentation实例 。后续操作全靠它 。
publicclassInstrumentationHolder{privatestaticInstrumentationINSTANCE;publicstaticvoidinit(Instrumentationins){INSTANCE=ins;}publicstaticInstrumentationget(){if(INSTANCE==null){thrownewRuntimeException("检查-javaagent配置");}returnINSTANCE;}}
2、编写MyAgentClass 。保存Instrumentation实例 。
publicclassMyAgentClass{publicstaticvoidpremain(StringagentArgs,Instrumentationinst){System.err.println("mainbefore...");InstrumentationHolder.init(inst);}}
3、编写ClassDumpTransformer 。获取Class文件字节数组 。导出到磁盘 。
publicclassClassDumpTransformerimplementsClassFileTransformer{privatefinalFilefile;privatefinalSet<Class<?>>classes=newHashSet<>();publicClassDumpTransformer(Stringpath,Class<?>...classes){this.file=newFile(path);this.classes.addAll(Arrays.asList(classes));}@Overridepublicbyte[]transform(ClassLoaderloader,StringclassName,Class<?>classBeingRedefined,ProtectionDomainprotectionDomain,byte[]classfileBuffer)throwsIllegalClassFormatException{if(classes.contains(classBeingRedefined)){FileUtil.writeBytes(classfileBuffer,file);}returnnull;}}
4、编写ClassUtil工具类 。支持导出Class文件 。
publicclassClassUtil{publicstaticvoidclassDump(Class<?>c,Stringpath){ClassDumpTransformertransformer=newClassDumpTransformer(path,c);Instrumentationinst=InstrumentationHolder.get();inst.addTransformer(transformer,true);try{inst.retransformClasses(c);}catch(UnmodifiableClassExceptione){e.printStackTrace();}finally{inst.removeTransformer(transformer);}}}
5、编写MANIFEST.MF文件 。构建Jar包 。
Manifest-Version:1.0Can-Redefine-Classes:trueCan-Retransform-Classes:truePremain-Class:top.javap.agent.MyAgentClass
6、编写测试类 。利用JDK动态代理生成代理类 。然后将代理类的Class文件导出 。
publicclassAgentDemo{publicstaticvoidmain(String[]args)throwsException{Objectinstance=Proxy.newProxyInstance(A.class.getClassLoader(),newClass[]{A.class},newInvocationHandler(){@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{returnnull;}});ClassUtil.classDump(instance.getClass(),"/target/X.class");}publicstaticinterfaceA{voida();}}
7、设置-javaagent参数并启动程序 。
java-javaagent:agent.jarAgentDemo
此时 。target目录下就会生成X.class文件 。通过IDEA打开即可看到JDK生成的代理类源码 。
5. 总结JavaAgent十分强大 。通过它可以在JVM加载Class文件前修改字节码 。甚至修改JVM已经加载的Class 。基于此 。我们可以「零侵入」的对应用程序做增强 。服务实现热部署等等 。
【源代码在哪里找 在哪能找到Java源码】本文通过一个小示例 。编写ClassFileTransformer实现类导出对象的Class文件 。反编译查看其源码 。这对于ASM操作字节码、JDK动态代理等动态生成类的场景下 。而我们又想看对象的具体实现时 。提供了帮助 。
推荐阅读
- 有哪些令你惊艳的一句话或诗句?
- 现在有没有用移动宽带的网速怎么样?
- 雪中悍刀行武功境界划分表 雪中悍刀行各个境界及寿命
- 菲律宾有多少人用微信?
- 有哪些惊艳到你的名家名句?
- 拼多多违禁词大全 这些拼多多违禁词可要千万注意了
- 菲律宾你了解多少?
- 韩愈写的名句有哪些?
- 抖音测试短剧付费开启,看抖音竟然需要钱