【实用工具】Java Agent 介绍以及入门DEMO

介绍

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

Agent分为两种,一种是在主程序之前运行的Agent,一种是在主程序之后运行的Agent(前者的升级版,1.6以后提供)。

实现DEMO

定义HelloAgent预加载类,并且定义premain方法。

public class HelloAgent {

    public static void premain(String agentOps, Instrumentation inst) {
        System.out.println("[TLOG AGENT] START!");
//        AspectLogEnhance.enhance();
        inst.addTransformer(new HelloTransformer());
    }
}

指定转换器,使用javassist对类进行修改。

public class HelloTransformer implements ClassFileTransformer {

    public final String TEST_CLASS_NAME = "com.charles.agent.HelloController";

    public final String METHOD_NAME = "hello";


    public byte[] transform(ClassLoader loader,
                            String className,
                            Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain,
                            byte[] classfileBuffer) throws IllegalClassFormatException {

//      System.out.println("className : " + className);

        String finalClassName = className.replace("/", ".");

        if (TEST_CLASS_NAME.equals(finalClassName)) {
            System.out.println("class name 匹配上了 !");

            CtClass ctClass;

            try {
                ctClass = ClassPool.getDefault().get(finalClassName);
                System.out.println("ctClass is OK !");
                CtMethod ctMethod = ctClass.getDeclaredMethod(METHOD_NAME);
                System.out.println("CtMethod is OK !");
                ctMethod.insertBefore("System.out.println(\"字节码添加成功,打印日志 !\");");
                return ctClass.toBytecode();
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
        }

        return null;
    }
}

修改maven打包方式

    <build>
        <plugins>
            <!--
            META-INF 下 MANIFEST.MF 文件 内容
            Manifest-Version: 1.0
            Premain-Class: com.jenson.TestAgent
            下面Maven插件可以自动实现
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <manifestEntries>
                                        <Premain-Class>com.charles.agent.HelloAgent</Premain-Class>
                                        <!--<Can-Redefine-Classes>${can.redefine.classes}</Can-Redefine-Classes>-->
                                        <!--<Can-Retransform-Classes>${can.retransform.classes}</Can-Retransform-Classes>-->
                                    </manifestEntries>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

在打好包的jar包中就会在META-INF目录下自动生成MANIFEST.MF

Manifest-Version: 1.0
Premain-Class: com.charles.agent.HelloAgent
Archiver-Version: Plexus Archiver
Built-By: admin
Created-By: Apache Maven 3.5.0
Build-Jdk: 1.8.0_291

使用方式:

  • java -javaagent:demo-agent-1.0-SNAPSHOT.jar app.jar
  • 在vm options中设置-javaagent: demo-agent-1.0-SNAPSHOT.jar

在JDK1.6之后新增的agentmain方法,能够实如今main方法执行之后进行插入执行。

public class HelloAgent {
    
    public static void agentmain(String args, Instrumentation instrumentation) {

        try {
            instrumentation.addTransformer(new TestTransformer());
            instrumentation.retransformClasses(needRetransFormClasses.toArray(new Class[0]));
        } catch (Exception e) {

        }
    }
}

打包方式

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <archive>
                    <!--自动添加META-INF/MANIFEST.MF -->
                    <manifest>
                        <addClasspath>true</addClasspath>
                    </manifest>
                    <manifestEntries>
                        <Agent-Class>com.charles.agent.HelloAgent</Agent-Class>
                        <Can-Redefine-Classes>true</Can-Redefine-Classes>
                        <Can-Retransform-Classes>true</Can-Retransform-Classes>
                    </manifestEntries>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

执行耗时的监控DEMO

public class MyMonitorTransformer implements ClassFileTransformer {

    private static final Set<String> classNameSet = new HashSet<>();

    static {
        classNameSet.add("org.itstack.demo.test.ApiTest");
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        try {
            String currentClassName = className.replaceAll("/", ".");
            if (!classNameSet.contains(currentClassName)) { // 提升classNameSet中含有的类
                return null;
            }
            System.out.println("transform: [" + currentClassName + "]");

            CtClass ctClass = ClassPool.getDefault().get(currentClassName);
            CtBehavior[] methods = ctClass.getDeclaredBehaviors();
            for (CtBehavior method : methods) {
                enhanceMethod(method);
            }
            return ctClass.toBytecode();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return null;

    }


    private void enhanceMethod(CtBehavior method) throws Exception {
        if (method.isEmpty()) {
            return;
        }
        String methodName = method.getName();
        if ("main".equalsIgnoreCase(methodName)) {
            return;
        }

        final StringBuilder source = new StringBuilder();
        // 前置增强: 打入时间戳
        // 保留原有的代码处理逻辑
        source.append("{")
                .append("long start = System.nanoTime();\n") //前置增强: 打入时间戳
                .append("$_ = $proceed($$);\n")              //调用原有代码,类似于method();($$)表示所有的参数
                .append("System.out.print(\"method:[")
                .append(methodName).append("]\");").append("\n")
                .append("System.out.println(\" cost:[\" +(System.nanoTime() - start)+ \"ns]\");") // 后置增强,计算输出方法执行耗时
                .append("}");

        ExprEditor editor = new ExprEditor() {
            @Override
            public void edit(MethodCall methodCall) throws CannotCompileException {
                methodCall.replace(source.toString());
            }
        };
        method.instrument(editor);
    }

}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值