目的
使用javaagent和javassist实现了把特定包下面的class的所有类的所有有body的非native的方法前面添加一句 System.out.println("hello im agent :" + ctMethod.getName());
javassist简介
Java 字节码以二进制的形式存储在 .class 文件中,每一个 .class 文件包含一个 Java 类或接口。Javassist 就是一个用来 处理 Java 字节码的类库。它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,并且不需要对字节码方面有深入的了解。同时也可以去生成一个新的类对象,通过完全手动的方式。
增加依赖
agentdemo1项目的pom文件增加javassist依赖包
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
</dependencies>
FirstAgent类
FirstAgent类实现ClassFileTransformer接口。重写transform方法。实现 ClassFileTransformer 这个接口的目的就是在class被装载到JVM之前将class字节码转换掉,从而达到动态注入代码的目的。其中代码的修改使用到了javassist。
FirstAgent类实现了把特定包下面的class的所有类的所有有body的非native的方法前面添加一句System.out.println("hello im agent :" + ctMethod.getName());
package com.david.instrumentationimpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.CodeAttribute;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class FirstAgent implements ClassFileTransformer {
//对特定包下的类修改字节码
public final String injectedClassName = "com.lagou.test";
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,byte[] classfileBuffer) throws IllegalClassFormatException {
className = className.replace("/", ".");
if (className.startsWith(injectedClassName)) {
CtClass ctclass = null;
try {
// 使用全称,用于取得字节码类,使用javassist技术实现
ctclass = ClassPool.getDefault().get(className);
CtMethod[] ctmethods = ctclass.getMethods();
for (CtMethod ctMethod : ctmethods) {
CodeAttribute ca = ctMethod.getMethodInfo2().getCodeAttribute();
if (ca == null) {
continue;
}
if (!ctMethod.isEmpty()) {
ctMethod.insertBefore("System.out.println(\"hello Im agent : " + ctMethod.getName() + "\");");
}
}
return ctclass.toBytecode();
}
catch (Exception e) {
System.out.println(e.getMessage()); e.printStackTrace();
}
}
return null;
}
}
PremainTest1类
package com.lagou.agent;
import com.lagou.instrumentationimpl.FirstAgent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class PremainTest1 {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("hello agent demo1");
System.out.println("agentArgs =====> " + agentArgs);
inst.addTransformer(new FirstAgent());
}
public static void premain(String agentArgs) {
System.out.println("hello agent demo2");
System.out.println("agentArgs =====> " + agentArgs);
}
}
更新打包插件
使用maven-jar-plugin插件进行打包。无法将第三方依赖的javassist包进行打包。如果需要将依赖包打入jar中。需要额外引入maven-dependency-plugin插件。为了简单,使用maven-shade-plugin进行打包。
修改agentdemo1的pom文件。将打包插件更换为maven-shade-plugin。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTr ansformer">
<manifestEntries>
<Premain- Class>com.lagou.agent.PremainTest1</Premain-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
打包
mvn clean package
agenttest1项目
修改AgentTest1类
package com.lagou.test1;
public class AgentTest1 {
public static void main(String[] args) {
System.out.println("这里是agent第一个main方法测试。"); testAgent(); t
estAgent1("one", "two");
}
public static void testAgent() {
System.out.println("test agent say hello");
}
public static void testAgent1(String one, String two) {
System.out.println("test agent say hello" + one + two);
}
}
测试
测试结果如下:
hello agent demo1
agentArgs =====> lagou
hello Im agent : main
这里是agent第一个main方法测试。
hello Im agent : testAgent
test agent say hello
hello Im agent : testAgent1
test agent say helloonetwo
Process finished with exit code 0
独立测试
脱离idea开发工具,独立使用java -javaagent命令测试。
通过 -javaagent 参数来指定我们的Java代理包,值得一说的是 -javaagent 这个参数的个数是不限的,如果指定了多个,则会按指定的先后执行,执行完各个 agent 后,才会执行主程序的 main 方法。
特别提醒:如果你把 -javaagent 放在 -jar 后面,则不会生效。也就是说,放在主程序后面的 agent 是无效的。
class方式
agentdemo1项目打jar包
agenttest1项目没有打包。进入 ..\agenttest1\target\classes目录中,执行如下命令
java -javaagent:e:\javaagent\agentdemo1.jar com.lagou.test1.AgentTest1
jar包方式
agentdemo1和agenttest1两个项目全部打jar包。
<build>
<finalName>agenttest1</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<!--自动添加META-INF/MANIFEST.MF -->
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.lagou.test1.AgentTest1</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
agentdemo1项目打包:将agentdemo1.jar复制到d:\javaagent目录中
mvn clean package
agenttest1项目打包:将agenttest1.jar复制到d:\javaagent目录中
使用java -javaagent命令,不带参数
java -javaagent:d:\javaagent\agentdemo1.jar -jar agenttest1.jar
使用java -javaagent命令,带参数
java -javaagent:d:\javaagent\agentdemo1.jar -jar agenttest1.jar
网友评论