zoukankan      html  css  js  c++  java
  • java Instrument修改字节码实现aop功能

    Agent工程2个类:

    public class MyAgent {
        
         /**
         * 该方法在main方法之前运行,与main方法运行在同一个JVM中
         * 并被同一个System ClassLoader装载
         * 被统一的安全策略(security policy)和上下文(context)管理
         */
        public static void premain(String agentOps, Instrumentation inst) {
            System.out.println("=========premain方法执行========");
            System.out.println(agentOps);
            // 添加Transformer
            inst.addTransformer(new MyTransformer());
        }
    
        /**
         * 如果不存在 premain(String agentOps, Instrumentation inst) 
         * 则会执行 premain(String agentOps)
         */
        public static void premain(String agentOps) {
            System.out.println("=========premain方法执行2========");
            System.out.println(agentOps);
        }
    
    }
    public class MyTransformer implements ClassFileTransformer {
    
        final static String prefix = "
    long startTime = System.currentTimeMillis();
    ";
        final static String postfix = "
    long endTime = System.currentTimeMillis();
    ";
    
        // 被处理的方法列表
        final static Map<String, List<String>> methodMap = new HashMap<String, List<String>>();
    
        public MyTransformer() {
            add("com.hwtest.demo.MyProgram.sayHello");
            add("com.hwtest.demo.MyProgram.sayHello2");
        }
    
        private void add(String methodString) {
            String className = methodString.substring(0, methodString.lastIndexOf("."));
            String methodName = methodString.substring(methodString.lastIndexOf(".") + 1);
            List<String> list = methodMap.get(className);
            if (list == null) {
                list = new ArrayList<String>();
                methodMap.put(className, list);
            }
            list.add(methodName);
        }
    
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
            className = className.replace("/", ".");
            if (methodMap.containsKey(className)) {// 判断加载的class的包路径是不是需要监控的类
                CtClass ctclass = null;
                try {
                    ctclass = ClassPool.getDefault().get(className);// 使用全称,用于取得字节码类<使用javassist>
                    for (String methodName : methodMap.get(className)) {
                        String outputStr = "
    System.out.println("this method " + methodName
                                + " cost:" +(endTime - startTime) +"ms.");";
    
                        CtMethod ctmethod = ctclass.getDeclaredMethod(methodName);// 得到这方法实例
                        String newMethodName = methodName + "$old";// 新定义一个方法叫做比如sayHello$old
                        ctmethod.setName(newMethodName);// 将原来的方法名字修改
    
                        // 创建新的方法,复制原来的方法,名字为原来的名字
                        CtMethod newMethod = CtNewMethod.copy(ctmethod, methodName, ctclass, null);
    
                        // 构建新的方法体
                        StringBuilder bodyStr = new StringBuilder();
                        bodyStr.append("{");
                        bodyStr.append(prefix);
                        bodyStr.append(newMethodName + "($$);
    ");// 调用原有代码,类似于method();($$)表示所有的参数
                        bodyStr.append(postfix);
                        bodyStr.append(outputStr);
                        bodyStr.append("}");
    
                        newMethod.setBody(bodyStr.toString());// 替换新方法
                        ctclass.addMethod(newMethod);// 增加新方法
                    }
                    return ctclass.toBytecode();
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                    e.printStackTrace();
                }
            }
            return null;
        }
    }

    原始项目:

    public class MyProgram {
        public static void main(String[] args) {
            sayHello();
            sayHello2("hello world222222222");
        }
        
        public static void sayHello() {
            try {
                Thread.sleep(2000);
                System.out.println("hello world!!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void sayHello2(String hello) {
            try {
                Thread.sleep(1000);
                System.out.println(hello);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    agent项目打jar包是配置为:

    Manifest-Version: 1.0
    Premain-Class: com.hwtest.MyAgent
    Can-Redefine-Classes: true
    Boot-Class-Path: javassist.jar

    cmd执行命令

    java -javaagent:MyAgent.jar  -jar MyProgram.jar
    
    
    =========premain方法执行========
    null
    hello world!!
    this method sayHello cost:2000ms.
    hello world222222222
    this method sayHello2 cost:1000ms.

    附:agent jar中manifest的属性

    • Premain-Class: 当在VM启动时,在命令行中指定代理jar时,必须在manifest中设置Premain-Class属性,值为代理类全类名,并且该代理类必须提供premain方法。否则JVM会异常终止。
    • Agent-Class: 当在VM启动之后,动态添加代理jar包时,代理jar包中manifest必须设置Agent-Class属性,值为代理类全类名,并且该代理类必须提供agentmain方法,否则无法启动该代理。
    • Boot-Class-Path: Bootstrap class loader加载类时的搜索路径,可选。
    • Can-Redefine-Classes: true/false;标示代理类是否能够重定义类。可选。
    • Can-Retransform-Classes: true/false;标示代理类是否能够转换类定义。可选。
    • Can-Set-Native-Prefix::true/false;标示代理类是否需要本地方法前缀,可选。

    当一个代理jar包中的manifest文件中既有Premain-Class又有Agent-Class时,如果以命令行方式在VM启动前指定代理jar,则使用Premain-Class;反之如果在VM启动后,动态添加代理jar,则使用Agent-Class

      

    地址记录:

    (1)利用ClassFileTransformer实现aop:http://xj84.iteye.com/blog/1221105

    (2)Java通过修改类的字节码实现aop功能:http://www.360doc.com/content/07/0518/11/25392_506401.shtml

    (3)java.lang.instrument动态修改替换类代码:http://zctya.blog.163.com/blog/static/1209178201131944127774/

  • 相关阅读:
    minikube dashboard 403 forbidden
    kubernetes 条件需求 修改内核和系统启动项
    kali 2020汉化 设置中文
    mac Memcached安装及基本命令-转载
    转载——Linux性能剖析工具—perf
    Django-migrations中的迁移版本和数据库中的迁移版本对不上怎么办?
    下载yum源中的rpm包
    server2008 安装mysql8.0
    好累啊!
    我与游戏
  • 原文地址:https://www.cnblogs.com/nnavvi/p/7340910.html
Copyright © 2011-2022 走看看