Instrumentation增强部分rt.jar中的类

类加载的步骤: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



    4.0.0
    com.paulzhangcc
    agent
    1.0-SNAPSHOT
    
        
            
                org.apache.maven.plugins
                maven-jar-plugin
                
                    
                        
                            src/main/resources/META-INF/MANIFEST.MF
                        
                    
                
            
        
    

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中的类)