热部署插件通过agent来实现的,在JDK1.5以后,我们可以使用agent技术构建一个独立于应用程序的代理程序(即为Agent),用来协助监测、运行甚至替换其他JVM上的程序。使用它可以实现虚拟机级别的AOP功能。(Jrebel、BTrace)

首先要有一个agent程序,一个类。

有一个premain方法,顾名思义它代表着他将在主程序的main方法之前运行,agentArgs代表传递过来的参数,inst则是agent技术主要使用的API,我们可以使用它来改变和重新定义类的行为。

2、编写MANIFEST.MF文件

        MANIFEST.MF文件用于描述Jar包的信息,例如指定入口函数等。我们需要在该文件中加入如下配置,指定我们编写的含有premain方法类的全路径,然后将agent类打成Jar包。如果你是使用Maven来构建的项目,在构建的时候加入如下代码,否则Maven会生成自己的MANIFEST.MF覆盖掉你的。

电脑里面有两个java重新启动之后刚安装的java不见了只留下了旧的 多个javaagent启动_tomcat

 3、编写主程序

主程序的VM options添加上启动参数

-javaagent: 你的路径/test-1.0-SNAPSHOT.jar=hah

其中hah为上文中传入permain方法的agentArgs参数。运行我们的主程序

电脑里面有两个java重新启动之后刚安装的java不见了只留下了旧的 多个javaagent启动_程序运行_02

在主程序运行之前的agent模式有一些缺陷,例如需要在主程序运行前就指定javaagent参数,premain方法中代码出现异常会导致主程序启动失败等,为了解决这些问题,JDK1.6以后提供了在程序运行之后改变程序的能力。它的实现步骤和之前的模式类似。

我们复用上面的类,将premain方法修改为agentmain方法,由于是在主程序运行后再执行,意味着我们可以获取主程序运行时的信息,这里我们打印出来主程序中加载的类名。

电脑里面有两个java重新启动之后刚安装的java不见了只留下了旧的 多个javaagent启动_main方法_03

重复上面第二步骤,修改MANIFEST.MF文件。

添加Agent-Class参数,打成Jar包。

3、启动主程序,编写加载agent类的程序

在程序运行后加载,我们不可能在主程序中编写加载的代码,只能另写程序,那么另写程序如何与主程序进行通信?这里用到的机制就是attach机制,它可以将JVM A连接至JVM B,并发送指令给JVM B执行,JDK自带常用工具如jstack,jps等就是使用该机制来实现的。这里我们先用tomcat启动一个程序用作主程序B,再来写A程序代码。

电脑里面有两个java重新启动之后刚安装的java不见了只留下了旧的 多个javaagent启动_tomcat_04

我们使用VirtualMachine attach到目标进程,其中78256为tomcat进程的PID,可以使用jps命令获得,也可以使用VirtualMachine.list方法获取本机上所有的Java进程,再来判断tomcat进程,loadAgent方法第一个参数为Jar包在本机中的路径,第二个参数为传入agentmain的args参数,此处为null,运行程序。

电脑里面有两个java重新启动之后刚安装的java不见了只留下了旧的 多个javaagent启动_main方法_05

我们是将进程attach到了tomcat进程上,agent其实是在主程序B中运行的,所以程序A中自然就不会进行打印,我们跳回tomcat程序的控制台,查看结果。

以上就是Java Agent的俩个简单小栗子了,Java Agent十分强大,它能做到的不仅仅是打印几个监控数值而已,还包括使用Transformer(推荐观看)等高级功能进行类替换,方法修改等,要使用Instrumentation的相关API则需要对字节码等技术有较深的认识。