• 探索java agent技术和Javassist使用


    Java 字节码技术

    一、Java Agent

    1. Javaagent是java1.5以后引入的特性,其主要作用是在class被加载之前对其加载,以插入我们想要修改的代码

    1. javaagent最后展示形式是一个jar包,具有以下特性
    • 必须在META-INF/MANIFEST.MF中指定Premain-Class设定agent启动类
    • 在agent启动类中写启动方法public static void premain(String agentArgs, Instrumentation inst){}
    • 不可直接运行,只能通过jvm参数-javaagent:xxx.jar附着于其他JVM进程运行

    3.入门实例

    3.1 编写Agent启动类

      public class TestAgent {
            public static void premain(String agentArgs, Instrumentation inst){
                System.out.println("premain start");
            }
        }
    

    3.2 编写pomx.xml文件并将Agent启动类打包成jar文件
    <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>2.3.1</version> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> </manifest> <manifestEntries> <Premain-Class>com.mmc.agent.TestAgent</Premain-Class> </manifestEntries> </archive> </configuration> </plugin> </plugins> </build>
    使用package命令打包。

    3.3 编写一个main方法,并运行

      public class Main {
    
        public static void main(String[] args) {
            System.out.println("start");
        }
    }
    

    增加启动参数:-javaagent:D:webProjectsdubbo-masterdubbo-study-parentjava-agent argetjava-agent-1.0-SNAPSHOT.jar
    路径为第二步打包的jar包的文件路径

    3.4 运行情况

    premain start
    start


    二、Javassist使用

    javassist是一个修改字节码的工具库。可以拿来配合JavaAgent使用

    实例:计算某个方法的执行时间。

    1. 引入pom文件
     <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.javassist</groupId>
                <artifactId>javassist</artifactId>
                <version>3.24.1-GA</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>2.3.1</version>
                    <configuration>
                        <archive>
                            <manifest>
                                <addClasspath>true</addClasspath>
                            </manifest>
                            <manifestEntries>
                                <Premain-Class>com.mmc.agent.TestAgent</Premain-Class>
                                <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                <Can-Retransform-Classes>true</Can-Retransform-Classes>
                                <Boot-Class-Path>javassist-3.24.1-GA.jar</Boot-Class-Path>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    1. 先写一个目标方法
    public class UserService {
    
        public void sayHello(){
            System.out.println("hello");
        }
    }
    
    1. 写Agent方法
    public class TestAgent {
    
        public static void premain(String agentArgs, Instrumentation inst) throws NotFoundException, UnmodifiableClassException, ClassNotFoundException, CannotCompileException, IOException {
            inst.addTransformer(new ClassFileTransformer() {
                @Override
                public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                    if(!"com/mmc/agent/UserService".equals(className)){
                        return null;
                    }else {
                        byte[] bytes= new byte[0];
                        try {
                            bytes = buildMonitorClass();
                        } catch (NotFoundException e) {
                            e.printStackTrace();
                        } catch (CannotCompileException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        return bytes;
                    }
                }
            },true);
    
    
    
        }
    
    
        public static byte[] buildMonitorClass() throws NotFoundException, CannotCompileException, IOException {
            /**
             * 1. 拷贝一个新方法
             * 2. 修改原方法名称
             * 3. 加监听代码
             */
            ClassPool classPool=new ClassPool();
            classPool.appendSystemPath();
            CtClass ctClass = classPool.get("com.mmc.agent.UserService");
            CtMethod ctMethod = ctClass.getDeclaredMethod("sayHello");
            CtMethod newMethod = CtNewMethod.copy(ctMethod, ctClass, new ClassMap());
            ctMethod.setName("sayHello$agent");
            newMethod.setBody("{ long start=System.nanoTime();
    " +
                    "        try{
    " +
                    "            sayHello$agent();
    " +
                    "        }finally {
    " +
                    "            System.out.println("执行耗时:"+(System.nanoTime()-start)+"纳秒");
    " +
                     "        }}");
            ctClass.addMethod(newMethod);
            return ctClass.toBytecode();
        }
    }
    
    1. 测试执行

    Main方法参数要加如下:

    -javaagent:D:webProjectsdubbo-masterdubbo-study-parentjava-agent argetjava-agent-1.0-SNAPSHOT.jar

    public class Main {
    
        public static void main(String[] args) {
            System.out.println("start");
            new UserService().sayHello();
        }
    }
    

    执行结果:

    start
    hello
    执行耗时:70300纳秒

  • 相关阅读:
    mybatis框架——实战练习——第一个spring boot + mybatis项目
    mybatis框架——实战练习——第一个spring boot + mybatis项目——在第一个项目的基础上使用@Select注解
    mybatis框架——实战练习——mysql8安装——mysql8.0.29——用户名:root密码:123456数据库名:mysql8
    mybatis框架——实战练习——第一个spring boot + mybatis项目——在第一个项目的基础上查询某个记录
    spring boot的基本配置——spring boot的web开发——Thymeleaf模板引擎
    快速转移数据的要领
    详解 Go 中的 rune 类型
    go的defer 函数
    创建k8s User Account登录
    redis分析rdb文件工具
  • 原文地址:https://www.cnblogs.com/javammc/p/12543267.html
Copyright © 2020-2023  润新知