Введение в Java Agent (1) - Инструментарий Введение и использование

JVM

Что вы можете сделать, изучив Java Agent?

  • Этот метод используется ломбоком, инструментом, который автоматически добавляет методы получения/установки.
  • Инструменты динамической диагностики, такие как btrace, Arthas и housemd, также используют инструментальные технологии.
  • HotSwap, Jrebel и т. Д. Intellij idea также являются одной из реализаций этой технологии.
  • Продукты Pinpoint, skywalking, newrelic, Tingyun APM и т. д. реализованы на основе Instrumentation.

Введение в инструментарий Java

Источник: NetEase Youdao Dictionary — Professional Definition — Computer Science and Technology
Инструментарий: английское определение в компьютерных науках и технологиях - инструментарий и имплантация.
Инструмент: Инструмент (инструмент относится к аппарату или оборудованию, используемому для обнаружения, измерения, наблюдения и расчета различных физических величин, компонентов материалов, физических параметров и т. д.)

Dynamic Instrumentation — это новая функция Java SE 5, котораяjava.lang.instrumentpackage, он освобождает функциональные возможности инструментов Java от собственного кода, позволяя решать проблемы так же, как это делает код Java. использоватьInstrumentation, разработчики могут создать независимый от приложения агент (агент) для мониторинга и помощи программам, работающим на JVM, и даже для замены и изменения определения определенных классов. С помощью такой функции разработчики могут реализовать более гибкий мониторинг виртуальных машин и операции класса Java.Эта функция фактически обеспечивает метод АОП, поддерживаемый на уровне виртуальной машины, поэтому разработчикам не нужно использовать исходное приложение.Внести какие-либо изменения, вы можете добиться динамической модификации и улучшения класса


java.lang.instrumentПакеты наделены расширенными функциями: мониторинг после загрузки, мониторинг собственного кода и динамические изменения.classpathи Т. Д. Эти изменения означают, что Java обладает более сильным динамическим контролем и возможностями интерпретации, что делает язык Java более гибким и изменчивым.


В Java SE6 самым большим изменением является возможность внедрения кода в исполняемую программу JVM. В Java SE 5,InstrumentНеобходимо использовать параметры командной строки или системные параметры для установки прокси-класса перед запуском.В фактическом запуске, когда виртуальная машина инициализируется (до загрузки большинства библиотек классов Java),instrumentationНастройки запущены, а в виртуальной машине установлена ​​функция обратного вызова, которая определяет загрузку определенного класса и выполняет собственно работу. Но во многих практических случаях у нас нет возможности установить прокси для виртуальной машины при ее запуске, что фактически ограничиваетinstrumentПриложения. Эту ситуацию изменили новые функции Java SE 6. С помощью метода attach в Java Tool API мы можем легко установить класс прокси-сервера загрузки динамически во время выполнения процесса для достиженияinstrumentationцель.


Кроме того, даnative methodИнструментарий также является совершенно новой функцией Java SE 6, которая делает невозможные ранее функциональные возможности в Java SE 6 за счетnative methodИнструментирование интерфейса осуществляется путем добавления одного или нескольких префиксов.

Инструментарий в Java SE 6 также добавляет возможность динамического добавления путей к классам. Эти новые функции делаютjava.lang.instrumentПакеты более многофункциональны, что делает язык Java более мощным.


java.lang.instrumentКонкретная реализация пакета зависит отJVMTI(Java Virtual Machine Tool Interface)Это набор собственных программных интерфейсов, предоставляемых виртуальной машиной Java для инструментов, связанных с JVM. JVMTI был представлен в Java SE 5. JVMTI предоставляет набор программных механизмов «агента», которые могут поддерживать сторонние инструментальные программы для подключения и доступа к JVM в форме прокси-сервера, а также использовать интерфейс программирования, предоставляемый JVMTI, для завершения многие функции, связанные с JVM. По факту,java.lang.instrumentРеализация пакета основана на этом механизме

существуетInstrumentationВ реализации имеетсяJVMTIагент, позвонивJVMTIФункции, связанные с классами Java, используются для выполнения динамических операций над классами Java.

КромеInstrumentationиз строя,JVMTIОн также предоставляет большое количество доступных функций для управления памятью виртуальной машины, управления потоками, манипулирования методами и переменными и т. д. оJVMTIДля получения подробной информации см.Документация TI по ​​Java SE 6 JVM

Базовое использование инструментов Java

Как реализовать инструменты в Java? Короче говоря, есть следующие шаги

  1. Создайте обычный класс со статическим методом premain(), имя метода является именем метода по умолчанию агента Java, он всегда будет выполняться перед основной функцией.

    package cn.jpsite.learning.javaagent01;
    
    import java.lang.instrument.Instrumentation;
    
    public class JpAgent {
        public static void premain(String agentArgs, Instrumentation instrumentation)  {
    
            /*转换发生在 premain 函数执行之后,main 函数执行之前,这时每装载一个类,transform 方法就会执行一次,看看是否需要转换,
            所以,在 transform(Transformer 类中)方法中,程序用 className.equals("TransClass") 来判断当前的类是否需要转换。*/
            // 方式一:
            System.out.println("我是两个参数的 Java Agent premain");
        }
        public static void premain(String agentArgs){
            System.out.println("我是一个参数的 Java Agent premain");
        }
    }
    
    

    2 как указано вышеpremain()метод, когда существуют методы premain() с 1 параметром и 2 параметрами,premain(String agentArgs)будет игнорироваться

  2. Создайте новый файл META-INF/MANIFEST.MF в каталоге ресурсов со следующим содержимым:

    Manifest-Version: 1.0
    Premain-Class: cn.jpsite.learning.javaagent01.JpAgent
    Can-Redefine-Classes: true
    Can-Retransform-Classes: true
    

    Доступ к вышеуказанному контенту можно получить через Maven'sorg.apache.maven.pluginsиmaven-assembly-pluginСотрудничество с подключаемым модулем завершено и создается во время установки mvn.MANIFEST.MF文件内容

  3. Структура каталогов такая

4. Выполнить в текущем проектеmvn clean installупакованный, сгенерированныйjpAgent.jarдокумент 5. Создайте новый проект maven example01, который содержит Main.java и Dog.java и, наконец, упакован вexample01-1.0-SNAPSHOT.jar public class Dog { public String say() { return "dog"; } } public class Main { public static void main(String[] args) { System.out.println("夜太黑"); System.out.println("----"+new Dog().say()); } }6. ВыполнитьjpAgent.jarнужно пройти-javaagentпараметр для указания пакета агента Java, >-javaagentКоличество этого параметра не ограничено, и вы можете указать более одного, и они будут выполняться в указанном порядке.После выполнения каждого агента будет выполняться основной метод основной программы.

```
// 为了执行方便,把jar文件放在同一层级目录下
java -javaagent:jpAgent.jar -cp example01-1.0-SNAPSHOT.jar  cn.jpsite.learning.Main
```
其中`example01-1.0-SNAPSHOT.jar`的`Main()`方法只是简单的输出2行内容,通过agent代理后多输出了一段内容。

метод addTransformer

Операцию файла класса Java можно понимать как операцию массива байтов (чтение двоичного потока байтов файла класса в массив байтов). Разработчики могутinterface ClassFileTransformerизtransformметод (через параметр classfileBuffer), работает и, наконец, возвращает определение класса (массив байтов), следующая демонстрацияtransformИспользование класса преобразования принимает простой метод замены файла класса.

  1. Недавно созданный JpClassFileTransformerDemo.java реализует интерфейс ClassFileTransformer, метод getBytesFromFile() считывает поток двоичных символов в соответствии с именем файла, а метод преобразования в ClassFileTransformer завершает замену определения класса.
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.instrument.ClassFileTransformer;
    import java.lang.instrument.IllegalClassFormatException;
    import java.security.ProtectionDomain;
    
    public class JpClassFileTransformerDemo implements ClassFileTransformer {
    
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            System.out.println("loader className: " + className);
            if (!className.equalsIgnoreCase("cn/jpsite/learning/Dog")) {
                return null;
            }
    
            return getBytesFromFile("D:\\learning\\Dog.class");
        }
    
        public static byte[] getBytesFromFile(String fileName) {
            File file = new File(fileName);
            try (InputStream is = new FileInputStream(file)) {
                // precondition
    
                long length = file.length();
                byte[] bytes = new byte[(int) length];
    
                // Read in the bytes
                int offset = 0;
                int numRead = 0;
                while (offset < bytes.length
                        && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                    offset += numRead;
                }
    
                if (offset < bytes.length) {
                    throw new IOException("Could not completely read file "
                            + file.getName());
                }
                is.close();
                return bytes;
            } catch (Exception e) {
                System.out.println("error occurs in _ClassTransformer!"
                        + e.getClass().getName());
                return null;
            }
        }
    }
    
  2. Добавлен JpAgent.javainstrumentation.addTransformer(new JpClassFileTransformerDemo());Содержание следующее, переупаковано
    public class JpAgent {
        public static void premain(String agentArgs, Instrumentation instrumentation)  {
    
            /*转换发生在 premain 函数执行之后,main 函数执行之前,这时每装载一个类,transform 方法就会执行一次,看看是否需要转换,
            所以,在 transform(Transformer 类中)方法中,程序用 className.equals("TransClass") 来判断当前的类是否需要转换。*/
            // 方式一:
           instrumentation.addTransformer(new JpClassFileTransformerDemo());
            System.out.println("我是两个参数的 Java Agent premain");
        }
        public static void premain(String agentArgs){
            System.out.println("我是一个参数的 Java Agent premain");
        }
    }
    
  3. На этом этапе мы переходим к модификации Dog.java в предыдущем проекте example01, заменяем строку «собака», возвращаемую методом say(), на «кошку», а затем компилируем ее в файл .class,getBytesFromFile("D:\\learning\\Dog.class")Является ли чтение модифицированного файла класса.
  4. воплощать в жизньjava -javaagent:jpAgent.jar -cp example01-1.0-SNAPSHOT.jar cn.jpsite.learning.MainПроверьте результаты следующим образом:

Преобразование происходит вpremainПосле выполнения функции, перед выполнением основной функции, каждый раз при загрузке классаtransformметод будет выполнен один раз, чтобы увидеть, требуется ли преобразование, поэтому вtransformметод, использованный здесьclassName.equals("cn/jpsite/learning/Dog")чтобы определить, нужно ли преобразовать текущий класс.

Помимо использования addTransformer, в Instrumentation есть еще один метод redefineClasses для реализации преобразования, указанного в premain. Использование аналогично, а именно:

ClassDefinition def = new ClassDefinition(Dog.class, Objects.requireNonNull(JpClassFileTransformerDemo
                .getBytesFromFile("D:\\learning\\Dog.class")));
instrumentation.redefineClasses(new ClassDefinition[] { def });

Расширенное чтение

Платформа анализа параметров виртуальной машины Java
java.lang.instrument api doc
Документация TI по ​​Java SE 6 JVM
Java SE 8 doc Java Attach API
Анализ исходного кода JavaAgent
Java Probe-Java Agent Technology-Ali Вопросы интервью
Реализуйте вызов нативного метода самостоятельно
Реализуйте вызов нативного метода самостоятельно 2

Обратите внимание, не потеряйтесь

Статья постоянно обновляется каждую неделю, вы можете найти «Десять минут на изучение программирования» на WeChat, чтобы прочитать и обновить ее как можно скорее.Если эта статья хорошо написана, если вы чувствуете, что есть что-то, чего можно желать ~ ставьте лайк 👍 подписывайтесь ❤️ поделитесь ❤️
Ваша поддержка и признание — самая большая мотивация для моего творчества, увидимся в следующей статье!