构建自己的监测器【6】-agentmain方式

在前面的一些例子中,已经用到了jdk5中premain和instrumentation的一些基本用法,给了我们很多惊喜,相当的给力。不过也有一些不方便的地方,由于其必须在命令行指定代理jar,并且代理类必须在main方法前启动。因此,要求开发者在应用前就必须确认代理的处理逻辑和参数内容等等,在有些场合下,这是比较困难的。比如正常的生产环境下,一般不会开启代理功能,但是在发生问题时,我们不希望停止应用就能够动态的去修改一些类的行为,以帮助排查问题,这在应用启动前是无法确定的。 为解决运行时启动代理类的问题,Java SE6开始,提供了在应用程序的VM启动后在动态添加代理的方式,即agentmain方式。 与Permain类似,agent方式同样需要提供一个agent jar,并且这个jar需要满足:

  1. 在manifest中指定Agent-Class属性,值为代理类全路径

  2. 代理类需要提供public static void agentmain(String args, Instrumentation inst)或public static void agentmain(String args)方法。并且再二者同时存在时以前者优先。args和inst和premain中的一致。

假如上面的agent的jar包打好了,那面临另外一个问题——如何在应用程序启动之后再开启代理程序呢? JDK6中提供了Java Tools API,其中Attach API可以满足这个需求。

在Attach API中的VirtualMachine代表一个运行中的VM。其提供了loadAgent()方法,可以在运行时动态加载一个代理jar。

Attach API在%java_home%\lib这个目录下的tool.jar包中。我是将它打在工程的lib包中的。

Attact API主要在这个目录下,也就这些东西,如图:


最主要的类就是VirtualMachine,下面写个例子熟悉下它。

这个例子做的事情很简单:

1、一个VMTest类没1秒钟输出一行:System.out.println("wait.....");  

2、写一个MyAgentMain类,这个类实现了

public static void agentmain(Stringargs, Instrumentation inst) 方法,在这个方法中打印出所有已经装在的class,其他什么也不做(也可以像先前讲解premin方法中的加入方法耗时统计)。


下面是具体的实现:

首先是具体的应用类:

/**
 * TODO Comment of VMTest
 * @author yongkang.qiyk
 */
public class VMTest {
    public static void main(String[] args) throwsInterruptedException {
        while(true){
            Thread.sleep(10000);
            new Thread(new WaitThread()).start();
        }
    }
    
   static class WaitThread implements Runnable {
 
        @Override
        public void run() {
            try {
                WaitTest w = new WaitTest();
                w.waiting();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }
}


这个是首先运行的类,运行后,每10秒打印一次 System.out.println("wait.....");


然后编写我们的运行时要加载的agent类:

public class MyAgentMain {
    
    public static void agentmain(String args, Instrumentation inst)throws UnmodifiableClassException{
        System.out.println("MyAgentMain agentmain attach...");
        
        System.getProperties().setProperty("monitor.conf", args);
        
        for (Class clazz :inst.getAllLoadedClasses()){
           System.out.println(clazz.getName()); 
        }
//        inst.addTransformer(newMonitorAgentTransformer(),true);
//       inst.retransformClasses(WaitTest.class);
        
        System.out.println("MyAgentMain agentmain end...");
    }
}


可以看到,这个agent类只是打印了加载的类,以及运行时设置了一个系统属性,这个系统属性是监控配置的文件路径,在这个例子中我没有用到它。


上面的写好之后,记得打成jar包,。注意:METAINF.MF的中记得加入Agent-Class:这个属性:


Manifest-Version: 1.0

Premain-Class: monitor.agent.MyAgent

Agent-Class: monitor.agentmain.MyAgentMain

Can-Redefine-Classes: true

Can-Retransform-Classes:true

Boot-Class-Path: javassist.jar

 


打包好monitor.jar的包之后,写一个在运行时加载代理的实现:

public class VirtualMachineTest {
 
    public static void main(String[] args) throwsAttachNotSupportedException, IOException,
            AgentLoadException,AgentInitializationException, InterruptedException {
        // attach to target VM 
        VirtualMachine vm = VirtualMachine.attach("13712");
        vm.loadAgent("D:/tools/java/monitor.jar", "D:/tools/java/profile.txt");
        Thread.sleep(1000);
        vm.detach();
    }
}


可以这个类做的事情也很简单,该程序接受一个参数为目标应用程序的进程id,通过Attach Tools API的VirtualMachine.attach方法绑定到目标VM,并向其中加载代理jar。13712就是刚才我运行VMTest的进程pid。

运行VirtualMachineTest 可以看到运行结果:


   打印出了所有已经加载的class。


同样的,你也可以通过instrumentation的 inst.addTransformer()方法加入ClassFileTransformer的实现类,然后对指定的class进行修改。

但是在运行时对class修改和premain的方式对class的修改是有点差别的,至少在jdk6以及以前的版本是这样的。

通过premain的方式在应用启动前对class可以做任何修改,可以添加class,删除class,添加删除修改方法体,添加成员变量等等没有任何限制,

然而通过agentmain的方式在运行时修改class是有限制的,,比如在class已经被加载过的情况下,是不能对class添加,删除方法的,只能重定义方法体,这对很多功能其实做了限制。相信jdk6以后的版本应该会放开这个限制吧。

但是我个人感觉,通过premain这种方式在vm启动前修改好class应该基本满足我们的需要了,如果一定要在运行时添加方法,删除方法等,也一定有方法可以绕开jdk6 instrumentation的这个限制。会找到办法的。。。。

参考文档:http://www.ibm.com/developerworks/cn/java/j-lo-jse61/index.html 

http://www.jar114.com/jdk6/zh_CN/api/java/lang/instrument/package-summary.html 


  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值