Realização de um JavaAgent simples

Primeiro, o que é javaagent

javaagent é uma JVM "plug-in", um arquivo .jar especialmente criado, ele pode tirar proveito Instrumentação API JVM fornece.

1.1, Visão geral

Agente Java é constituída por três partes: a classe de proxy, a meta informao e mecanismos de carregamento de classes de proxy e agente JVM jar, os conteúdos inteiros como mostrado abaixo:
Aqui Insert Picture Descrição

1.2, a pedra angular javaagent

java.lang.instrumentAo modificar o método de programa de bytecode forma javaagent em execução nos serviços de JVM. javaagent implementado como um pacote JAR, especificar os atributos para ser carregado ficheiro de manifesto de proxy classe JAR para iniciar o agente.

javaagent começar das seguintes formas:

  • Comece especificando parâmetros na linha de comando.

  • Iniciar JVM começou. Por exemplo, para fornecer uma ferramenta que pode ser ligado para a aplicação em execução, e permite que os agentes de carga em que o pedido tenha sido executado.

  • Empacotado como um arquivo executável com o aplicativo.

1.3, javaagent início

1.3.1, a linha de comando para iniciar

linha de comando para iniciar os seguintes parâmetros:

-javaagent:<jarpath>[=<options>]

<jarpath>: Javaagent caminho, por exemplo /opt/var/Agent-1.0.0.jar.
<options>: Parâmetros Javaagent, parâmetros analíticos javaagent responsável.
arquivo de manifesto JAR javaagent deve conter Premain-Classatributos, o nome do caminho completo do valor do atributo de classe agente (pacote nome + nome da classe). classe agente deve implementar premainmétodos, premainmétodos e mainmétodos são os mesmos que o ponto de entrada e o proxy de aplicação. Em primeiro lugar, a função de chamar premain agente após JVM inicialização está completa, e em seguida, chamar a função principal da aplicação, depois método premain deve retornar o processo para começar.

premain Método de assinatura da seguinte forma:

public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)

JVM primeiras tentativas para chamar a assinatura do método no proxy, se a classe agente não implementar a assinatura do método, tentativas de assinatura JVM para invocar um método 2:

Aulas de teatro pode ter uma agentmainfunção, a chamada de função terá início após a conclusão do JVM. Se você usar a linha de comando para iniciar o agente,agentmain o caminho não será chamado.

Todos os parâmetros são tratados como agentes de uma cadeia de agentArgstransmissão variável, o agente responsável pela análise da cadeia de parâmetros.

Se o agente de classe porque o agente não pode ser carregada, a classe de agente não implementa premaino método gera uma exceção ou não capturado, JVM vai sair.

aplicação início javaagent não é obrigada a fornecer uma determinada linha de comando, se o suporte alcançado a partir da linha de comando para iniciar, a implementação deve suportar a linha de comando, especificando os -javaagentparâmetros de inicialização. -javaagentPode ser usado várias vezes na linha de comando, iniciar vários agentes. premainChamando funções de sequência e na ordem especificada na linha de comando é consistente, pode ser utilizada a mesma pluralidade de agentes <jarpath>.

Não é um modelo rigoroso de definir premaino escopo da função de trabalho, qualquer mainfunção pode fazer o trabalho, como a criação de um segmento, o premainfunção é legal.

1.3.2, a JVM inicialização de inicialização

A implementação pode fornecer um mecanismo de re-iniciar o agente após a inicialização JVM. Como iniciar os detalhes específicos de implementação do agente, geralmente foi lançado o aplicativo e seus mainmétodos têm sido chamados. Se o agente apoia a implementação de inicialização JVM iniciado, o agente deve seguir os seguintes critérios:

  • O arquivo de manifesto contém Agent-Classatributos, atributo é o nome completo da classe proxy.

  • classe agente deve implementar public static agentmainmétodo.

método de assinatura agentmain tem as duas funções seguintes:

public static void agentmain(String agentArgs, Instrumentation inst)
public static void agentmain(String agentArgs)

JVM primeiras tentativas de chamar a assinatura do método tem 1, se a classe agente não implementar este método, as tentativas de JVM para invocar a assinatura do método 2.

aulas de teatro podem ser atingidos simultaneamente premaine agentmaindois métodos, quando o agente inicia a linha de comando, JVM chama a premainfunção, quando o agente começou após o início JVM, JVM chama a agentmainfunção, e não JVM não chamar a premainfunção.

agentmainTambém através dos parâmetros da função de transferência agentArgs, todos os parâmetros são combinados em uma cadeia, o agente responsável por os parâmetros de resolução.

agentmainFunção deve completar toda a operação necessária inicialização Boot Agent, quando a inicialização for concluída, agentmaina função deve retornar. Se o agente não pode ser iniciado ou lançar uma exceção não capturada, JVM sairá.

1.3.3, empacotado como um arquivo executável

Se o agente é empacotado em um arquivo JAR executável, uma lista de arquivo JAR executável deve conter o Launcher-Agent-Classatributo que especifica um proxy na aplicação antes do início da função principal chama a classe. JVM tenta chamar o método no proxy:

public static void agentmain(String agentArgs, Instrumentation inst)

Se a classe de agente não implementar o método acima, seguindo método JVM é chamado.

public static void agentmain(String agentArgs)

agentArgs Parâmetro deve ser a string vazia.

agentmainA função de inicialização deve completar todas as ações que o agente deve iniciar e retornar após o arranque. Se o agente não iniciar ou lança uma exceção não capturada, JVM sairá.

1.3.4, classe carga de agente módulo e disponível proxy de classe / classe

carregador de classe sistema é responsável por carregar todos os arquivos JAR o agente de aulas, e tornou-se um membro do carregador de classe sistema de módulos sem nome. O sistema de aplicação de carregador de classe também tipicamente definido contém mainmétodos de classe. Todas as classes de classes proxy são visíveis para o carregador de classe sistema visível, você deve atender aos seguintes requisitos mínimos:

  • módulo camada Iniciar derivado classes no pacote. A partir camada contém todo o modo inicial de arranque, dependendo da plataforma módulo ou módulo de aplicação.

  • Classe pode ser definida como o carregador de classe sistema.

  • Todas as classes de proxy iniciar módulo carregador de classe definido seus membros sem nome.

Se você precisa de link para uma plataforma camada de classe de proxy (ou outro) módulo de classe não começa, você precisa garantir que esses módulos estão localizados em camadas começando maneira de iniciar a aplicação. Por exemplo, a implementação JDK, --add-modulesopção de linha de comando pode ser usado para adicionar módulos a serem resolvidas no módulo de raiz concentrado começar.

Iniciar carregador de classe pode carregar classes de suporte de proxy (por appendToBootstrapClassLoaderSearchou designados atributo Boot-Class-Path), só deve ligar para a classe carregador de classe de inicialização personalizado. Não podemos garantir o carregador de classe de inicialização pode funcionar em todas as plataformas.

Se o carregador de classe sistema personalizado (configurado por getSystemClassLoaderas propriedades do sistema método especificado java.system.class.loader), deve ser definido appendToSystemClassLoaderSearchno especificado appendToClassPathForInstrumentationmétodo. Em outras palavras, a classe sistema de carregador personalizado deve suportar a adição dos arquivos JAR agente para o mecanismo de carregador de classe sistema dentro do intervalo de pesquisa.

1.4, javaagent da Lista de Propriedades

propriedade explicação são obrigatórios O valor padrão
Premain-Classe método de classe compreendendo Premain forma Start-dependente não
Agent-Class O método compreende um agentmain classe forma Start-dependente não
Boot-Class-Path Iniciar carregador de classe caminho de pesquisa não não
Can-Redefine-Classis Posso re-definir a classe de proxy necessária não falso
Can-Retransformar-Classis Você é capaz de re-converter as classes necessárias por este agente não falso
Can-Set-Nativo-Method-Prefixo Este agente pode ser fornecida, se desejado método prefixo nativa não falso

Em segundo lugar, escrever um Agente Java

Com base na descrição acima, podemos alcançar um javaagent JVM todas as classes não são do sistema download.

Durante todo o processo de desenvolvimento inclui os seguintes três passos:

  • 1) a definição da classe de proxy, o download classe de implementação;

  • 2) o pacote de configuração;

  • 3) A linha de comando para iniciar o teste.

2.1, os implementos classe de proxy

implementar premainfunção

package io.ct.java.agent;

import java.lang.instrument.Instrumentation;

public class AgentApplication {
    public static void premain(String arg, Instrumentation instrumentation) {
        System.err.println("agent startup , args is " + arg);
        // 注册我们的文件下载函数
        instrumentation.addTransformer(new DumpClassesService());
    }
}

implementos Download de classe ClassFileTransformerda interface, quando a classe é carregada baixado bytecode classes:

package io.ct.java.agent;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.List;

/**
 * Copyright (C), 2018-2018, open source
 * FileName: DumpClassesService
 *
 * @author : 大哥
 * Date:     2018/12/8 21:01
 */
public class DumpClassesService implements ClassFileTransformer {
    private static final List<String> SYSTEM_CLASS_PREFIX = Arrays.asList("java", "sum", "jdk");

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (!isSystemClass(className)) {
            System.out.println("load class " + className);
            FileOutputStream fos = null;
            try {
                // 将类名统一命名为classNamedump.class格式
                fos = new FileOutputStream(className + "dump.class");
                fos.write(classfileBuffer);
                fos.flush();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            } finally {
                // 关闭文件输出流
                if (null != fos) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return classfileBuffer;
    }

    /**
     * 判断一个类是否为系统类
     *
     * @param className 类名
     * @return System Class then return true,else return false
     */
    private boolean isSystemClass(String className) {
        // 假设系统类的类名不为NULL而且不为空
        if (null == className || className.isEmpty()) {
            return false;
        }

        for (String prefix : SYSTEM_CLASS_PREFIX) {
            if (className.startsWith(prefix)) {
                return true;
            }
        }
        return false;
    }
}

2.2, MANIFEST.MF configuração

MANIFEST.MFArquivo gerado de duas maneiras: geração de configuração manual e automática, a configuração manual só precisa resourcescriar o próximo arquivo META-INF/MENIFEST.MFarquivo. Manualmente removendo a configuração externa pode ser gerado automaticamente usando fase de embalagem tampão perito, o perito do plug-in configuração como se segue:

             <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>io.ct.java.agent.AgentApplication</Premain-Class>
                            <Agent-Class>io.ct.java.agent.AgentApplication</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>

Gerando um frasco seguinte formato:
Aqui Insert Picture Descrição
em que arquivo MANIFEST.MF segue (gerando um ficheiro de configuração diferente conteúdo não é exactamente):

Manifest-Version: 1.0
Implementation-Title: agent
Premain-Class: io.ct.java.agent.AgentApplication
Implementation-Version: 0.0.1-SNAPSHOT
Built-By: chentong
Agent-Class: io.ct.java.agent.AgentApplication
Can-Redefine-Classes: true
Implementation-Vendor-Id: io.ct.java
Can-Retransform-Classes: true
Created-By: Apache Maven 3.5.4
Build-Jdk: 1.8.0_171
Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo
 ot-starter-parent/agent

2.3, a linha de comando para iniciar o Java Agent

Execute o seguinte comando para executar a classe já compilado Olá, pode gerar um arquivo chamado Hellodump.class no mesmo diretório.

java -javaagent:agent-0.0.1-SNAPSHOT.jar Hello
Publicado 100 artigos originais · Louvor obteve 45 · vista 640 000 +

Acho que você gosta

Origin blog.csdn.net/wangzhongshun/article/details/100287986
Recomendado
Clasificación