JAVA-Agent探针打印方法执行的返回值
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 { @Override public 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.GetMapping.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; } }
以上只是伪代码 而且只有请求上有@GetMappting的注解才会打印
然后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就能自动断点到
-----------------------有任何问题可以在评论区评论,也可以私信我,我看到的话会进行回复,欢迎大家指教------------------------
(蓝奏云官网有些地址失效了,需要把请求地址lanzous改成lanzoux才可以)