首页 / JAVA / Javaagent 入门
Javaagent 入门
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Javaagent 入门,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含5697字,纯文字阅读大概需要9分钟。
内容图文
![Javaagent 入门](/upload/InfoBanner/zyjiaocheng/633/8ff984d24e93459ea6659e5a87fd735c.jpg)
学习背景
在解决一个线上问题时发现是因为JAVA线程池本身的设计导致,要彻底解决的话需要重写这部分实现。
然后就找了一些资料研究怎么重写JAVA底层的类,就发现了javaagent。
主要学习资料如下:
JVM TI
JavaAgent 原理与实践
JVM 源码分析之 javaagent 原理完全解读
简介
启动时加载的 JavaAgent 是 JDK1.5 之后引入的新特性,此特性为用户提供了在 JVM 将字节码文件读入内存之后,JVM 使用对应的字节流在 Java 堆中生成一个 Class 对象之前,用户可以对其字节码进行修改的能力,从而 JVM 也将会使用用户修改过之后的字节码进行 Class 对象的创建。
是什么
介绍javaagent之前也要介绍另一个概念JVMTI。
JVMTI是JDK提供的一套用于开发JVM监控, 问题定位与性能调优工具的通用编程接口(API)。
通过JVM TI,我们可以开发各式各样的JVMTI Agent。这个Agent的表现形式是一个以C/C++语言编写的动态共享库。
javaagent可以帮助我们快速使用JVMTI的功能,又不需要重写编写C/C++的底层库。
javaagent是依赖java底层提供的一个叫instrument的JVMTI Agent。这个agent又叫JPLISAgent(Java Programming Language Instrumentation Services Agent)
简单来说,javaagent是一个JVM的“插件”。
在java运行命令中 javaagent是一个参数,用来指定agent。
能做什么
- 可以在加载class文件之前进行拦截并把字节码做修改。
- 可以在运行期对已加载类的字节码做变更,但是这种情况下会有很多的限制。
- 还有其他一些小众的功能
- 获取所有已经加载过的类
- 获取所有已经初始化过的类(执行过 clinit 方法,是上面的一个子集)
- 获取某个对象的大小
- 将某个jar加入到bootstrap classpath里作为高优先级被bootstrapClassloader 加载
- 将某个jar加入到classpath里供AppClassloard去加载
- 设置某些native方法的前缀,主要在查找native方法的时候做规则匹配
总的来说可以让JVM按照我们的预期逻辑去执行。
最主要的也是使用最广的功能就是对字节码的修改。通过对字节码的修改我们就可以实现对JAVA底层源码的重写,也正好可以满足我之前的需求。
我们还可以做:
- 完全非侵入式的进行代码埋点,进行系统监控
- 修改JAVA底层源码,进行JVM自定义
- 实现AOP动态代理
Javaagent案例
接下可以创建一个全新的Maven项目,编写Javaagent的案例。
项目需要添加两个maven依赖。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.2-GA</version>
<optional>true</optional>
</dependency>
定义代理类
定义我们的第一代理类FirstAgent。这个代理类只做一个事情,在Instrumentation添加了一个我们自定义的Transformer。
public class FirstAgent {
public static void premain(String agentArgs, Instrumentation inst){
System.out.println("FirstAgent is Start.");
inst.addTransformer(new FirstTransformer());
}
}
实现ClassFileTransformer接口
接下来我们自定义一个ClassFileTransformer接口的实现
在这个类里,我们对原有的类做了两点修改:
- 增加了一个sex属性
- 重写toString方法
在修改字节码时使用了javassist这个字节码工具。
Javaassist就是一个用来处理Java字节码的类库,有机会再详细介绍。
User
public class User {
public String name;
public User(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
FirstTransformer
public class FirstTransformer implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
//只修改自定义的User类
if(className.equals("User")){
try {
ClassPool classPool = ClassPool.getDefault();
classPool.appendClassPath(new LoaderClassPath(loader));
CtClass clazz = classPool.makeClass(new ByteArrayInputStream(classfileBuffer), false);
//定义一个String类型的sex属性
CtField param = new CtField(classPool.get("java.lang.String"), "sex", clazz);
//设置属性为private
param.setModifiers(Modifier.PRIVATE);
//将属性加到类中,并设置属性的默认值为male
clazz.addField(param, CtField.Initializer.constant("male"));
//为刚才的sex属性添加GET SET 方法
clazz.addMethod(CtNewMethod.setter("setSex", param));
clazz.addMethod(CtNewMethod.getter("getSex", param));
//重写toString方法,将sex属性加入返回结果中。
CtMethod method = clazz.getDeclaredMethod("toString");
method.setBody("return \"User{\" +\n" +
" \"name='\" + name + '\\',' +\n" +
" \"sex='\" + sex + '\\'' +\n" +
" '}';");
return clazz.toBytecode();
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
}
打包
FirstAgent加上FirstTransformer就完成了第一个javaagent的全部代码编写部分。接下来进行打包,生成第一个javaagent
打包可以直接使用maven的打包工具,用mvn install打包即可。
<build>
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>FirstAgent</Premain-Class>
<Boot-Class-Path>firstAgent.jar</Boot-Class-Path>
<Can-Redefine-Classes>false</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
<Can-Set-Native-Method-Prefix>false</Can-Set-Native-Method-Prefix>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
这个里面有几个重要的配置:
- Premain-Class :包含 premain 方法的类(类的全路径名)我们这里就是FirstAgent类
- Boot-Class-Path :设置打包jar的文件名
- Can-Redefine-Classes :true表示能重定义此代理所需的类,默认值为 false(可选)
- Can-Retransform-Classes :true表示能重转换此代理所需的类,默认值为 false (可选)
- Can-Set-Native-Method-Prefix:true表示能设置此代理所需的本机方法前缀,默认值为 false(可选)
测试javaagent
编写一个测试方法
public static void main(String[] args) {
User user = new User("zane");
System.out.println(JSON.toJSON(user));
System.out.println(user.toString());
}
在没有使用我们编写的javaagent时,输出如下:
{"name":"zane"}
User{name='zane'}
在运用时添加参数 -javaagent:/Users/zane/javaagent/target/javaagent-1.0.jar,使用我们的代理类,注意路径修改成你本地的jar包地址。
在运行输出如下:
FirstAgent is Start.
{"sex":"male","name":"zane"}
User{name='zane,sex='male'}
可以看到代理已经生效, user对象新增了一个sex的属性,并重写了toString方法。
内容总结
以上是互联网集市为您收集整理的Javaagent 入门全部内容,希望文章能够帮你解决Javaagent 入门所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。