Java – 在程序外部改变代码行为

标题起得有点大了,其实主要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];}}

人若软弱就是自己最大的敌人

Java – 在程序外部改变代码行为

相关文章:

你感兴趣的文章:

标签云: