gball个人知识库
首页
基础组件
基础知识
算法&设计模式
  • 操作手册
  • 数据库
  • 极客时间
  • 每日随笔
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 画图工具 (opens new window)
关于
  • 网盘 (opens new window)
  • 分类
  • 标签
  • 归档
项目
GitHub (opens new window)

ggball

后端界的小学生
首页
基础组件
基础知识
算法&设计模式
  • 操作手册
  • 数据库
  • 极客时间
  • 每日随笔
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 画图工具 (opens new window)
关于
  • 网盘 (opens new window)
  • 分类
  • 标签
  • 归档
项目
GitHub (opens new window)
  • 面试

  • 数据库

  • linux

  • node

  • tensorFlow

  • 基础组件

  • 基础知识

    • java集合

    • jvm调优

    • java并发编程

    • java网络编程

    • java8新特性

    • javaAgent

      • javaAgent初探
        • premain方法介绍
        • premain实现更改class文件
        • 测试过程:
    • java高级

  • 算法与设计模式

  • 分布式

  • 疑难杂症

  • go学习之旅

  • 极客时间

  • 知识库
  • 基础知识
  • javaAgent
ggball
2022-09-28

javaAgent初探

# agent 初探

  1. premain 命令式 在main方法之前执行
  2. agentmain 以Attach的方式载入,在Java程序启动后执行,1.6提供

img.png

public interface Instrumentation {
    
    /**
     * 添加Transformer(转换器) 
     * ClassFileTransformer类是一个接口,通常用户只需实现这个接口的  byte[] transform()方法即可;
     * transform这个方法会返回一个已经转换过的对象的byte[]数组
     * @param transformer            拦截器
     * @return canRetransform        是否能重新转换
     */
 	void addTransformer(ClassFileTransformer transformer, boolean canRetransform);    

    /**
     * 重新触发类加载,
     * 该方法可以修改方法体、常量池和属性值,但不能新增、删除、重命名属性或方法,也不能修改方法的签名
     * @param classes           Class对象
     * @throws  UnmodifiableClassException       异常
     */
    void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;

    /**
     * 直接替换类的定义
     * 重新转换某个对象,并已一个新的class格式,进行转化。
     * 该方法可以修改方法体、常量池和属性值,但不能新增、删除、重命名属性或方法,也不能修改方法的签名
     * @param definitions           ClassDefinition对象[Class定义对象]
     * @throws  ClassNotFoundException,UnmodifiableClassException       异常
     */
    void redefineClasses(ClassDefinition... definitions)throws  ClassNotFoundException, UnmodifiableClassException;

    /**
     * 获取当前被JVM加载的所有类对象
     * @return Class[]        class数组
     */
    Class[] getAllLoadedClasses();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

# premain方法介绍

public class AgentWithTransFormer {

    public static void premain(String agentOps, Instrumentation instrumentation) {
        System.out.println("premian start 。。");
        System.out.println("agentOps = " + agentOps);
        instrumentation.addTransformer(new DefineTransformer(),true);
    }


    /**
     *
     */
    static class DefineTransformer implements ClassFileTransformer {

        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            System.out.println("premain load class :" + className);
            return classfileBuffer;
        }
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

premain方法会拦截大部分类(不包含有些系统类),实现类DefineTransformer需要实现transform方法,比如我们可以拦截某个类,将某个类的class文件修改,可以整个文件替换或者利用javassist自定义添加或者删除方法。

# premain实现更改class文件

被代理类

/**
 * @author ggBall
 * @version 1.0.0
 * @ClassName AgentTest.java
 * @Description
 * 测试命令 需要在 D:\project\idea\ggball_test\agent_demo\agent_test\src\main\java 目录下执行
 * javac -encoding UTF-8 .\com\zhu\AgentMainTest.java
 * 在項目所在java目录下运行
 * cd D:\project\idea\ggball_test\agent_demo\agent_test\src\main\java
 * java -javaagent:D:\project\idea\ggball_test\agent_demo\agent\target\java_agent.jar  com.zhu.AgentMainTest
 * @createTime 2022年09月27日 19:00:00
 */
public class AgentMainTest {

    public static void main(String[] args) throws Exception {
        Thread.sleep(1000);
        Console.print();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

console类

public class Console {

    public static void print() {
        System.out.println("hello!");
    }
}

1
2
3
4
5
6
7

agent类

public class ReplaceFileAgent {

    public static void premain(String agentOps, Instrumentation instrumentation) {
        System.out.println("ReplaceFileAgent premian start 。。");
        System.out.println("agentOps = " + agentOps);
        instrumentation.addTransformer(new ClassFileFormer(),true);
    }
}
1
2
3
4
5
6
7
8

ClassFileTransformer

public class ClassFileFormer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (className.equals("com/zhu/Console")) {
            String root = System.getProperty("user.dir")+"\\agent_demo";
            String classPath = root + "\\agent_test\\src\\main\\java\\com\\zhu\\Console.class";
            byte[] bytes = FileUtil.readBytes(classPath);
            return bytes;
        }

        return classfileBuffer;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

主要作用就是修改Console.class文件 在 目录 D:\project\idea\ggball_test\agent_demo\agent_test\src\main\java 下执行

  1. 先修改print 为 System.out.println("hello agent!");
  2. 然后编译 C:\Users\16678.jdks\corretto-1.8.0_292\bin\javac -encoding UTF-8 .\com\zhu\Console.java (注意点: 编译的jdk版本需要与运行时的jdk)
  3. 最后 将 print方法改回 System.out.println("hello!");

# 测试过程:

首先需要在代理项目resources目录下 添加META-INF 目录 再添加 MANIFEST.MF,MANIFREST.MF文件(我自己可能打包方式有问题,MANIFEST.MF文件内容不完全,导致只能在jar里手动改) 填写内容:

Manifest-Version: 1.0
Premain-Class: com.zhu.ReplaceFileAgent
Built-By: 16678
Agent-Class: com.zhu.ReplaceFileAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Class-Path: javassist-3.28.0-GA.jar hutool-all-5.8.7.jar
Created-By: Apache Maven 3.8.1
Build-Jdk: 1.8.0_292
1
2
3
4
5
6
7
8
9
  • Premain-Class :包含 premain 方法的类(类的全路径名)
  • Agent-Class :包含 agentmain 方法的类(类的全路径名)
  • Boot-Class-Path :设置引导类加载器搜索的路径列表。查找类的特定于平台的机制失败后,引导类加载器会搜索这些路径。按列出的顺序搜索路径。列表中的路径由一个或多个空格分开。路径使用分层 URI 的路径组件语法。如果该路径以斜杠字符(“/”)开头,则为绝对路径,否则为相对路径。相对路径根据代理 JAR 文件的绝对路径解析。忽略格式不正确的路径和不存在的路径。如果代理是在 VM 启动之后某一时刻启动的,则忽略不表示 JAR 文件的路径。(可选)
  • Can-Redefine-Classes :true表示能重定义此代理所需的类,默认值为 false(可选)
  • Can-Retransform-Classes :true 表示能重转换此代理所需的类,默认值为 false (可选)
  • Can-Set-Native-Method-Prefix: true表示能设置此代理所需的本机方法前缀,默认值为 false(可选) 即在该文件中主要定义了程序运行相关的配置信息,程序运行前会先检测该文件中的配置项。

或者添加maven插件

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <archive>
                        <!--自动添加META-INF/MANIFEST.MF -->
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                        <manifestEntries>
                            <Premain-Class>com.zhu.AgentWithTimeTransFormer</Premain-Class>
                            <Agent-Class>com.zhu.AgentWithTimeTransFormer</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

img_1.png idea添加vm option参数 类似于

java -javaagent:D:\project\idea\ggball_test\agent_demo\agent\target\java_agent.jar  com.zhu.AgentMainTest
1

如果不被代理情况下 只会打印 hello,由于console class文件被替换 所以执行结果 img_2.png

上次更新: 2025/06/04, 15:06:15
记录JAVA8中Optional的使用
动态代理

← 记录JAVA8中Optional的使用 动态代理→

最近更新
01
AIIDE
03-07
02
githubActionCICD实战
03-07
03
windows安装Deep-Live-Cam教程
08-11
更多文章>
Theme by Vdoing
总访问量 次 | 总访客数 人
| Copyright © 2021-2025 ggball | 赣ICP备2021008769号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×

评论

  • 评论 ssss
  • 回复
  • 评论 ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
  • 回复
  • 评论 ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
  • 回复
×