美文网首页
Instrumentation增强部分rt.jar中的类

Instrumentation增强部分rt.jar中的类

作者: 全都是泡沫啦 | 来源:发表于2018-11-12 14:38 被阅读0次

    类加载的步骤:1.加载 2.校验 3.准备 4.解析(不固定:对于动态调用可能在初始化后解析,例如多态的实现,java8的lambda语法) 5.初始化 6.使用 7.卸载

    Instrumentation原理:在类加载器加载过程中对class文件流进行拦截替换,
    Instrumentation提供了获取对象大小的方法:getObjectSize

    META-INF/MANIFEST.MF文件内容

    Manifest-Version: 1.0
    Premain-Class: com.paulzhangcc.InstrumentationHolder
    Can-Redefine-Classes: true
    Can-Retransform-Classes: true
    

    pom.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.paulzhangcc</groupId>
        <artifactId>agent</artifactId>
        <version>1.0-SNAPSHOT</version>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <configuration>
                        <archive>
                            <manifestFile>
                                src/main/resources/META-INF/MANIFEST.MF
                            </manifestFile>
                        </archive>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    
    package com.paulzhangcc;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.lang.instrument.ClassFileTransformer;
    import java.lang.instrument.IllegalClassFormatException;
    import java.lang.instrument.Instrumentation;
    import java.security.ProtectionDomain;
    import java.util.Properties;
    
    /**
     * @author paul
     * @description
     * @date 2018/8/7
     */
    public class InstrumentationHolder {
        public static final String default_config_path = "/opt/java-agent.properties";
        public static Instrumentation instrumentation = null;
        public static void premain(String agentArgs, Instrumentation instrumentationTemp) {
            instrumentation = instrumentationTemp;
            System.out.println("[premain][init ]:agentArgs=" + agentArgs + ",instrumentationName="+instrumentationTemp.getClass().getName());
            Class[] allLoadedClasses = instrumentationTemp.getAllLoadedClasses();
            for (Class _class:allLoadedClasses){
                System.out.println("[premain][LoadedClasses]:className="+_class.getName());
            }
            Properties properties = new Properties();
            try {
                if (agentArgs != null && agentArgs.length() != 0) {
                    System.out.println("[premain][read ]:config_path="+agentArgs);
                    properties.load(new FileInputStream(agentArgs));
                } else {
                    System.out.println("[premain][read ]:default_config_path="+default_config_path);
                    properties.load(new FileInputStream(default_config_path));
                }
    
            } catch (Exception e) {
                if (e instanceof FileNotFoundException) {
                    try {
                        properties.load(new FileInputStream(default_config_path));
                    } catch (IOException e1) {
                        System.out.println("[premain][error]:how to use:java -javaagent:{1}={2}  {1} is agent jar ,{2} is conf properties , default {2} is /opt/java-agent.properties");
                        System.out.println("[premain][error]:/opt/java-agent.properties for example sun/security/util/HostnameChecker=C:/Users/paul/Desktop/HostnameChecker.class");
                    }
                } else {
                    e.printStackTrace();
                }
            }
            if (properties.isEmpty()){
                System.out.println("[premain][info ]:config properties is empty,so do not transform Class");
                return;
            }
            instrumentationTemp.addTransformer(new ClassFileTransformer() {
                @Override
                public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                    //className 对于类     :java/lang/Void
                    //className 对于内部类 :java/lang/Class$MethodArray
                    String property = properties.getProperty(className);
                    if (property != null) {
                        byte[] fileBytes = getFileBytes(property);
                        if (fileBytes != null) {
                            System.out.println("[premain][replace]:className=" + className + ",fileName="+property);
                            return fileBytes;
                        }
                    }
                    return null;
                }
            }, true);
        }
    
        public static byte[] getFileBytes(String fileName) {
            try {
                File file = new File(fileName);
                if (!file.exists()) {
                    return null;
                }
                long fileSize = file.length();
                FileInputStream fi = new FileInputStream(file);
                byte[] buffer = new byte[(int) fileSize];
                int offset = 0;
                int numRead = 0;
                while (offset < buffer.length
                        && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {
                    offset += numRead;
                }
                if (offset != buffer.length) {
                    return null;
                }
                fi.close();
                return buffer;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    

    测试类

    import java.lang.instrument.Instrumentation;
    import java.lang.reflect.Field;
    
    /**
     * @author paul
     * @description
     * @date 2018/11/12
     */
    public class Test {
    
        public static Instrumentation getInstrumentation(){
            try {
                Class<?> aClass = Class.forName("com.paulzhangcc.InstrumentationHolder");
                Field instrumentation = aClass.getField("instrumentation");
                return (Instrumentation) instrumentation.get(null);
            }catch (Exception e){
            }
            return null;
        }
        public static void main(String[] args) throws Exception {
            Instrumentation instrumentation = getInstrumentation();
            if (instrumentation != null){
                //查看Test对象的大小
                long objectSize = instrumentation.getObjectSize(new Test());
                System.out.println("Object Test size is "+objectSize +" Byte");
            }
        }
    }
    

    运行Test时:java -javaagent:agent-1.0-SNAPSHOT.jar=/test/conf.properties Test
    /test/conf.properties配置类似如下:(注意String类无法覆盖由于系统在使用Instrumentation前就已经加载了String类,日志中:[premain][LoadedClasses]的类提前加载到内存都无法进行覆盖)

    java/lang/String=/opt/class/String.class   #只作为格式参考
    

    对于提前加载的类可以使用Instrumentation#redefineClasses进行修改,但是有他的局限性
    1.不允许新增加field/method 2.正在跑的函数,没有退出不能生效

    相关文章

      网友评论

          本文标题:Instrumentation增强部分rt.jar中的类

          本文链接:https://www.haomeiwen.com/subject/mfxcfqtx.html