这篇文章里,我会为你展示这周我偶尔遇到的一个强大的产品: Lombok项目 允许你修改编译好的字节码。首先,我会详细描述下Lombok的特征,让你有初步的了解。在这篇文章的第二部分,我会描述怎样去扩展它来生成你的代码。
自从JEE开始就有一些抱怨组件编码的复杂性。我认为EJB v2就是这复杂性的好例子:只是一个简单的EJB,你不得不为EJB类提供一个home和每种方位类型的接口(本地和远程)。这就使它变得复杂,容易犯错以及更重要的是让你没多少时间去关注正在值得去关注的业务代码。两个举措表名了在编码时减少需要样板代码数量的愿望:
Lombok项目的目的同上面两个举措差不多,但为了这么做,它使用另外的途径。
Java的第五个版本介绍了注解的概念,代码元数据可以在编译时或运行时处理。不幸的是,在JDK5里,编译时注解分成了两个步骤。首先,你必须运行apt可执行文件处理注解,可以是创建或修改源文件,然后使用javac编译你的源代码。这不是最好的方法,所以在Java6移除了apt同时让javac能够管理注解,让处理注解能顺畅地在简单的一步计算完成。这也是Lombok采用的途径。
Lombok核心特征是你需要用注解来创建代码,目的是减少你要写的样板代码的数量。它为你提供如下注解,这可能会永远改变代码(不是你的生活):
例如,当你学习OO(面向对象)方式来编码,你经常是把你的字段设置为私有同时编写公共的访问器来访问这些字段:
public class Person { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
因为这种写法是很烦人的,一些(并非全部)IDE会有一个功能来生成访问器。它有一些缺点:
此外,手动创建访问器是比较容易出错的:有一次,我花费几个小时在同事写的代码里找一个bug,最后发现是setter写错了。
因为getter/setter仅仅是字段的元数据,Lombok的立场就是把这些方法看成这样:Java里的元数据都通过注解来管理。看下面的代码:
import org.lombok.Getter; import org.lombok.Setter; public class Person { @Getter @Setter private String name; }
我反编译了生成的class文件(使用 JAD),发现它其实创建的字节码都是完全相同的,只是源代码更为简洁和更不容易产生错误。
思考一下,我发现有3个对使用Lombok的争论:
import lombok.AccessLevel; import org.lombok.Getter; import org.lombok.Setter; public class Person { @Getter @Setter(AccessLevel.PROTECTED) private String name; }
同时这对所有提供的注解都有效。可以去阅读一下它。
使用Lombok有3个步骤操作:
虽然,这里有一个问题。注意下最后那句对javac的强调。由于大部分(并非全部)你和我都知道的开发者专注于一点效率,导致大家都去使用IDE。我不知道NetBeans和其他同类的IDE(是否有问题),但是我喜欢用的Eclipse不会使用javac来编译而是它内置的编译器。
我们来自Lombok的朋友也想到了这点,同时Lombok也能够hook到Eclipse的编译过程中。为了要做到这个,只要执行lombok.jar然后你在你屏幕上看到下面的说明:它将会加入两行东西到你的eclipse.ini里。
一个忠告(因为我犯了这个错误):如果你通过有参数的命令行来启动Eclipse,例如是Window的快捷方式,这些参数是会优先使用,同时eclipse.ini会默默地忽略掉了。这只是让你知道一下...
据我了解, 目前只有一款名为Morbok的Lombok扩展。它让你只使用注解来创建典型的私有静态常量logger。
好处是Morbok使用全限定类名作为logger的名字,这样就不会再有复制错误了。坏处是如果你不使用Commons Logging作为你的日志框架,你必需为每个@Logger注解配置你要使用的框架,这里是没有全局配置的:依我看来,下个版本可能会修复这个问题(有这个版本?)。
先说重要的,Lombok需要JDK6来编译,因为注解处理是在Java5的APT完成的。现在,Lombok在环境已经为类构建了AST后直接hook到编译过程中。
然后传递结构从而形成它每个引用的处理程序。每个注解都有一个处理程序:@Getter的HandleGetter,@Setter的HandlerSetter等待。处理程序的责任是处理注解,你可以猜想下它是什么。
扩展Lombok分为3个步骤:
真正在编码是在步骤2里:接口有一个单独的方法handle(AnnotationValues<T> annotation,com.sun.tools.javac.tree.JCTree.JCAnnotation ast, JavacNode annotationNode).注意到第二个参数的包了吗?它表示Sun的私有实现。它有一些大缺点:
我编写了一个使用@Delegate注解的雏形作为例子。这样一个在字段的注解表示声明的类会和字段的类有相同的公共方法,同时每个方法的主体会调用委托对象的方法。
public class Delegator { @Delegate private DelegateObject object; ... } public class DelegateObject { public void doSomething() { ... } }
上面的代码会生成和下面代码相同的字节码:
public class Delegator { private DelegateObject object; public void doSomething() { object.doSomething(); } ... }
到目前为止:
最终的实现交给勇敢的阅读者们来完成:原始代码在这里是用Eclipse/Maven的格式。
一些改进将很快加入到Lombok里。首先,我不喜欢JAR的整体结构。恕我直言,它可以很好地解耦到3个独立的JAR包:Lombok本身,提供注解和关联的处理器,以及最后还有安装器。
此外,为每个注解编写两个处理器是很费时的。更何况你也想支持NetBeans呢?也许使用Service Provider就是个错误...
最后,依赖于Sun的内部编译API是很大的风险。我任务如果Lombok能提供一个对这API的facade,它能减少企业使用这个工具的风险,同时桥接代码能够交给懂得这个API的人(Lombok队伍)而不是开发者(像我这样的)。
总而言之,不关这些缺陷,Lombok通过很好模仿Spring的成功做法,让它看起来是个非常有前途的项目。这是我无论如何都期待它的原因,因为它的小思维带来了如此大的价值:一路走好!
了解更多:
2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务