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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。