• Java Agent入门


    介绍

    在JDK1.5以后,我们可以使用agent技术构建一个独立于应用程序的代理程序(即为Agent),用来协助监测、运行甚至替换其他JVM上的程序。使用它可以实现虚拟机级别的AOP功能。Agent分为两种,一种是在主程序之前运行的Agent,一种是在主程序之后运行的Agent(前者的升级版,1.6以后提供)。

    使用

    主程序运行之前的代理程序

    创建代理类
    public class MyPreMainAgent {
    
    //方法名和参数都是固定的 premain表示在主程序运行之前运行
      public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("PreMain start");
        System.out.println(agentArgs);
        System.out.println(inst);
      }
    }
    

    Instrumentation是java1.5新提供的类,它提供在运行时重新加载某个类的的class文件的api。

    public interface Instrumentation {
    /**
         * 添加一个转换器Transformer,之后的所有的类加载都会被Transformer拦截。
         * ClassFileTransformer类是一个接口,使用时需要实现它,该类只有一个方法,该方法传递类的信息,返回值是转换后的类的字节码文件。
         */
     	void addTransformer(ClassFileTransformer transformer, boolean canRetransform);    
    
     /**
         * 对JVM已经加载的类重新触发类加载。使用的就是上面注册的Transformer。
         * 该方法可以修改方法体、常量池和属性值,但不能新增、删除、重命名属性或方法,也不能修改方法的签名
         */
        void retransformClasses(Class<?>... classes) throws UnmodifiableClassException;
        
    /**
       *此方法用于替换类的定义,而不引用现有的类文件字节,就像从源代码重新编译以进行修复和继续调试时所做的那样。
       *在要转换现有类文件字节的地方(例如在字节码插装中),应该使用retransformClasses。
       *该方法可以修改方法体、常量池和属性值,但不能新增、删除、重命名属性或方法,也不能修改方法的签名
       */
    	void redefineClasses(ClassDefinition... definitions)throws  ClassNotFoundException, UnmodifiableClassException;
    
        /**
         * 获取一个对象的大小
         */
        long getObjectSize(Object objectToSize);
        
        /**
         * 将一个jar加入到bootstrap classloader的 classpath里
         */
        void appendToBootstrapClassLoaderSearch(JarFile jarfile);
        
        /**
         * 获取当前被JVM加载的所有类对象
         */
        Class[] getAllLoadedClasses();
    }
    
    maven打包
    <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <configuration>
                        <archive>
                            <manifest>
                                <addClasspath>true</addClasspath>
                            </manifest>
                            <manifestEntries>
    			//指明提供代理功能的类
                                <Premain-Class>com.imooc.myagent.MyPreMainAgent</Premain-Class>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    客户端调用

    在另一个项目中使用这个jar包。

    public class Client {
      public static void main(String[] args) {
        System.out.println("main");
      }
    }
    

    添加虚拟机启动参数
    -javaagent:jar包路径=hah
    输出结果为

    PreMain start
    hah
    sun.instrument.InstrumentationImpl@27c170f0
    main
    

    结果符合预期,在main方法执行前执行了premain.

    主程序运行之后的代理程序

    创建代理类
    public class MyAgentMainAgent {
    
    //表示在main方法执行之后执行
      public static void agentmain(String agentArgs, Instrumentation inst) {
        System.out.println("AgentMain start");
        System.out.println(agentArgs);
        System.out.println(inst);
    //获取所有已加载的类
        Class[] allLoadedClasses = inst.getAllLoadedClasses();
        for (Class loadedClass : allLoadedClasses) {
          System.out.println(loadedClass);
        }
      }
    }
    
    maven打包
    <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <configuration>
                        <archive>
                            <manifest>
                                <addClasspath>true</addClasspath>
                            </manifest>
                            <manifestEntries>
    			//指明提供代理功能的类
                                <Agent-Class>com.imooc.myagent.MyAgentMainAgent</Agent-Class>
                            </manifestEntries>
                        </archive>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    客户端调用

    在另一个项目中使用这个jar包。在主程序运行之后加载,我们没办法在主程序中调用,只能使用辅助程序然后和主程序通信,这里要用到attach机制。
    Attach API是Sun公司提供的一套扩展API,用来向目标JVM"附着"(Attach)代理工具程序的。有了它,开发者可以方便的监控一个JVM,运行一个外加的代理程序,Sun JVM Attach API功能上非常简单,仅提供了如下几个功能:

    1. 列出当前所有的JVM实例描述
    2. Attach到其中一个JVM上,建立通信管道
    3. 让目标JVM加载Agent

    jdk提供的jstack,jps功能就是使用该机制实现的。
    主程序为

    public class Client {
      public static void main(String[] args) {
        while (true) {
          System.out.println("now:" + new Date());
          try {
            Thread.sleep(10000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
    

    让主程序一直在跑。
    辅助程序为

    public class Client2 {
      public static void main(String[] args)
          throws IOException,
          AttachNotSupportedException,
          AgentLoadException,
          AgentInitializationException {
    //代理程序的jar包位置
        String agentPath = "D:\java\code_resp\IdeaProjects\myagent\target\myagent-1.0-SNAPSHOT.jar";
    //获取所有实例
        List<VirtualMachineDescriptor> descriptorList = VirtualMachine.list();
        for (VirtualMachineDescriptor descriptor : descriptorList) {
    //判断如果是主程序,就加载代理程序
          if (descriptor.displayName().equals(Client.class.getName())) {
            VirtualMachine virtualMachine = VirtualMachine.attach(descriptor);
            virtualMachine.loadAgent(agentPath, "hello");
          }
        }
      }
    }
    

    输出结果为

    now:Sun Jul 12 15:51:55 CST 2020
    now:Sun Jul 12 15:52:05 CST 2020
    now:Sun Jul 12 15:52:15 CST 2020
    AgentMain start
    hello
    sun.instrument.InstrumentationImpl@5e84c484
    class com.imooc.myagent.MyAgentMainAgent
    class com.imooc.sourcecode.java.javaagent.test2.Client
    class com.intellij.rt.execution.application.AppMainV2$1
    class com.intellij.rt.execution.application.AppMainV2
    class com.intellij.rt.execution.application.AppMainV2$Agent
    class sun.nio.cs.Surrogate
    class sun.nio.cs.Surrogate$Parser
    class sun.nio.cs.ISO_8859_1$Encoder
    ...
    ...
    ...
    

    结果符合预期,打印了所有已加载的类。

    使用场景

    • apm:(Application Performance Management)应用性能管理。pinpoint、cat、skywalking等都基于Instrumentation实现
    • idea的HotSwap、Jrebel等热部署工具
    • 应用级故障演练
    • Java诊断工具Arthas、Btrace等
  • 相关阅读:
    XML to Excel
    C# 位域[flags]
    使用windows7的System帐户
    VS.NET 控件命名规范
    Microsoft Robotics Studio到底能做什么?
    SQLServer系统表及其应用(转)
    利用xslt、xml,ajax实现了一个无限级树型导航
    利用xslt实现一个树形导航
    网页信息抓取如何获取延迟加载的网页数据
    站长盈利盈利方式面面观
  • 原文地址:https://www.cnblogs.com/strongmore/p/13288852.html
Copyright © 2020-2023  润新知