javaAgent
记录之前印象中的使用过程记录,感兴趣的可按照套路来实践下。
注意需要使用到tools.jar包
- 1. 制作hotfix-1.0.jar
Manifest-Version: 1.0
Agent-Class: xxx.xxx.xxxx.JavaDynAgent
Can-Redefine-Classes: true
import java.lang.instrument.Instrumentation;
import org.apache.log4j.Logger;
public class JavaDynAgent {
private static final Logger logger = Logger.getLogger(JavaDynAgent.class);
private static Instrumentation instrumentation;
private static Object lockObject = new Object();
public static void agentmain(String args, Instrumentation inst) {
synchronized(lockObject) {
if(instrumentation == null) {
logger.info("agentmain called, init instrumentation inst");
instrumentation = inst;
}else {
logger.info("agentmain called, already init");
}
}
}
public static Instrumentation getInstrumentation() {
return instrumentation;
}
}
- 2. 项目调用接口类
import java.io.File;
import java.io.IOException;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.google.common.base.Preconditions;
import com.google.common.io.Files;
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 JavaAgentUtil {
private static final Logger logger = Logger.getLogger(JavaAgentUtil.class);
private static String basePath;
private static String classesPath;
private static String jarPath;
private static VirtualMachine vm;
private static String pid;
static {
basePath = getBasePath();
//classesPath = basePath + "classes" + "/";
classesPath = basePath + "hotfix" + "/";
jarPath = basePath + "lib" + "/";
// 当前进程pid
String name = ManagementFactory.getRuntimeMXBean().getName();
pid = name.split("@")[0];
logger.info("JavaAgentUtil init classesPath = " + classesPath + " jarPath = " + jarPath);
}
public static String getBasePath() {
String clsPath = JavaAgentUtil.class.getResource("").getPath();
String[] strArr = clsPath.split("classes|lib");
return strArr[0];
}
public static boolean javaAgent(String root, String[] classArr) throws ClassNotFoundException, IOException, UnmodifiableClassException, AttachNotSupportedException, AgentLoadException, AgentInitializationException
{
try {
// 虚拟机加载
logger.info("init agent attach jvm");
vm = VirtualMachine.attach(pid);
vm.loadAgent(jarPath + "hotfix-1.0.jar");
Instrumentation instrumentation = JavaDynAgent.getInstrumentation();
Preconditions.checkNotNull(instrumentation, "initInstrumentation must not be null");
StringBuilder tmpStr = new StringBuilder();
// 1.整理需要重定义的类
List<ClassDefinition> classDefList = new ArrayList<ClassDefinition>();
for (String className : classArr) {
Class<?> c = Class.forName(className);
String classPath = (StringUtils.isNotBlank(root) ? root : classesPath) + className.replace(".", "/") + ".class";
byte[] bytesFromFile = Files.toByteArray(new File(classPath));
ClassDefinition classDefinition = new ClassDefinition(c, bytesFromFile);
classDefList.add(classDefinition);
tmpStr.append(className);
}
// 2.redefine
JavaDynAgent.getInstrumentation().redefineClasses(classDefList.toArray(new ClassDefinition[classDefList.size()]));
logger.info(" redefine classes success classArr = " + tmpStr.toString());
return true;
}
catch (Exception e) {
logger.error("javaAgent err when redefine class msg = " + e.getMessage());
} finally {
if (vm != null) {
vm.detach();
logger.info("detach agent jvm");
}
}
return false;
}
// reload class files
public static boolean loadClass(String[] names){
try {
return javaAgent(null, names);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnmodifiableClassException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AttachNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AgentLoadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AgentInitializationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
}
- 3. 调用前面接口例子
String[] clsNames = fixnames.split("\\|");
JavaAgentUtil.loadClass(clsNames);
其中fixnames为以|分割的字符串
例如:class1路径|class2路径