利用Instrumentation热更新

热更新一种方式是通过classLoader重新加载来进行,通过定义不同的classLoader,监听到文件变化时,卸载原来的classLoader,然后用新的classLoader加载新的jar包。另外一种方式是利用Instrumentation的方式来进行。其主要是利用tools包下的VirtualMachine将agent类加入到对应的虚拟机中。并利用Instrumentation对类进行重定义操作。

首先,启动一个一直存活的线程。

public class Starter {
	public static void main(String[] args) throws InterruptedException {
		Test test = new Test() ;
		while(true) {
			System.out.println(test.get()) ; 
			Thread.sleep(1000);
		}
	}
}
public class Test {
	public String get() {
		return "A" ; 
	}	
}

可以看到这个程序一直在打印A
在这里插入图片描述

获取这个进程的pid

利用Instrumentation热更新_第1张图片
利用jps可以查询到这个虚拟机对应的pid是26636

编写代理代码

代理代码主要是agentmain方法会传入Instrumentation这个接口,可以通过这个接口进行代理操作。

import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
public class DemoAgent {		
	public static void agentmain(String args,Instrumentation instrumentation) 
			throws ClassNotFoundException, UnmodifiableClassException {
		System.out.println("agentmain") ; 
		String[] ss = args.trim().split(" ") ; 
		Class<?> clazz = Class.forName(ss[0]) ; 
		System.out.println(clazz) ; 
		System.out.println(ss[1]) ; 
		instrumentation.redefineClasses(new ClassDefinition(clazz, FileUtil.getBytes(ss[1])));
	}	
}
import java.io.InputStream;
public class FileUtil {
	public static byte[] getBytes(String filePath) {
		InputStream fis = null ; 
		byte[] bytes = null ; 
		try {
			fis = FileUtil.class.getResourceAsStream(filePath) ; 
			bytes = new byte[fis.available()] ; 
			fis.read(bytes) ; 
			return bytes ; 
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			if(fis != null) {
				try {
					fis.close();
				}catch(Exception e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

}

将其打包

然后将其打成一个jar包,利用jd-gui反编译这个agent.jar,可以看到这个agent.jar中的MAINFEST.MF文件中配置了Agent-Classs为com.test.DemoAgent,而这个代理操作的入口就是这个DemoAgent的对应的agentmain方法。并且配置了Can-Redefine-Classes表示可以重新定义class,即可以调用InstrumentationredefineClasses方法。

Manifest-Version: 1.0
Agent-Class: com.test.DemoAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true

利用Instrumentation热更新_第2张图片

利用VirtualMachine将代理包连接到对应虚拟机

import java.io.IOException;
import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
public class AgentMain {
	
	public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException {
		VirtualMachine attach = VirtualMachine.attach("26636") ; 
		String arg = "com.test4.Test" + " " + "/res/Test.class" ; 
		attach.loadAgent("D:\\workspace\\eclipse_work_space\\Test\\res\\agent.jar",arg);
		attach.detach(); 
		System.out.println("all done") ;
	}
	
}

这段程序主要是利用tools包中的VirtualMachine 将上面的agent.jar连接到Starter进程中,进行动态更新操作,其中tools包为jdk的lib下的jar包
利用Instrumentation热更新_第3张图片
下图展示了其最终的结果,可以看到又原来的打印A在动更后转为打印B了。
利用Instrumentation热更新_第4张图片

你可能感兴趣的:(深入理解java虚拟机学习笔记)