1、Java Agent

通过Java Agent,生成一种特殊的jar包(一种工具),业务程序可以主动去调用jar包里的方法。比如下面这个有打印内存情况功能的agent:

在这里插入图片描述

2、两种加载模式

让Java程序执行Java Agent程序中的代码,实现方式:

静态加载模式

在Java程序开始执行前,先执行Agent的代码。在Java Agent的项目中编写一个premain的方法,并打包成jar包:

//premain,即在用户的main方法执行之前
public static void premain(string agentArgs, Instrumentation inst)

如果自己的程序是test.jar,可执行:

java -javaagent:./agent.jar -jar test.jar

如此,JVM将会加载agent中的代码去执行。premain方法会在主线程中执行:

在这里插入图片描述

动态加载模式

连接用户的Java进程,随时让Java Agent的代码执行,适用于Arthas类似的诊断工具。实现动态加载模式,需要在Java Agent项目中编写一个agentmain方法,并打成jar包:

public static void agentmain(string agentArgs, Instrumentation inst)

以下代码可以让Java Agent的代码在指定的Java进程中执行:

//动态连接到24200进程ID的java程序
VirtualMachine vm = VirtualMachine.attach("24200");

//加载java agent的jar并执行
vm.loadAgent("jvm-java-agent-jar-with-dependencies.jar");

如此,Java Agent的jar就会被执行。和静态加载的permain不同,agentmain方法的执行是在一个单独的线程里:

在这里插入图片描述

一点想法:感觉AOP和Java Agent的区别就是:前者是开发者在自己的程序里写的,且增强的时机更灵活。后者则独立于开发者自己的程序(无侵入性),能适用于所有的Java程序。且AOP与Spring框架耦合,没了Spring就不能用,也无法做到随时开启和停用。

一定程度上,可以说,AOP常常独立于业务代码,加一些公共逻辑,但Java Agent是独立于你的项目、你的jar包

3、静态加载模式实现

  • 创建Java Agent的maven项目,添加maven-assembly-plugin插件,以便打出java agent的jar包
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <configuration>
                <!--将所有的依赖都打入同一个jar包中,配置了这个,以后打的Agent包,既有我写的Agent代码,也有上面引入的依赖的代码-->
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <!--指定java agent相关的配置文件-->
                <archive>
                    <manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

  • 定义个类,写premain方法,写代理要执行逻辑
public class AgentMain {

    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("premain方法执行了...");
    }

}
  • 编写MANIFEST.MF文件,写Java Agent的属性
Manifest-Version: 1.0
Premain-Class: com.llg.AgentMain
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
  • 使用maven-assembly-plugin进行打包

在这里插入图片描述

  • 让另一个普通的Java程序,-javaagent启动,静态代理成功:

在这里插入图片描述

4、动态加载的实现

  • 创建Java Agent的maven项目,添加maven-assembly-plugin插件,以便打出java agent的jar(同上)
  • 定义个类,写agentmain方法,写代理要执行逻辑
public class AgentMain {

    public static void agentmain(String agentArgs,Instrumentation inst){
        System.out.println("agentmain执行了...");
    }

}
  • 编写MANIFEST.MF文件,写Java Agent的属性
Manifest-Version: 1.0
Agent-Class: com.llg.AgentMain
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
  • 使用maven-assembly-plugin进行打包(同上)
  • 启动普通的Java程序
  • 写个main方法,动态连接到运行中的java程序并加载执行Java Agent的Jar,实现动态代理(以后这个main方法就可以写在工具里,让用户只输入一个自己Java进程的PID,帮用户把传入到这个main,动态代理)
public class Test {
    public static void main(String[] args) throws  Exception {
    	//传入用户Java进程的PID,实现连接
        VirtualMachine vm = VirtualMachine.attach("45627");
        //执行Java Agent的里的agentmain方法
        vm.loadAgent("D:\\jmh2\\llg-agent\\target\\llg-agent-1.0-SNAPSHOT-jar-with-dependencies.jar");

    }
}
  • 效果:

在这里插入图片描述

Logo

Agent 垂直技术社区,欢迎活跃、内容共建,欢迎商务合作。wx: diudiu5555

更多推荐