通过java agent 探针和javassist 实现从字节码层面的aop功能
首先我们要创建一个探针jar包
项目结构如下:
-
1、创建入口类
package com.wan;
import java.lang.instrument.Instrumentation;
public class MyAgent {
public static void premain(String agentOpts, Instrumentation instrumentation) {
System.out.println("====premain 方法执行===");
instrumentation.addTransformer(new InsertTransformer());
}
public static void premain(String agentOps){
System.out.println("====premain方法执行2====");
System.out.println(agentOps);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
-
2、创建MANIFEST.MF文件,指定Premain-Class类
Manifest-Version: 1.0
Premain-Class: com.wan.MyAgent
Can-Redefine-Classes: true
-
3、创建pom文件,让maven可以将MANIFEST.MF文件打入到jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>my-agent</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<!--指明包含 premain 方法的类名,否则打包出来的文件会找不到 MANIFEST.MF -->
<Premain-Class>com.wan.MyAgent</Premain-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
-
4、创建转换类
主要实现的作用就是在TongShuaiStockController
的shareStockList ()
方法一开始打印出我是before
,关于javassit 可以查看相关资料 javassist使用全解析
这里有个关键是我们通过线程获得上下文的类加载器,不然会找不到 TongShuaiStockController类,因为项目是web项目,使用的是自定义的类加载器
package com.wan;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.LoaderClassPath;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class InsertTransformer implements ClassFileTransformer {
public final String injectedClassName = "com.haier.cbs.web.controller.stock.TongShuaiStockController";
public final String methodName = "shareStockList";
// public final String injectedClassName = "com.wan.spi.Solution";
// public final String methodName = "oddEvenList";
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
className = className.replace("/", ".");
// System.out.println(className);
if (className.equals(injectedClassName)) {
CtClass ctclass = null;
try {
ClassPool classPool = ClassPool.getDefault();
//使用相同的类加载器,不然找不controller
classPool.appendClassPath(new LoaderClassPath(this.getClass().getClassLoader()));
ctclass = classPool.get(className);// 使用全称,用于取得字节码类<使用javassist>
CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 得到这方法实例
ctmethod.insertBefore("System.out.println("我是before");");
return ctclass.toBytecode();
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
return null;
}
}
-
5、使用maven进行打包得到jar包
-
6、在需要使用探针的项目中加入虚拟机启动参数
-javaagent:D:/workspace/my-agent/target/my-agent-1.0-SNAPSHOT.jar="1212"
这个方法就是我们要处理的方法:
在我们调用接口的时候就会先打印“我是before”。