Java?Agent探針技術(shù)詳解示例
什么是java agent?
在JVM中運(yùn)行中,類是通過classLoader加載.class文件進(jìn)行生成的。在類加載加載.class文件生成對應(yīng)的類對象之前時(shí),我們可以通過修改.class文件內(nèi)容(就是字節(jié)碼修改技術(shù)),達(dá)到修改類的目的。JDK提供了對字節(jié)碼進(jìn)行操作的一系列api,而使用這些api開發(fā)出的程序就可以稱之為java agent。
java agent能做什么?
不修改目標(biāo)應(yīng)用達(dá)到代碼增強(qiáng)的目的,就好像spring的aop一樣,但是java agent是直接修改字節(jié)碼,而不是通過創(chuàng)建代理類。例如skywalking就是使用java agent技術(shù),為目標(biāo)應(yīng)用代碼植入監(jiān)控代碼,監(jiān)控代碼進(jìn)行數(shù)據(jù)統(tǒng)計(jì)上報(bào)的。這種方式實(shí)現(xiàn)了解耦,通用的功能。
文末附上了我寫的一款接口mock agent。感興趣的可以看看,學(xué)習(xí)一波。
使用示例
入門
創(chuàng)建maven工程
創(chuàng)建一個(gè)簡單的maven工程:

添加agent類:
package com.hiwei;
import java.lang.instrument.Instrumentation;
public class DemoAgent {
/**
* 該方法在main方法之前運(yùn)行
*/
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>
<!-- 對目標(biāo)類字節(jié)碼操作權(quán)限 -->
<Can-Redefine-Classes>${can.redefine.classes}</Can-Redefine-Classes>
<!-- 對目標(biāo)類字節(jié)碼操作權(quán)限 -->
<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

運(yùn)行測試類:

進(jìn)階(一款接口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方法之前運(yùn)行
* @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
到此這篇關(guān)于Java Agent探針技術(shù)詳解示例的文章就介紹到這了,更多相關(guān)Java Agent內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring?this調(diào)用當(dāng)前類方法無法攔截的示例代碼
這篇文章主要介紹了Spring?this調(diào)用當(dāng)前類方法無法攔截,通過debug 查看這個(gè)proxyService1 和this的區(qū)別,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
SpringBoot借助spring.factories文件跨模塊實(shí)例化Bean
這篇文章主要介紹了SpringBoot借助spring.factories文件跨模塊實(shí)例化Bean,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,需要的小伙伴可以參考一下2022-04-04
mybatis-spring:@MapperScan注解的使用
這篇文章主要介紹了mybatis-spring:@MapperScan注解的使用,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
kill命令在Java應(yīng)用中使用的注意事項(xiàng)小結(jié)
這篇文章主要給大家介紹了關(guān)于kill命令在Java應(yīng)用中使用的注意事項(xiàng),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
Java?Mybatis使用resultMap時(shí),屬性賦值順序錯(cuò)誤的巨坑
這篇文章主要介紹了Java?Mybatis使用resultMap時(shí),屬性賦值順序錯(cuò)誤的巨坑,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01

