Java - 在程序外部改变代码行为
标题起得有点大了,其实主要java.lang.instrument下的内容。
之前写的<JRebel - 使用JRebel提高开发效率>中用Maven启动工程时加上VM参数时有一个参数是"-javaagent:D:\jrebel_5.6.0\jrebel.jar"。
javaagent是什么? java -help后看到如下信息,相当于什么都没说...:
要说-javaagent参数就不得不提到java代理。
Java代理不是应用程序中的一部分,Java代理用instrumentation重新定义运行中的服务。
我们可以在不修改程序代码的前提下通过Instrumentation API改变运行中的java程序。
而我们用到的下面这一段命令则用于使代理开始工作:
-javaagent:jarpath[=options]
参数值是个jar路径,先让我们试着写一个代理,再将其导出为jar.
鉴于没有什么实际目的,那就以java agent的使用为重点,尽量写得简单一些。
所谓agent的代码实现就是一个带premain()方法的类,但这个方法并不是根据某个抽象,而是硬生生地定义到类里。
package pac.testcase.basic.agent; import java.lang.instrument.Instrumentation; public class TestAgent { public static void premain(String agentArgs, Instrumentation inst){ System.out.println("Alvez Agent:::"); } }
将TestAgent导出为jar,假设我的导出路径是"D:/myAgent.jar"。
随便写一个main方法,加上参数"-javaagent:D:/myAgent.jar"并运行:
package pac.testcase.basic.agent; public class TestAgentMain { public static void main(String[] args) throws InterruptedException { System.out.println("code in runtime::"); } }
结果提示:
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中在加一条属性,比如本例中改为如下:
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,如下:
package pac.testcase.basic.agent; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; public class TestTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if(className.equals("pac/testcase/basic/agent/TestAgentMain")){ classfileBuffer = new String(classfileBuffer).replace("code","Alvez").getBytes(); System.out.println(new String(classfileBuffer)); } return new byte[0]; } }
不要忘记将实现的ClassFileTransformer添加到instrumentation中:
public static void premain(String agentArgs, Instrumentation inst){ inst.addTransformer(new TestTransformer()); System.out.println("Alvez Agent:::"); }
运行输出:
本文出自 “Alvez. - 99.9% 0B/s” 博客,请务必保留此出处http://alvez.blog.51cto.com/7711135/1544876
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。