bytebuddy的开发指南-翻译


原文链接

翻译这个的目的是,简述了源码的结构


入门

首先,您需要在本地计算机上创建Byte Buddy的副本。克隆存储库后,您可以使用Maven构建项目 。你的shell命令,这可能看起来像这样:

git clone https://github.com/raphw/byte-buddy.git
cd byte-buddy
mvn package

在开始编码之前,请确保所有测试用例都能成功运行。Byte Buddy的master分支一直都是这种情况。我们当前的Travis版本可以肯定地证实了这一点 ,该版本在监测Byte Buddy的最新状态。Travis被设置为针对Open JDKv6v7以及 Oracle JDKv6v7来编译和测试项目 。由于我们某些测试是创建和加载Java类,因此要确保所生成类的格式能被特定Java版本能够解析。如果您要对Byte Buddy进行更改并且会直接影响所生成的字节码,请在提交更改之前确保使用不同的Java版本充分测试您的更改。这些都意味着您需要编写与Java 6编译器兼容的Java代码。通常,您的代码应至少具有90%的测试覆盖率,但是与只尝试碰到任何行的测试相比,我们推荐进行彻底(100%)的测试。您可以随时运行Maven的cobertura:cobertura来获取覆盖率。Byte Buddy当前的测试覆盖率是85%。作为更现代的替代方法,Byte Buddy还支持通过从Maven 运行变异测试org.pitest:pitest-maven:mutationCoverage 。但是请记住,与计算覆盖率相比,运行变异测试需要大量额外的运行时间。最后,可以激活Maven 的integration profile 配置文件,以扫描代码库中潜在的错误。这个配置文件是始终在持续集成服务器上激活的。 另一方面,checks profile聚集了运行速度更快的代码检查,并且对于任何Maven构建默认情况下都被激活。

该项目分为不同的模块。

  • 根项目byte-buddy-parent是任何Byte Buddy模块配置的共同基础。
  • 项目的实现代码在byte-buddy-dev模块。该模块是直接依赖ASM库,因此dev作为模块的名字。
  • 由于ASM库·不能保证向后兼容性·,因此byte-buddy模块负责将ASM依赖关系重新打包到Byte Buddy自己的名称空间中。这样做会将ASM依赖(shift)重命名为net.bytebuddy.jar.asm。这个dependency-free无依赖的模块没有定义其自身的来源,而是依赖于byte-buddy-dev模块。在其构建过程中,将解决所有依赖性并创建dependency-free部署描述符。更多的说明在如何定义对Byte Buddy的依赖关系的部分。为了重新打包,您需要使用extras profile配置文件运行Maven构建,这将进一步触发源代码的构建和javadoc的生成。如果gpg Maven尝试为所有产出签名。为了实现这一点, 签名者必须正确配置gpg插件。
  • 作为另一个模块,byte-buddy-benchmark包含一个 JMH基准测试,,用于测量Byte Buddy的运行时的耗时与性能,来和与其他代码生成库相比。
  • byte-buddy-agent该模块提供了一个Java代理,该代理可以增强Byte Buddy的通用API。
  • byte-buddy-android模块包含一个在Android平台上起作用的ClassLoadingStrategy,其中 byte-buddy-android-test模块定义了一个应用此策略的测试应用程序。这仅当android profile配置文件被激活时才构建它,因为构建模块需要Android SDK。

架构概览

每个代码生成框架都需要一种反映类型及其成员的方法。Byte Buddy通过TypeDescription接口访问类型信息。该接口提供的API 和Java反射API 中定义的Class相似,并且可以使用TypeDescription.ForLoadedType来代表加载过的类,这个会包括具体的类型。但是,Byte Buddy也可以用于创建Java agent,在该Java agent中,需要在加载类之前对类进行操作。因此,Byte Buddy永远不会对已经加载过的类型进行操作。所有描述类型接口都在net.bytebuddy.description包中找到。

同样,Byte Buddy将泛型描述为·ypeDescription.Generic接口的实例。该接口比Java自身的等效接口(TypeByte Buddy的Type接口,提供了用于处理各种通用类型的方法)丰富。当使用泛型的一些不支持的功能时将引发异常。我们选择这种方法作为Java语言所需的类型转换方法更简介的替代。由于大多数操作是由visitors(asm访问字节码的接口)执行的,因此实际上这并不是问题。

Byte Buddy建立在ASM字节代码解析器之上,该解析器已成为Java生态系统中字节代码解析的事实上的标准。ASM的 接口来操作字节码的生成,通常是使用代表名称和描述符的字符串值来(所以很简单)。Byte Buddy提供了一个StackManipulation 接口来封装了visitors的命令,StackManipulation 用来和Byte Buddy 的 descriptio 接口交互,把需要的的值提取为适当的格式并与ASM进行交互。此外,每种堆栈操作都知道其对JVM操作数堆栈大小的影响。这样,就可以将几种堆栈操作组合在一起,以计算它们对最小堆栈大小的共同要求。可以用一个ByteCodeAppender 来表示几种堆栈操作的。字节码追加器代表一个代码块,并且需要将操作数堆栈留空。另外,字节代码追加器应该暴露为本地变量(local variables 就是字节码知识里面的本地变量,)的空间(执行字节码指令时,需要关联使用这些变量)。通常,字节码追加器由一个或多个堆栈操作组成。与代码生成相关的所有类都收集在net.bytebuddy.implementation.bytecode包中。

每个字节码附加器以接受方法的实现作为入参,然后apply到目标方法上。此外,它还接收一个ASM visitor ,用于注册该方法的代码和的实例 Implementation.ContextImplementation.Context允许注册辅助类型(auxiliary type)。辅助类型表示执行该方法所需的帮助程序类型(就是额外的工具类,以便于其他操作)。比如,一个代理类要调用被修改过的类中的方法。通常会使用MethodDelegation为被修改过的类加一个@SuperCall注解。Implementation.Context是Byte Buddy中为数不多的可变类之一,因为它伴随着的ASM 的method visitor 本事就是可变得(这里的变是指动态生成,生成时依赖条件)。但是,由于Implementation.Context并未公开给公共API,因此也不应该由ByteCodeAppender 也不应该暴露Implementation.Context给用户使用。

构造方法的top-level的API是Implementation接口。一个ByteCodeAppender,需要一个Implementation的是例给出一个instrumented type。此外,Implementation可以用来注册额外的方法,字段或代码块,在instrumented type静态初始化化的时候。最后,任何实现实例都会收到一个实例Implementation.Target,它提供了一种访问instrumented type中属性的方法,该属性与具体的类型无关。例如,可以查询instrumented typesuper方法 调用。如果用户执行subclass的生成,则此查询将返回真正super方法的。如果用户进行type-rebasement,,则实现目标实现将调用被重新创建方法的原始代码(原始代码的方法被重命名,然后被放在新方法中)。

在Byte Buddy中,通过将属性注册到可以构造任何类型DynamicType.Builder。builder 本身构造了一个InstrumentedType实例,该实例可以扩展 TypeDescription以允许Byte Buddy的其他组件在创建其类型之前查看之前的的类型。此外,方法和字段实现以FieldRegistry或 方式注册MethodRegistry。最后,动态类型构建器将所有收集的信息TypeWriter提供给与ASM API交互以生成类文件的所有类型,并将其应用于匹配的方法描述 。


编码约定

Byte Buddy的目标是实现完全不变,除了与大多数易变的ASM库进行交互的易变类之外。但是,可变组件必须隔离在与ASM交互的范围内,并且不得暴露给最终用户。此类实例也不应存储为字段的值。此外,某些组件(例如Byte Buddy的类加载器)由于其作为类加载器的性质而可变。但是,应将Byte Buddy中的所有集合视为不可变的,并且当从公共API返回集合时,也必须强制执行此属性。

所有一成不变的类必须实现适当的hashCode,并equals为对象平等是一些Byte Buddy组件的一个重要概念,方法。例如,任何Implementation实例都必须仅一次性准备instrumented type 类型,因为在Java中两次注册具有相同名称的字段是不合法的。为确保这一点,Implementation将检查任何对象是否与之前有机会准备插入类型的实现是否相等。如果实现内部依赖于不同的对象,则所有这些组件都必须履行其相等的约束,这一点很重要。另外,任何组件都应实施适当的 toString一种改进调试的方法,尤其是在帮助论坛上发布了用户的堆栈跟踪信息时。使用ObjectPropertyAssertion,Byte Buddy可以运行单元测试来确定所有这些方法的正确实现。

Byte Buddy是强面向对象,但从面向函数设计上获得了一些启发。不幸的是,作为许多其他库开发人员使用的元库,Byte Buddy受到强大的兼容性要求的约束,并在Java 6上进行编译。为模仿函数,Byte Buddy经常通过枚举实现接口,其中枚举产生命名函数。最后,类文件被用作相关类的容器而不是包。在类文件中,可以定义比top-level类更好的可见性范围,例如通过允许protected只能由子类或在内部使用另一个类的类中看到的类。同样,当重构后不再需要一个类时,此分组约定使删除所有相关代码变得容易。

作为元库,Byte Buddy试图提供对API尽可能开放的API,因为无法预期使用范围。只要有可能,委托将是优于类扩展的首选扩展机制。所有代码都应记录在案,这使得此属性 更易于通过自动检查进行验证。在Byte Buddy中,将其null用作字段,参数的值或方法返回值是一种不好的做法。模仿Java反射API的描述类型是一个例外,其中null值是常见的。由于描述实例(description instances)向最终用户公开,因此决定将相似性视为比一致性更重要的因素。所以潜在的null值必须记录在方法上。对于Byte Buddy,未检查的异常比检查的异常更可取。


贡献

修复错误后,只需 在GitHub上创建拉取请求即可。收到通知后,我们将尽快调查此事。但是,请确保已正确描述了更改和已解决的问题,并提供了一个可重现该问题并证明您的修复程序有效的测试用例。这使我们的工作更加轻松,我们将能够更快地应用您的补丁。如果添加新的方法,字段或类型,请确保编写一些描述其用途的代码内文档。如果您应用与性能相关的更改,请使用byte-buddy-benchmark套件。最后,请注意,Byte Buddy的新版本通常是在其自己的分支中开发的。

如果您要提供功能,请先联系我们,然后再花费大量时间,以便我们可以讨论您的更改在Byte Buddy当前的开发状态下如何有意义。字节伙伴旨在稳定地提供更多功能,但我们不会以其稳定性和代码一致性为代价来扩展其功能集。但是,不要因本公告而气disc。如果您对Byte Buddy的源代码有足够的了解,能够实现您想要共享的新功能,那么您肯定会考虑一下,我们将尽最大努力将其合并到我们的版本中!只需与我们交谈,我们非常乐意欢迎您加入我们。

如果您想为Byte Buddy的文档,本网页上的描述甚至本网页的结构和设计做出贡献,绝对欢迎您这样做!我们深信,透彻和最新的文档是成功项目的关键,我们将尽最大努力实现这一信念。只要可以改善Byte Buddy的可访问性或外观,甚至可以进行很小的更改,因为最终,该项目是为用户设计的。只需克隆此网页,该网页托管在项目gh-pages分支的GitHub上。该网页是使用angular.js和 Twitter的Bootstrap创建的。

路线图

Byte Buddy已达到1.0版,除了尚不支持的两个功能外,还被认为是功能完整。随着1.0版的发布,库的稳定性和性能得到了极大的重视,并且防御性地增加了新功能。自然,Java编程语言和字节代码格式的发展将在未来需要较新的版本,而Byte Buddy的目标是提供一种向后兼容的方式来处理旧版本和较新版本的Java。Java 9支持目前仍处于试验阶段。从Java 8开始,Byte Buddy当前不支持以下功能:

类型推断

Java编译器可以推断泛型类型。字节好友当前不提供此类功能。对类型推断的支持将允许更好地验证泛型类型并实现Assigner考虑泛型类型信息的。不幸的是,此功能在实际应用很少的情况下需要进行大量工作。因此,目前尚未实现。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值