zoukankan      html  css  js  c++  java
  • JVM中的"AOP",java agent

    前言:

    考虑一下问题:如何统计一个方法执行了多久?我们会回答Spring aop代理,加拦截。但是这有一个问题,如果spring内部方法调用,aop就会失效了。

    再想一想:我们代码写好了,或者我们的项目发布了,怎么统计方法运行时长,怎么能排查时间过长的方法执行?

    带着这些问题,我们来看一下jvm层面的代理。

    首先,可以参考一下这个 

    Arthas运行原理

    类似的,我们可以用到里面的技术,Instrument和Attach API

    import java.lang.instrument.ClassFileTransformer;
    import java.lang.instrument.Instrumentation;
    
    public class MyAgent {
    
        public static void premain(String agentArgs, Instrumentation inst) {
            System.out.println("this is an perform monitor agent.");
    
            // 添加 Transformer
            ClassFileTransformer transformer = new PerformMonitorTransformer();
            inst.addTransformer(transformer);
        }
    }

    package instrument;
    
    import javassist.CannotCompileException;
    import javassist.ClassPool;
    import javassist.CtBehavior;
    import javassist.CtClass;
    import javassist.expr.ExprEditor;
    import javassist.expr.MethodCall;
    
    import java.lang.instrument.ClassFileTransformer;
    import java.lang.instrument.IllegalClassFormatException;
    import java.security.ProtectionDomain;
    
    public class PerformMonitorTransformer implements ClassFileTransformer {
    
        private static final String PACKAGE_PREFIX = "instrument";
    
        @Override
        public byte[] transform(ClassLoader loader,
                                String className,
                                Class<?> classBeingRedefined,
                                ProtectionDomain protectionDomain,
                                byte[] classfileBuffer) throws IllegalClassFormatException {
            try {
                String currentClassName = className.replaceAll("/", ".");
    
                // 仅仅提升这个包中的类
                if (!currentClassName.startsWith(PACKAGE_PREFIX)) {
                    return null;
                }
    
                System.out.println("now 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;
            }
            final String methodName = method.getName();
            if (methodName.equalsIgnoreCase("main")) { // 不提升main方法
                return;
            }
    
            ExprEditor editor = new ExprEditor() {
                @Override
                public void edit(MethodCall methodCall) throws CannotCompileException {
                    methodCall.replace(genSource(methodName));
                }
            };
            method.instrument(editor);
        }
    
        /* 打入新的代码 */
        private String genSource(String methodName) {
            StringBuilder source = new StringBuilder();
            source.append("{")
                    .append("long start = System.nanoTime();
    ") // 前置增强: 打入时间戳
                    .append("$_ = $proceed($$);
    ") // 保留原有的代码处理逻辑
                    .append("System.out.print("method:[" + methodName + "]");").append("
    ")
                    .append("System.out.println(" cost:[" +(System.nanoTime() -start)+ "ns]");") // 后置增强
                    .append("}");
            return source.toString();
        }
    }

    测试类

    public class InstrumentTest {
        private void fun1() {
            System.out.println("this is fun 1.");
        }
    
        private void fun2() {
            System.out.println("this is fun 2.");
        }
    
        // add VM options: -javaagent:./first-instrument/target/my-agent.jar=first
        public static void main(String[] args) {
            InstrumentTest test = new InstrumentTest();
            test.fun1();
            test.fun2();
    
            AnotherTest test1 = new AnotherTest();
            test1.fun3();
            test1.fun4();
        }
    }

    运行日志

     简单的demo大概是这样 代码地址

    参考

    Java字节码系列

    1:Java字节码1-Agent简单上手

    2: Java字节码2-instrument初体验

    3: Java字节码3-使用ByteBuddy实现一个Java-Agent

    4: Java字节码4-使用Java-Agent实现一个JVM监控工具

  • 相关阅读:
    Zlib编译
    最新Webstrom, Idea 2019.2.3 的激活
    图像理解与深度学习开篇
    C# 反射(Reflection)
    SpringMVC中使用forward和redirect进行转发和重定向以及重定向时如何传参详解
    Navicat for Oracle中如何使用外键
    【数据库】主键,外键,主表,从表,关联表,父表,子表
    onclick事件没有反应的五种可能情况
    button小手设置 css的cursor
    Spring MVC F5刷新问题
  • 原文地址:https://www.cnblogs.com/liran123/p/15319911.html
Copyright © 2011-2022 走看看