Java字节码工具包 ByteKit-阿里云开发者社区

开发者社区> 开发者小助手> 正文

Java字节码工具包 ByteKit

简介: ByteKit 期望能提供简单的 API,让开发人员可以比较轻松的完成字节码增长
+关注继续查看

目标

  1. Arthas之前的字节码增强,是通过asm处理的,代码逻辑不好,理解困难
  2. 基于ASM提供更高层的字节码处理能力,智能诊断/APM领域,不是通用的字节码库
  3. ByteKit 期望能提供简单的 API,让开发人员可以比较轻松的完成字节码增长

对比

功能函数进出注入点绑定数据排队防止重复增强避免装箱/拆箱原点调用替换@ExceptionHandler
字节套件@AtEnter
@AtExit
@AtExceptionExit
@AtFieldAccess
@AtInvoke
@AtInvokeException
@AtLine
@AtSyncEnter
@AtSyncExit
@AtThrow
this/args/return/throw
field
locals
子调用入参/返回值/子调用异常
行数
字节好友OnMethodEnter
@OnMethodExit
@OnMethodExit#onThrowable()
this/args/return/throw
字段
本地人
传统AOPEnter
Exit
Exception
这个/参数/返回/抛出

特性

1. 集中的注入点支持

  • @AtEnter 函数入口
  • @AtExit 函数退出
  • @AtExceptionExit 函数外异常
  • @AtFieldAccess 访问领域
  • @AtInvoke 在方法里的子函数调用
  • @AtInvokeException 在方法里的子函数调用抛出异常
  • @AtLine 在指定行号
  • @AtSyncEnter进入同步块,例如synchronized
  • @AtSyncExit 退出同步块
  • @AtThrow 代码里显式throw异常点

2.动态的绑定

  • @Binding.This 这个对象
  • @@Binding.Class 类对象
  • @Binding.Method 函数调用的方法 对象
  • @Binding.MethodName 函数的名字
  • @Binding.MethodDesc 函数的描述
  • @Binding.Return 函数调用的返回值
  • @Binding.Throwable 函数里抛出的异常
  • @Binding.Args 函数调用的入参
  • @Binding.ArgNames 函数调用的入参的名字
  • @Binding.LocalVars 局部变量
  • @Binding.LocalVarNames 局部变量的名字
  • @Binding.Field field对象属性字段
  • @Binding.InvokeArgs 方法里的子函数调用的入参
  • @Binding.InvokeReturn 方法里的子函数调用的返回值
  • @Binding.InvokeMethodName 方法里的子函数调用的名字
  • @Binding.InvokeMethodOwner 方法里的子函数调用的类名
  • @Binding.InvokeMethodDeclaration method里的子函数调用的desc
  • @Binding.Line 行号
  • @Binding.Monitor 同步块里监控的对象

3.异常的处理

  • @ExceptionHandler在插入的增强代码中,可以用try/catch模块外接起来

4.内联支持

增强的代码和异常代码都是通过内联内联到原来的类里,达到最理想的处理技术效果。

5.invokeOrigin技术

经常,我们要增强一个类,就可以在函数中不断插入一个静态的方法的功能呢,有什么办法。那么有没有更灵活的方式?

例如有一个 hello() 函数:

    public String hello(String str) {
        return "hello " + str;
    }

我们想对它做增强,那么可以写下面的代码:

    public String hello(String str) {
        System.out.println("before");
        Object value = InstrumentApi.invokeOrigin();
        System.out.println("after, result: " + value);
        return object;
    }

增强后的结果是:

    public String hello(String str) {
        System.out.println("before");
        Object value = "hello " + str;
        System.out.println("after, result: " + value);
        return object;
    }

现代方式可以插入代码,非常灵活。

参考增强Dubbo Filter的示例: [查看源文件]

示例

ByteKitDemo.java举例说明,[查看源文件]。

1. 定义注入点和绑定数据

  • 在下面的SampleInterceptor时定义了要注入@AtEnter/ @AtExit/@AtExceptionExit三个地方,
  • @Binding绑定了不同的数据
  • @AtEnter里配置了inline = true,则说明插入的功能SampleInterceptor#atEnter会被内联掉
  • 配置了suppress = RuntimeException.classsuppressHandler = PrintExceptionSuppressHandler.class说明插入的代码会被try/catch交互
    public static class SampleInterceptor {

        @AtEnter(inline = true, suppress = RuntimeException.class, suppressHandler = PrintExceptionSuppressHandler.class)
        public static void atEnter(@Binding.This Object object, 
                @Binding.Class Object clazz,
                @Binding.Args Object[] args, 
                @Binding.MethodName String methodName,
                @Binding.MethodDesc String methodDesc) {
            System.out.println("atEnter, args[0]: " + args[0]);
        }

        @AtExit(inline = true)
        public static void atExit(@Binding.Return Object returnObject) {
            System.out.println("atExit, returnObject: " + returnObject);
        }

        @AtExceptionExit(inline = true, onException = RuntimeException.class)
        public static void atExceptionExit(@Binding.Throwable RuntimeException ex,
                @Binding.Field(name = "exceptionCount") int exceptionCount) {
            System.out.println("atExceptionExit, ex: " + ex.getMessage() + ", field exceptionCount: " + exceptionCount);
        }
    }

2.@ExceptionHandler

上面在的@AtEnter配置里,生成的代码会被try / catch语句包围,具体那么的内容的英文在PrintExceptionSuppressHandler

    public static class PrintExceptionSuppressHandler {

        @ExceptionHandler(inline = true)
        public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
            System.out.println("exception handler: " + clazz);
            e.printStackTrace();
        }
    }

3. 查看反编译结果

原来的Sample类是:

    public static class Sample {
        private int exceptionCount = 0;

        public String hello(String str, boolean exception) {
            if (exception) {
                exceptionCount++;
                throw new RuntimeException("test exception, str: " + str);
            }
            return "hello " + str;
        }
    }

增强后的字节码,再反编译:

package com.example;

public static class ByteKitDemo.Sample {
    private int exceptionCount = 0;

    public String hello(String string, boolean bl) {
        try {
            String string2 = "(Ljava/lang/String;Z)Ljava/lang/String;";
            String string3 = "hello";
            Object[] arrobject = new Object[]{string, new Boolean(bl)};
            Class<ByteKitDemo.Sample> class_ = ByteKitDemo.Sample.class;
            ByteKitDemo.Sample sample = this;
            System.out.println("atEnter, args[0]: " + arrobject[0]);
        }
        catch (RuntimeException runtimeException) {
            Class<ByteKitDemo.Sample> class_ = ByteKitDemo.Sample.class;
            RuntimeException runtimeException2 = runtimeException;
            System.out.println("exception handler: " + class_);
            runtimeException2.printStackTrace();
        }
        try {
            String string4;
            void str;
            void exception;
            if (exception != false) {
                ++this.exceptionCount;
                throw new RuntimeException("test exception, str: " + (String)str);
            }
            String string5 = string4 = "hello " + (String)str;
            System.out.println("atExit, returnObject: " + string5);
            return string4;
        }
        catch (RuntimeException runtimeException) {
            int n = this.exceptionCount;
            RuntimeException runtimeException3 = runtimeException;
            System.out.println("atExceptionExit, ex: " + runtimeException3.getMessage() + ", field exceptionCount: " + n);
            throw runtimeException;
        }
    }
}

开发相关人员

部署到远程仓库:

mvn clean deploy -DskipTests -P release


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
MySQL percona-toolkit工具包的安装和使用(超详细版)
转载:https://www.cnblogs.com/zishengY/p/6852280.html 一.检查和安装与Perl相关的模块     PT工具是使用Perl语言编写和执行的,所以需要系统中有Perl环境。
1562 0
走进JavaWeb技术世界12:从手动编译打包到项目构建工具Maven
本文出自我的公众号:程序员江湖。 满满干货,关注就送。 小李的Build之路(上) 转自: 刘欣 码农翻身 2016-07-10 摘要:手工Build的烦恼要不是为了和女朋友留在一个城市,小李肯定去北上广奋斗去了。
1596 0
分析Java的类加载器与ClassLoader(二):classpath与查找类字节码的顺序,分析ExtClassLoader与AppClassLoader的源码
先回顾一下classpath classpath的作用:         classpath的作用是指定查找类的路径:当使用java命令执行一个类(类中的main方法)时,会从classpath中进行查找这个类。
1084 0
淘系自研前端环境管理工具 AppToolkit 正式发布
AppToolkit提供可视化配置环节的能力,屏蔽环节配置的复杂度和命令行的而学习成本,帮助开发者简单快速搭建前端开发环境。
22 0
MySQL中的Percona-toolkit工具由来漫谈
首先问一个问题,你听说过下面这两个工具吗? Maatkit 和Aspersa 如果听过,可能就暴露年龄了,你如果现在去查Aspersa相关的文章,会发现下载链接之类的都不可用了。
1426 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
3963 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
5699 0
1321
文章
282
问答
来源圈子
更多
阿里巴巴相信开源的世界里人人贡献代码,人人获得收益,共同创造一个互帮互利的社区,促进技术进步和发展。
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载