最初我是在公司内部的 broadcast 上面听到有 principal 介绍到它的,和 AspectJ 归在一起。看了几个例子之后觉得有点意思,就去 Lombok 的官网上扒了一下。我们已经知道向 AspectJ、CGLib 等等都可以做到对已有 Java 代码在字节码层级的改变,无论是编译时期静态织入还是运行时期动态代理,对于我们使用 AOP 来减少那些重复性编码的劳动、增加切面性质的逻辑颇有帮助。这里有几个概念:
- 首先是 AOP,我在这里不啰嗦,网上有的是这样的文章。
- 其次,如果你还没有接触过 AspectJ,那么在 Lombok 之前,AspectJ 是值得推荐了解的。和 Lombok 相比,AspectJ 更强大,有它自己的语法,本身更像是一个代码生成器,它有独特的语法编译工具,可以自己生成 class 文件。换言之,它已经超出了往常 Java 项目最后简单地编译成 “jar” 被调用的范畴。
- 再次,是 JDK 的动态代理。原理上不复杂,JDK 的 Proxy 类提供了一个建立代理对象的方法,需要传入被代理对象的接口集合、class loader 和代理对象自己,其中代理对象需要实现 InvocationHandler 的 invoke 方法,在其中实现了,对于被代理对象的方法执行,可以进行任意的行为改变。最终,生成的代理对象和被代理对象实现自相同的接口,只是方法的行为被改变了。
- 最后,是 CGLib。相较于 AspectJ,CGLib 是一个纯纯粹粹的 jar 包而已了,也就意味着,它对于字节码的织入,只能在运行时通过某种方式实现了。它使用的方式是动态代理,但是相对于 JDK 传统的动态代理方式,它没有对被代理对象接口的要求,换言之,如果被代理对象没有实现自任何接口,或者期望改变的方法没有源自任何接口,只要不是 final 修饰的类和方法,一样可以做到动态代理。它的原理也不复杂,在运行时给被代理类创建一个子类,覆写被代理类中需要改变行为的方法。和 JDK 的动态代理相比,除去类创建时更大的开销,在方法执行时它的效率要高过前者。
现在让我们回到 Lombok,它的原理和 AspectJ 类似。它的目的在于让程序员少写一些 “样板代码”。所谓样板代码,是那些没有营养,却又不得不写的代码,写的时候觉得毫无技术含量,依样画葫芦,比如一个类的全参构造函数、无参构造函数、get/set 方法、toString 方法等等。这些代码你可以指望向 Eclipse 这样的 IDE 帮你自动生成到你的代码文件里去,当然,也可以借由 Lombok 这样的工具,在编译阶段,不修改你的代码源文件,但是让编译出的 class 文件具备样板代码的逻辑。
下面这张图来自 Lombok 官网的一段视频。你可以看到左侧的代码仅仅是一个普通的 POJO 类,增加了 Lombok 的注解而已。右侧显示了编译出的 class 文件,get/set 方法已经生成完毕。可以使用反编译工具打开 class 文件查看,这样的 class 文件和手写样板代码生成出来的 class 文件是一样的。
我们再来仔细认识一下 Lombok 的特性:
1. val 关键字:
你可以使用 val 关键字写出这样的代码来,看起来就是 duck type 啊:
public String example() { val example = new ArrayList(); example.add("Hello, World!"); val foo = example.get(0); return foo.toLowerCase(); }
2. 注解,除了一看便知的 @Getter、@Setter、@ToString 等等以外,我介绍几条有意思的 Lombok 注解,你可以在这里找到全集:
- @Data:相当于同时使用了 @ToString、@EqualsAndHashCode、@Getter、@Setter 和 @RequiredArgsConstructor 这些注解,对于 POJO 类十分有用。
- @NonNull:给方法参数增加这个注解会自动在方法内对该参数进行是否为空的校验,如果为空,则抛出 NPE。
- @Cleanup:自动生成 try-finally 这样的代码来关闭流(你一定写过使用-关闭流的样板代码)。
- @Getter(lazy=true):可以替代掉经典的 Double Check Lock 样板代码!
我很喜欢这种小项目,很小的范围,简单,而且专注,解决非常特定的问题。
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》