Instrumentation增強(qiáng)部分rt.jar中的類(lèi)

類(lèi)加載的步驟:1.加載 2.校驗(yàn) 3.準(zhǔn)備 4.解析(不固定:對(duì)于動(dòng)態(tài)調(diào)用可能在初始化后解析,例如多態(tài)的實(shí)現(xiàn),java8的lambda語(yǔ)法) 5.初始化 6.使用 7.卸載

Instrumentation原理:在類(lèi)加載器加載過(guò)程中對(duì)class文件流進(jìn)行攔截替換,
Instrumentation提供了獲取對(duì)象大小的方法:getObjectSize

META-INF/MANIFEST.MF文件內(nèi)容

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 對(duì)于類(lèi)     :java/lang/Void
                //className 對(duì)于內(nèi)部類(lèi) :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;
    }
}

測(cè)試類(lèi)

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對(duì)象的大小
            long objectSize = instrumentation.getObjectSize(new Test());
            System.out.println("Object Test size is "+objectSize +" Byte");
        }
    }
}

運(yùn)行Test時(shí):java -javaagent:agent-1.0-SNAPSHOT.jar=/test/conf.properties Test
/test/conf.properties配置類(lèi)似如下:(注意String類(lèi)無(wú)法覆蓋由于系統(tǒng)在使用Instrumentation前就已經(jīng)加載了String類(lèi),日志中:[premain][LoadedClasses]的類(lèi)提前加載到內(nèi)存都無(wú)法進(jìn)行覆蓋)

java/lang/String=/opt/class/String.class   #只作為格式參考

對(duì)于提前加載的類(lèi)可以使用Instrumentation#redefineClasses進(jìn)行修改,但是有他的局限性
1.不允許新增加field/method 2.正在跑的函數(shù),沒(méi)有退出不能生效

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容