https://www.cnblogs.com/silyvin/p/11260965.html,
此前对javaagent的使用是在类加载期,需要重启应用才能织入,本次实践java agent运行期织入
- premain是静态修改,在类加载之前修改; attach是动态修改,在类加载后修改
- 要使premain生效重启应用,而attach不重启应用即可修改字节码并让其重新加载
可以看到attach的方式更加强大,其核心原理首先是找到相关的进程id, 然后根据进程id去动态修改相关字节码
还是参考这篇文章:https://www.jianshu.com/p/b2d09a78678d
package agent; // //import com.sun.tools.attach.VirtualMachine;无法通过编译 import javassist.ClassPool; import javassist.CtBehavior; import javassist.CtClass; import java.io.ByteArrayInputStream; import java.io.IOException; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.lang.instrument.Instrumentation; import java.lang.instrument.UnmodifiableClassException; import java.lang.reflect.Method; import java.security.ProtectionDomain; /** * https://www.cnblogs.com/silyvin/p/11260965.html * https://www.cnblogs.com/silyvin/p/11336727.html * Created by sunyuming on 19/7/28. */ public class MyTransformer implements ClassFileTransformer { private ClassPool classPool = new ClassPool(true); @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { if(className.equals("agent/MyClient")) { System.out.println("类 " + className); try { CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer)); for(CtBehavior ctBehavior : ctClass.getDeclaredBehaviors()) { if(ctBehavior.getLongName().equals("agent.MyClient.print()")) { System.out.println("开始处理方法 " + ctBehavior.getLongName()); ctBehavior.insertBefore("System.out.println("前置aop");"); ctBehavior.insertAfter("System.out.println("后置aop");"); } } return ctClass.toBytecode(); } catch (Exception e) { e.printStackTrace(); return null; } } else return null; } public static void agentmain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException, InterruptedException { System.out.println("Agent Main start " + agentArgs); inst.addTransformer(new MyTransformer(), true); inst.retransformClasses(MyClient.class); } public static void premain(String args, Instrumentation instrumentation) { System.out.println("开始premain " + args); ClassFileTransformer classFileTransformer = new MyTransformer(); instrumentation.addTransformer(classFileTransformer); } /** * premain * MANIFEST.MF.premain * cp target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar ~/Documents/tool/jars/myagentpre.jar * MANIFEST.MF * java -javaagent:/Users/sunyuming/Documents/tool/jars/myagentpre.jar=sun -jar target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar */ /** * agentmain * MANIFEST.MF * cp target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar ~/Documents/tool/jars/myagented.jar * java -jar ~/Documents/tool/jars/myagented.jar * 另一个terminal * MANIFEST.MF.agentmain * jps * java -Djava.ext.dirs=${JAVA_HOME}/lib -jar target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar 47929 */ public static void main(String [] args) throws Exception { final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Class vmClass = classLoader.loadClass("com.sun.tools.attach.VirtualMachine"); Object vm = vmClass.getMethod("attach", String.class).invoke(null, args[0]); String path = MyTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath(); // main与agentmain也可以分开,体现在path上,我们这边合并 vmClass.getMethod("loadAgent", String.class, String.class).invoke(vm, path, ""); // 无法通过编译,除非在pom中加入tool,即使如此,运行期也需要 -Djava.ext.dirs=${JAVA_HOME}/lib // VirtualMachine vm = VirtualMachine.attach(args[0]); //正在运行的java 程序 ps id // vm.loadAgent("/Users/sunyuming/work/MyTest/target/MyTest-1.0-SNAPSHOT-jar-with-dependencies.jar"); } }
被注入的jar
Manifest-Version: 1.0 Main-Class: agent.MyClient
agent
Manifest-Version: 1.0 Main-Class: agent.MyTransformer Agent-Class: agent.MyTransformer Can-Redefine-Classes: true Can-Retransform-Classes: true
premain
Manifest-Version: 1.0 Premain-Class: agent.MyTransformer
2个附属:
1)也许以后补充一个asm
2)配置文件是否可以?