简单使用java instrument包

蹲街弑〆低调 提交于 2020-08-06 20:36:02

解释: 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加载这个类的时候我们就能获取到这个属性的默认值。

深入使用还是得配置字节码注入框架。这个其实就是一个接口。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!