标题起得有点大了,其实主要java.lang.instrument下的内容。
之前写的<JRebel – 使用JRebel提高开发效率>中用Maven启动工程时加上VM参数时有一个参数是”-javaagent:D:\jrebel_5.6.0\jrebel.jar”。
javaagent是什么? java -help后看到如下信息,相当于什么都没说…:
要说-javaagent参数就不得不提到java代理。
Java代理不是应用程序中的一部分,instrument支持Java以代理的形式监控或重新定义运行中的服务。
我们可以在不修改程序代码的前提下通过Instrumentation API改变运行中的java程序。
而我们使用的”-javaagent:jarpath[=options] “参数则是一种方式,也就是在应用程序启动前执行代理。
参数值是个jar路径,先让我们试着写一个代理,再将其导出为jar.鉴于没有什么实际目的,那就以java agent的使用为重点,尽量写得简单一些。所谓agent的代码实现就是一个带premain()方法的类,但这个方法并不是根据某个抽象,而是硬生生地定义到类里。
premain方法的声明可以是public static void premain(String args, Instrumentation inst)或 public static void premain(String args)。
参数String args对应”-javaagent:jarpath[=options] “中的options。
参数Instrumentation inst则是运行时传入的Instrumentation实例。
packagepac.testcase.basic.agent;importjava.lang.instrument.Instrumentation;publicclassTestAgent{publicstaticvoidpremain(StringagentArgs,Instrumentationinst){System.out.println(“AlvezAgent:::”);}}
将TestAgent导出为jar,假设我的导出路径是”D:/myAgent.jar”。
随便写一个main方法,加上参数”-javaagent:D:/myAgent.jar”并运行:
packagepac.testcase.basic.agent;publicclassTestAgentMain{publicstaticvoidmain(String[]args)throwsInterruptedException{System.out.println(“codeinruntime::”);}}
结果提示:
Error occurred during initialization of VM
agent library failed to init: instrument
Failed to find Premain-Class manifest attribute in D:/myAgent.jar
这需要在MANIFEST.MF中在加一条属性,手动指定premain class,比如本例中改为如下:
Manifest-Version: 1.0
Premain-Class: pac.testcase.basic.agent.TestAgent
Can-Redefine-Classes: true
接下来就可以运行了,结果输出:
Alvez Agent:::
code in runtime::
到这一步仅仅是在main方法执行之前执行了一下agent的方法,有点像interceptor或者AOP中的前置advice。
并没有想开头所说的”用instrumentation重新定义运行中的服务”。
对,关键还是instrumentation,这次试着使用它来改变main方法的行为。
我们可以为Instrumentation添加一个ClassFileTransformer。
ClassFileTransformer接口没有其他层次,下面是清一色的concrete…
那我也只提供一个concrete,把TestAgentMain.class中的code in runtime变成Alvez in runtime,如下:
packagepac.testcase.basic.agent;importjava.lang.instrument.ClassFileTransformer;importjava.lang.instrument.IllegalClassFormatException;importjava.security.ProtectionDomain;publicclassTestTransformerimplementsClassFileTransformer{@Overridepublicbyte[]transform(ClassLoaderloader,StringclassName,Class<?>classBeingRedefined,ProtectionDomainprotectionDomain,byte[]classfileBuffer)throwsIllegalClassFormatException{if(className.equals(“pac/testcase/basic/agent/TestAgentMain”)){classfileBuffer=newString(classfileBuffer).replace(“code”,”Alvez”).getBytes();System.out.println(newString(classfileBuffer));}returnnewbyte[0];}}
人若软弱就是自己最大的敌人