1、创建一个maven项目,不要用springboot的
引入依赖
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.12.0</version></dependency><dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.29.0-GA</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.3.24</version></dependency>
其中 javassist这个依赖必须的 其他是代码逻辑用到的 根据自己需要来
设置打包插件
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><configuration><!--将所有依赖都打入同一个jar包中--><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs><!--指定java agent相关配置文件--><archive><manifestFile>src/main/resources/MANIFEST.MF</manifestFile></archive></configuration></plugin>
编写主类代码
JavaAgent.java
import java.lang.instrument.Instrumentation;/*** 拦截打印方法的返回值*/ public class JavaAgent {public static void premain(String agentArgs, Instrumentation inst) {inst.addTransformer(new ClassPreProcessorAgentAdapter(), true);}}
agentArgs:可以通过这个把一些参数传进来
ClassPreProcessorAgentAdapter.java
import javassist.*; import org.apache.commons.lang3.StringUtils;import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain;/*** 打印方法返回值 具体逻辑*/ public class ClassPreProcessorAgentAdapter implements ClassFileTransformer {@Overridepublic byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {//是我们自己写的类才打印if (StringUtils.isBlank(className)) {return classfileBuffer;}//将类名路径 / 换成 .String classPkgName = className.replace('/', '.');//如果不是我们写的类 直接返回 这里自定义逻辑if (!classPkgName.contains("IndexController")) {return classfileBuffer;}final ClassPool pool = ClassPool.getDefault();try {CtClass ctClass = pool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer));//匹配类的路径是我们要的才打印if (ctClass.getName().replace('/', '.').startsWith("com.example.demotest")) {for (CtMethod method : ctClass.getDeclaredMethods()) {if (method.hasAnnotation(org.springframework.web.bind.annotation.RequestMapping.class)) {// 这句话最重要 打印返回值的内容method.insertAfter("System.out.println(\"Controller Method Returned: \" + $_);");}}return ctClass.toBytecode();}} catch (Exception e) {e.printStackTrace();}System.out.println("premain load Class:" + className);return classfileBuffer;}}
然后resource下放文件
MANIFEST.MF
Manifest-Version: 1.0 Specification-Title: Log Agent Specification-Version: 0.0.1 Specification-Vendor: LogAgent Implementation-Title: log.agent Implementation-Version: 0.0.1 Implementation-Vendor: LogAgent Premain-Class: com.demo.agent.JavaAgent Can-Redefine-Classes: true Can-Retransform-Classes: true
Premain-Class:这个就是我们写的premain的类的全路径 我这里是JavaAgent的类路径 根据自己的来
最后一行要空一行 ,这个不能少
最后打包,会生成一个 jar-with-dependencies.jar结尾的jar包,然后我们通过启动的时候命令引用进去即可
-javaagent:D:\jar包路径\agent-jar-with-dependencies.jar
IDEA的话是在那个启动类的 VM options里面设置
如果要调试代码的话 要把启动的项目和我们agent的项目放在一个工程底下,这样idea就能自动断点到