Java?Agent探針技術詳解示例
什么是java agent?
在JVM中運行中,類是通過classLoader加載.class文件進行生成的。在類加載加載.class文件生成對應的類對象之前時,我們可以通過修改.class文件內(nèi)容(就是字節(jié)碼修改技術),達到修改類的目的。JDK提供了對字節(jié)碼進行操作的一系列api,而使用這些api開發(fā)出的程序就可以稱之為java agent。
java agent能做什么?
不修改目標應用達到代碼增強的目的,就好像spring的aop一樣,但是java agent是直接修改字節(jié)碼,而不是通過創(chuàng)建代理類。例如skywalking就是使用java agent技術,為目標應用代碼植入監(jiān)控代碼,監(jiān)控代碼進行數(shù)據(jù)統(tǒng)計上報的。這種方式實現(xiàn)了解耦,通用的功能。
文末附上了我寫的一款接口mock agent。感興趣的可以看看,學習一波。
使用示例
入門
創(chuàng)建maven工程
創(chuàng)建一個簡單的maven工程:
添加agent類:
package com.hiwei; import java.lang.instrument.Instrumentation; public class DemoAgent { /** * 該方法在main方法之前運行 */ public static void premain(String arg, Instrumentation instrumentation) { System.out.println("執(zhí)行premain方法"); } }
pom.xml打包配置
添加打包插件:
<plugins> <plugin> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <shadedArtifactAttached>false</shadedArtifactAttached> <createDependencyReducedPom>true</createDependencyReducedPom> <createSourcesJar>true</createSourcesJar> <shadeSourcesContent>true</shadeSourcesContent> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <manifestEntries> <!-- 包含premain方法的類 --> <Premain-Class>${premain.class}</Premain-Class> <!-- 對目標類字節(jié)碼操作權限 --> <Can-Redefine-Classes>${can.redefine.classes}</Can-Redefine-Classes> <!-- 對目標類字節(jié)碼操作權限 --> <Can-Retransform-Classes>${can.retransform.classes}</Can-Retransform-Classes> </manifestEntries> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins>
將工程打成jar包,就可以使用了。打包很簡單:
在target目錄下就出現(xiàn)了jar包:
使用agent
創(chuàng)建測試類:
package com.hiwe.demo.cache; public class TestCache { public static void main(String[] args) { System.out.println("測試類"); } }
添加javaagent參數(shù)配置:
-javaagent:D:\person-project\demo-agent\target\demo-agent-1.0-SNAPSHOT.jar
運行測試類:
進階(一款接口mock數(shù)據(jù)小插件)
流程
使用java agent為接口mock數(shù)據(jù)
agent類:
package com.hiwei.test.agent; import com.hiwei.test.agent.utils.StringUtils; import java.io.*; import java.lang.instrument.Instrumentation; import java.nio.charset.StandardCharsets; public class DemoAgent { /** * 該方法在main方法之前運行 * @param arg 文件路徑 * @param instrumentation */ public static void premain(String arg, Instrumentation instrumentation) { //讀取mock文件 try { FileInputStream file = new FileInputStream(arg); BufferedReader rd = new BufferedReader(new InputStreamReader(file, StandardCharsets.UTF_8)); String configStr = readToString(rd); if(StringUtils.isNotEmpty(configStr)){ MockConfig.parseConfig(configStr); instrumentation.addTransformer(new DemoTransformer()); } } catch (Exception e) { e.printStackTrace(); } } private static String readToString(Reader rd) throws IOException { StringBuilder sb = new StringBuilder(); int i; while ((i = rd.read()) != -1) { sb.append((char) i); } return sb.toString(); } }
mock數(shù)據(jù)配置類
package com.hiwei.test.agent; import com.alibaba.fastjson.JSONObject; import java.util.HashMap; import java.util.Map; import java.util.Set; public class MockConfig { private static Map<String, Map<String, Object>> mockData = new HashMap<>(); public static void parseConfig(String configStr) { JSONObject config = JSONObject.parseObject(configStr); Set<String> classNameSet = config.keySet(); classNameSet.forEach(className -> { Map<String, Object> methodMock = new HashMap<>(); JSONObject methods = config.getJSONObject(className); Set<String> mSet = methods.keySet(); mSet.forEach(m -> { methodMock.put(m, methods.get(m)); }); mockData.put(className, methodMock); }); } public static boolean isMock(String className) { return !(mockData.get(className) == null); } public static Object getMockData(String className, String methodName) { Map<String, Object> stringJSONObjectMap = mockData.get(className); if (stringJSONObjectMap != null) { return stringJSONObjectMap.get(methodName); } return null; } }
字節(jié)碼操作類
package com.hiwei.test.agent; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import java.lang.instrument.ClassFileTransformer; import java.security.ProtectionDomain; public class DemoTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { ClassPool classPool = ClassPool.getDefault(); String realClassName = className.replaceAll("/", "."); //獲取類 if(MockConfig.isMock(realClassName)){ try { CtClass ctClass = classPool.get(realClassName); CtMethod[] declaredMethods = ctClass.getDeclaredMethods(); for (CtMethod m : declaredMethods) { String mName = m.getName(); Object mockData = MockConfig.getMockData(realClassName, mName); if(mockData!=null){ String mock = String.format("if(true){%s}", mockData); m.insertBefore(mock); } } return ctClass.toBytecode(); } catch (Throwable e) { e.printStackTrace(); } } return classfileBuffer; } }
配置文件:mock.json
{
"com.hiwei.test.DemoService":{ -- 類名
"add":"return 123;", -- 方法名:mock代碼
"delete":"return \"mock delete\";",
"getUser": "com.hiwei.test.User user2 = new com.hiwei.test.User();return user2;"
}
}
使用
配置參數(shù):
-javaagent:D:\person-project\java-agent\demo-agent\target\demo-agent-1.0-SNAPSHOT.jar=D:\person-project\java-agent\agent-test\mock.json
源碼鏈接:https://github.com/hiwei-zhang/java-agent
到此這篇關于Java Agent探針技術詳解示例的文章就介紹到這了,更多相關Java Agent內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring?this調(diào)用當前類方法無法攔截的示例代碼
這篇文章主要介紹了Spring?this調(diào)用當前類方法無法攔截,通過debug 查看這個proxyService1 和this的區(qū)別,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03SpringBoot借助spring.factories文件跨模塊實例化Bean
這篇文章主要介紹了SpringBoot借助spring.factories文件跨模塊實例化Bean,文章圍繞主題展開詳細的內(nèi)容介紹,需要的小伙伴可以參考一下2022-04-04mybatis-spring:@MapperScan注解的使用
這篇文章主要介紹了mybatis-spring:@MapperScan注解的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09Java?Mybatis使用resultMap時,屬性賦值順序錯誤的巨坑
這篇文章主要介紹了Java?Mybatis使用resultMap時,屬性賦值順序錯誤的巨坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01