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:
1.2, a pedra angular javaagent
java.lang.instrument
Ao 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-Class
atributos, o nome do caminho completo do valor do atributo de classe agente (pacote nome + nome da classe). classe agente deve implementar premain
métodos, premain
métodos e main
mé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 agentmain
funçã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 agentArgs
transmissã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 premain
o 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 -javaagent
parâmetros de inicialização. -javaagent
Pode ser usado várias vezes na linha de comando, iniciar vários agentes. premain
Chamando 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 premain
o escopo da função de trabalho, qualquer main
função pode fazer o trabalho, como a criação de um segmento, o premain
funçã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 main
mé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-Class
atributos, atributo é o nome completo da classe proxy. -
classe agente deve implementar
public static agentmain
mé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 premain
e agentmain
dois métodos, quando o agente inicia a linha de comando, JVM chama a premain
função, quando o agente começou após o início JVM, JVM chama a agentmain
função, e não JVM não chamar a premain
função.
agentmain
També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.
agentmain
Função deve completar toda a operação necessária inicialização Boot Agent, quando a inicialização for concluída, agentmain
a 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-Class
atributo 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.
agentmain
A 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 main
mé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-modules
opçã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 appendToBootstrapClassLoaderSearch
ou 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 getSystemClassLoader
as propriedades do sistema método especificado java.system.class.loader
), deve ser definido appendToSystemClassLoaderSearch
no especificado appendToClassPathForInstrumentation
mé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 premain
funçã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 ClassFileTransformer
da 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.MF
Arquivo gerado de duas maneiras: geração de configuração manual e automática, a configuração manual só precisa resources
criar o próximo arquivo META-INF/MENIFEST.MF
arquivo. 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:
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