JavaAgent
1.0 先看几个面试题:
- 怎么获取某个对象的大小?
- 运行期将已经加载的类的字节码能做变更吗,怎么做?有什么限制?
- 如何获取所有已经被加载过的类?
- 怎么在加载java文件之前做拦截把字节码做修改?
- 如何获取所有已经被初始化过了的类(clinit后的方法)
- 如何设置某些native方法的前缀?
- 如何在查找native方法的时候做规则匹配?
- 如何将某个jar加入到bootstrapclasspath 里作为高优先级被bootstrapClassloader 加载?
- AppClassloader怎么去加载某个加入到 classpath 里的 jar?
我:…(#$%^&*)
1.1 先从来源说起
JavaAgent 是JDK 1.5 以后引入的,也可以叫做Java代理。
JavaAgent 是运行在 main方法之前的拦截器,它内定的方法名叫 premain ,也就是说先执行 premain 方法然后再执行 main 方法。
主要功能如下:
- 看问题 1.0
按照加载时机可以分为两种:
- 进程启动前
- 进程启动后
1.2 如何实操?
准备工作:
- 电脑一台、带键盘鼠标显示器,能通电能开机,内存512M,一核CPU,差不多就行
- 安装 jdk(1.8) ,安装 IDEA(2021.2.4),安装 maven
实施步骤:
- 首先创建一个 Java Aganet 的 maven 项目,步骤如下:
打开 IDEA: - 新建项目:
走到这一步的小伙伴就可以编写具体的 JavaAgent 的代码了。
具体代码:
package com.carrot.sec;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class MyAgent {
public static void premain(String arg, Instrumentation instrumentation){
System.err.println("MyAgent 开始执行了! , 参数是 " + arg);
//注册我们的函数
instrumentation.addTransformer(new ClassFileTransformer() {
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer
) throws IllegalClassFormatException {
System.out.println("加载的类名字是: " + className);
return classfileBuffer;
}
});
}
}
写好了就打包一个 java 的 jar 包。
pom文件增加以下内容:
<build>
<plugins>
<!-- jdk 1.8 编译 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
<!-- java agent -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</plugins>
</build>
执行 maven 打包命令:
mvn clean package -U -DMaven.test.skip=true
出现这个错误,不要慌,去 resources 下手动创建 pom 文件里面指定的目录:src/main/resources/META-INF/MANIFEST.MF
META-INF/MANIFEST.MF 的内容:
Manifest-Version: 1.0
Premain-Class: com.carrot.sec.MyAgent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
属性 | 说明 | 是否必填 | 默认值 |
Premain-Class | 包含premain方法的类 | 依赖启动方式 | 无 |
Agent-Class | 包含agentmain方法的类 | 依赖启动方式 | 无 |
Boot-Class-Path | 启动类加载器搜索路径 | 否 | 无 |
Can-Redefine-Classis | 是否可以重定义代理所需的类 | 否 | false |
Can-Retransform-Classis | 是否能够重新转换此代理所需的类 | 否 | false |
Can-Set-Native-Method-Prefix | 是否能够设置此代理所需的本机方法前缀 | 否 | false |
再次打包就可以了。。。
找到 jar 包,检查里面的内容,如果不对就修改一下。
没啥问题就过了。。。
- 再建一个普通的项目,我这边比较懒直接建了个 Spring boot 项目。
- 步骤如下:
IDEA中的VM命令
-javaagent:D:\local-project\FirstAgent\target\FirstAgent-1.0-SNAPSHOT.jar
这些工作做完了之后就可以启动我们的 Spring Boot 项目了。
假如你想用命令来启动,也是可以做到的:
- 把 Spring Boot 项目打包,和 MyAgent 的打包方式是一样的~~~
- 找到 Spring Boot 的 jar 包的位置,也找到 MyAgent 的位置。
- 执行命令:
java -javaagent:D:\local-project\FirstAgent\target\FirstAgent-1.0-SNAPSHOT.jar -jar demo-0.0.1-SNAPSHOT.jar
更多的用法或者用途,我们一起发现一起创造!
比如:
- 怎么获取某个对象的大小?
- 运行期将已经加载的类的字节码能做变更吗,怎么做?有什么限制?
- 如何获取所有已经被加载过的类?
- 怎么在加载java文件之前做拦截把字节码做修改?
- 如何获取所有已经被初始化过了的类(clinit后的方法)
- 如何设置某些native方法的前缀?
- 如何在查找native方法的时候做规则匹配?
- 如何将某个jar加入到bootstrapclasspath 里作为高优先级被bootstrapClassloader 加载?
- AppClassloader怎么去加载某个加入到 classpath 里的 jar?
这些案例会抽时间逐一做出解答。