java agent 运行期织入【yetdone】(javassist)

https://www.cnblogs.com/silyvin/p/11260965.html,

此前对javaagent的使用是在类加载期,需要重启应用才能织入,本次实践java agent运行期织入

 

  1. premain是静态修改,在类加载之前修改; attach是动态修改,在类加载后修改
  2. 要使premain生效重启应用,而attach不重启应用即可修改字节码并让其重新加载

可以看到attach的方式更加强大,其核心原理首先是找到相关的进程id, 然后根据进程id去动态修改相关字节码

 

还是参考这篇文章:https://www.jianshu.com/p/b2d09a78678d

    <dependency>
      <groupId>org.javassist</groupId>
      <artifactId>javassist</artifactId>
      <version>3.22.0-GA</version>
    </dependency>

 

public static void agentmain(String agentArgs, Instrumentation inst)
throws ClassNotFoundException, UnmodifiableClassException,
InterruptedException {
System.out.println("Agent Main start " + agentArgs);
inst.addTransformer(new MyTransformer(), true);
Class[] classes = inst.getAllLoadedClasses();
for(Class c : classes) {
if(c.getName().indexOf("MyClient") != -1)
inst.retransformClasses(c);
}

}

 

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)配置文件是否可以?

posted on 2019-08-11 21:11  silyvin  阅读(520)  评论(0编辑  收藏  举报