解释: ClassFileTransformer接口,定义一个类文件转换器。接口中的transform()方法会在类文件被加载时调用,而在transform方法里,我们可以利用上文中的ASM或Javassist对传入的字节码进行改写或替换,生成新的字节码数组后返回。
在jar包前加 java -javaagent:{agent路径}/agent.jar -jar xxx.jar
例如我们想获取akka.http.scaladsl.server.RequestContextImpl类的这个属性request的默认值
项目结构:
代码:
maven3.5.xml
<?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>com.zxing</groupId>
<artifactId>agentdemo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<asm.version>5.0.3</asm.version>
</properties>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>${asm.version}</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>${asm.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<!-- <version>3.0.0</version>-->
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<!--修改为你自己的类名全路径-->
<Premain-Class>com.hxm.agent.App</Premain-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
ReflectUtil.java
package com.hxm;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 反射工具
*/
public class ReflectUtil {
/**
* 获取当前类的所有字段,包含public,protected和private类型
* @param clazz
* @param name
* @return
*/
public static Field getDeclaredField(Class<?> clazz, String name) {
if(clazz != null && name != null) {
try {
Field fieldFromCache = clazz.getDeclaredField(name);
if(!fieldFromCache.isAccessible()){
fieldFromCache.setAccessible(true);
}
return fieldFromCache;
} catch(Exception e) {
System.out.format("getDeclaredField class = %s, name = %s, ERROR --> %s", clazz, name);
}
}
return null;
}
/**
* 获取当前类和从基类继承的,从接口实现的所有public字段
* @param clazz
* @param name
* @return
*/
public static Field getField(Class<?> clazz, String name) {
if(clazz != null && name != null) {
try {
Field fieldFromCache = clazz.getField(name);
if(!fieldFromCache.isAccessible()){
fieldFromCache.setAccessible(true);
}
return fieldFromCache;
} catch(Exception e) {
System.out.format("getField class = %s, name = %s, ERROR --> %s", clazz, name);
}
}
return null;
}
/**
* 获取当前类和从基类继承的,从接口实现的所有字段,包含public,protected和private类型
* @param clazz
* @param name
* @return
*/
public static Field getAnyField(Class<?> clazz, String name) {
if(clazz != null && name != null) {
try {
Field fieldFromCache = clazz.getDeclaredField(name);
if(!fieldFromCache.isAccessible()){
fieldFromCache.setAccessible(true);
}
return fieldFromCache;
} catch (Exception e) {
if (clazz.getSuperclass() != Object.class && clazz.getSuperclass() != null) {
return getAnyField(clazz.getSuperclass(), name);
} else {
System.out.format("getAnyField [%s], name [%s], Exception [%s]", clazz, name);
return null;
}
}
}
return null;
}
/**
* 获取当前类的所有方法,包含public,protected和private类型
* @param clazz
* @param name
* @return
*/
public static Method getDeclaredMethod (Class<?> clazz, String name) {
return getDeclaredMethod(clazz, name, null);
}
public static Method getDeclaredMethod (Class<?> clazz, String name, Class<?>[] parameterTypes) {
if(clazz != null && name != null) {
try {
Method methodFromCache = clazz.getDeclaredMethod(name, parameterTypes);
if(!methodFromCache.isAccessible()) {
methodFromCache.setAccessible(true);
}
return methodFromCache;
} catch (Exception e) {
System.out.format("getDeclaredMethod class = %s, name = %s, ERROR --> %s", clazz, name);
}
}
return null;
}
/**
* 获取当前类和从基类继承的,从接口实现的所有public方法
* @param clazz
* @param name
* @return
*/
public static Method getMethod (Class<?> clazz, String name) {
return getMethod(clazz, name, null);
}
public static Method getMethod(Class<?> clazz, String name, Class<?>[] parameterTypes) {
if(clazz != null && name != null) {
try {
Method methodFromCache = clazz.getMethod(name, parameterTypes);
if(!methodFromCache.isAccessible()) {
methodFromCache.setAccessible(true);
}
return methodFromCache;
} catch (Exception e) {
System.out.format("getDeclaredMethod class = %s, name = %s, ERROR --> %s", clazz, name);
}
}
return null;
}
/**
* 获取当前类和从基类继承的,从接口实现的所有方法,包含public,protected和private类型
* @param clazz
* @param name
* @return
*/
public static Method getAnyMethod (Class<?> clazz, String name) {
return getAnyMethod(clazz, name, null);
}
public static Method getAnyMethod(Class<?> clazz, String name, Class<?>[] parameterTypes) {
if(clazz != null && name != null) {
try {
//这里将getmethod修改为了getDeclaredMethod
Method methodFromCache = clazz.getDeclaredMethod(name, parameterTypes);
if(!methodFromCache.isAccessible()) {
methodFromCache.setAccessible(true);
}
return methodFromCache;
} catch (Exception e) {
for (Class<?> icl : clazz.getInterfaces()) {
Method m = getAnyMethod(icl, name, parameterTypes);
if (m != null) {
return m;
}
}
Class<?> mcl = clazz;
while ((mcl = mcl.getSuperclass()) != null) {
Method m = getAnyMethod(mcl, name, parameterTypes);
if (m != null) {
return m;
}
}
System.out.format("getAnyMethod class = %s, name = %s, ERROR --> %s", clazz, name);
}
}
return null;
}
/**
* 通过反射获取字段保存的值
* @param obj
* @param field
* @return
*/
public static Object getField(Object obj, Field field) {
if(obj != null && field != null) {
if(!field.isAccessible()) {
field.setAccessible(true);
}
try {
return field.get(obj);
} catch (Exception e) {
e.printStackTrace();
System.out.format("getField Object = %s, Field = %s, exception --> %s", obj, field);
}
}
return null;
}
/**
* 通过反射设置字段保存的值
* @param obj
* @param field
* @param value
* @return
*/
public static void setField(Object obj, Field field, Object value) {
if(obj != null && field != null) {
if(!field.isAccessible()) {
field.setAccessible(true);
}
try {
field.set(obj, value);
} catch (Exception e) {
e.printStackTrace();
System.out.format("setField Object = %s, Field = %s, exception --> %s", obj, field);
}
}
}
/**
* 通过反射设置字段保存的值
* @param obj
* @param fieldName
* @param value
*/
public static void setField(Object obj, String fieldName, Object value) {
// TODO:
}
/**
* 通过反射调用方法
* @param obj
* @param method
* @param args
* @return
*/
public static Object invokeMethod(Object obj, Method method, Object[] args) {
if(obj != null && method != null) {
if(!method.isAccessible()) {
method.setAccessible(true);
}
try {
if(args == null){
return method.invoke(obj);
}
return method.invoke(obj, args);
} catch (Exception e) {
e.printStackTrace();
System.out.format("invokeMethod Object = %s, Method = %s, exception --> %s", obj, method);
}
}
return null;
}
// 针对每个插件做缓存,缓存方法
public static Method getMethodFromCache(Map<String, Method> methodMap, Object object, String methodName) {
return getMethodFromCache(methodMap, object, methodName, null);
}
/**
* 针对每个插件做缓存,缓存方法
* @param methodMap
* @param object
* @param methodName
* @param parameterTypes
* @return
*/
public static Method getMethodFromCache(Map<String, Method> methodMap, Object object, String methodName, Class<?>[] parameterTypes) {
if(object==null){
return null;
}
String className = object.getClass().getName();
String methodId = className + methodName;
Method method = methodMap.get(methodId);
if(method == null) {
method = ReflectUtil.getAnyMethod(object.getClass(), methodName, parameterTypes);
methodMap.put(methodId, method);
}
return method;
}
/**
* 针对每个插件做缓存,缓存字段
* @param fieldMap
* @param object
* @param fieldName
* @return
*/
public static Field getFieldFromCache(Map<String, Field> fieldMap, Object object, String fieldName) {
String className = object.getClass().getName();
String fieldId = className + fieldName;
Field field = fieldMap.get(fieldId);
if(field == null) {
field = ReflectUtil.getAnyField(object.getClass(), fieldName);
if(field==null)return null;
fieldMap.put(fieldId, field);
}
return field;
}
}
App.java
package com.hxm.agent;
import java.lang.instrument.Instrumentation;
public class App {
public static void premain(String agentOps, Instrumentation inst) {
System.out.println("=========premain方法执行========");
System.out.println(agentOps);//-javaagent:xxxagent.jar=后携带的参数
// 添加Transformer
inst.addTransformer(new FirstAgent());
}
}
FirstAgent.java
package com.hxm.agent;
import com.hxm.ReflectUtil;
//import javassist.ClassPool;
//import javassist.CtClass;
//import javassist.CtMethod;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class FirstAgent implements ClassFileTransformer {
private static ConcurrentHashMap<String, Field> fieldMap = new ConcurrentHashMap<String, Field>();
private Map<String, Method> methodMap = new ConcurrentHashMap<String, Method>();
public final String //injectedClassName = "com.zxing.agentclient.App";
injectedClassName = "akka.http.scaladsl.server.RequestContextImpl";
public final String //methodName = "hello";
methodName = "complete";
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)) {
System.out.println("找到了"+className);
// CtClass ctclass = null;
try {
// akka.http.scaladsl.server.RequestContextImpl$$anonfun$complete$2
Class<?> ctclass = ClassLoader.getSystemClassLoader().loadClass(className);
// ctclass = ClassPool.getDefault().get(className);// 使用全称,用于取得字节码类<使用javassist>
// CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 得到这方法实例
// ctmethod.insertBefore("System.out.println(11111111);");//插入一行代码
System.out.format("AkkaHttpReqProcessor this is=%s ", ctclass);
// Method getTransportMethod = ReflectUtil.getMethodFromCache(methodMap, ctclass, methodName);
//
// if (getTransportMethod != null) {
// Object transport = getTransportMethod.invoke(ctclass);
// //2017-04-26 11:19:22 INFO Catalina ---transport->org.apache.thrift.transport.TSocket@149a0d40
// System.out.format("AkkaHttpReqProcessor transport is=%s .", transport);
//
// }
final Field request = ReflectUtil.getFieldFromCache(fieldMap, ctclass, "request");
System.out.format("AkkaHttpReqProcessor request is=%s ", request);
Object requestvalue = ReflectUtil.getField(ctclass, request);
System.out.println(requestvalue);
// return ctclass.toBytecode();
return null;
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
return null;
}
}
当jvm加载这个类的时候我们就能获取到这个属性的默认值。
深入使用还是得配置字节码注入框架。这个其实就是一个接口。
来源:oschina
链接:https://my.oschina.net/u/3730149/blog/4410722