6.1 问题的提出

之前的几章我们定义数据库实体对象时,需要为每个数据库字段编写了对象的Setter和Getter方法。当数据库表的数量或表的字段数量比较大的时候,编写这些没有创意的代码的工作量非常大,而且非常容易出错。为此我们使用MyBatis Generator来自动生成这些代码。但即便这样,这些代码的可维护性依旧很差,并且让代码变得很不整洁,不利于阅读。为彻底解决这些问题,我们使用Lombok插件来简化。

6.2 Lombok概述

Lombok 是一款 Java开发插件,使 Java 开发者可以通过其定义的一些注解来消除业务工程中冗长和繁琐的代码,尤其对于简单的 Java 模型对象(POJO)。在开发环境中使用 Lombok插件后,Java 开发人员可以节省出重复编写诸如 hashCodeequals 这样的方法以及各种业务对象模型的 accessortoString 等方法的大量时间。对于这些方法,Lombok 能够在编译源代码期间自动生成这些方法,但并不会像反射那样降低程序的性能。

Lombok本质上就是一个实现了“JSR 269 API”的程序。在使用javac的过程中,它产生作用的具体流程如下:

  • javac对源代码进行分析,生成了一棵抽象语法树(AST)
  • 运行过程中调用实现了“JSR 269 API”的Lombok程序
  • 此时Lombok就对第一步骤得到的AST进行处理,找到@Data注解所在类对应的语法树(AST),然后修改该语法树(AST),增加getter和setter方法定义的相应树节点
  • javac使用修改后的抽象语法树(AST)生成字节码文件,即给class增加新的节点(代码块)

    6.3 安装与配置

    6.3.1 添加依赖

    在初始化创建项目的时候,我们已经选做了 Lombok 的依赖,在 Maven 项目的 pom.xml 文件中可以看到如下的内容:
    1. <dependency>
    2. <groupId>org.projectlombok</groupId>
    3. <artifactId>lombok</artifactId>
    4. <optional>true</optional>
    5. </dependency>
    并且在<build>部分排除了lombok的内容:
    1. <configuration>
    2. <excludes>
    3. <exclude>
    4. <groupId>org.projectlombok</groupId>
    5. <artifactId>lombok</artifactId>
    6. </exclude>
    7. </excludes>
    8. </configuration>

    6.3.2 安装Lombok插件

    由于 Lombok 仅在编译阶段生成代码,所以使用 Lombok 注解的源代码在 IDE 中会被高亮显示错误,这个问题可以通过安装插件来解决。这里不详细展开,具体安装方式可以参考:https://www.baeldung.com/lombok-ide

下面是在IEDA的插件设置中搜索Lombok插件并安装(最新的IDEA已集成):

截屏2021-05-31 上午10.29.58.png

6.4 快速上手

实际使用Lombok的时候,直接在需要的实体类加上@Data注解即可。以UserEntry.java为例,下面是原始的代码:

  1. package com.longser.union.cloud.data.model;
  2. import com.longser.utils.enums.Degree;
  3. import com.longser.utils.enums.Gender;
  4. public class UserEntry {
  5. private static final long serialVersionUID = 1L;
  6. private Long id;
  7. private String userName;
  8. private String nickName;
  9. private String mobile;
  10. private String password;
  11. private Gender gender;
  12. private Degree degree;
  13. public UserEntry() {
  14. super();
  15. }
  16. public UserEntry(String userName, String mobile) {
  17. super();
  18. this.userName = userName;
  19. this.mobile = mobile;
  20. }
  21. public UserEntry(String userName, String mobile, Gender gender) {
  22. super();
  23. this.userName = userName;
  24. this.mobile = mobile;
  25. this.gender = gender;
  26. }
  27. public Gender getGender() {
  28. return gender;
  29. }
  30. public void setGender(Gender gender) {
  31. this.gender = gender;
  32. }
  33. public Long getId() {
  34. return id;
  35. }
  36. public void setId(Long id) {
  37. this.id = id;
  38. }
  39. public String getUserName() {
  40. return userName;
  41. }
  42. public void setUserName(String userName) {
  43. this.userName = userName;
  44. }
  45. public String getMobile() {
  46. return mobile;
  47. }
  48. public void setMobile(String mobile) {
  49. this.mobile = mobile;
  50. }
  51. public String getNickName() {
  52. return nickName;
  53. }
  54. public void setNickName(String nickName) {
  55. this.nickName = nickName;
  56. }
  57. public String getPassword() {
  58. return password;
  59. }
  60. public void setPassword(String password) {
  61. this.password = password;
  62. }
  63. @Override
  64. public String toString() {
  65. return "userName " + this.userName + ", nickName " + this.nickName +
  66. ", mobile " + this.mobile + ", gender " + this.gender
  67. + ", degree " + this.degree;
  68. }
  69. }

使用Lombok的@Data注解可以使代码大大简化

  1. package com.longser.union.cloud.data.model;
  2. import com.longser.utils.enums.Degree;
  3. import com.longser.utils.enums.Gender;
  4. import lombok.Data;
  5. @Data
  6. public class UserEntry {
  7. private static final long serialVersionUID = 1L;
  8. private Long id;
  9. private String userName;
  10. private String nickName;
  11. private String mobile;
  12. private String password;
  13. private Gender gender;
  14. private Degree degree;
  15. public UserEntry() {
  16. super();
  17. }
  18. public UserEntry(String userName, String mobile) {
  19. super();
  20. this.userName = userName;
  21. this.mobile = mobile;
  22. }
  23. public UserEntry(String userName, String mobile, Gender gender) {
  24. super();
  25. this.userName = userName;
  26. this.mobile = mobile;
  27. this.gender = gender;
  28. }
  29. }

在上面的代码中,那些get、set和toString方法都不用写了。

现在我们单独编译它:
image.png
然后在左侧的结构树中找到新编译好的User.class
image.png
双击UserEntry.class在右侧的编辑窗口打开反编译后的代码,可以看到在编译的过程中自动生成了gettingsettingequalscanEqualhashCodetoString等方法.

6.5 常用注解说明

下图是Lombok的常用注解
6. 用Lombok简化实体定义 - 图4

  • @Data注解在类上,提供类所有属性的 gettingsetting 方法,此外还提供了equalscanEqualhashCodetoString 方法
  • @Value注解和@Data类似,区别在于它会把所有成员变量默认定义为private final修饰,并且不会生成set方法
  • @Log4j/@Slf4j 注在类上,提供对应的 Logger 对象,变量名为 log
  • @NoArgsConstructor注解在类上;为类提供一个无参的构造方法
  • @AllArgsConstructor注解在类上;为类提供一个全参的构造方法
  • @Setter注解在属性上;为属性提供 setter 方法
  • @Getter注解在属性上;为属性提供 getter 方法
  • @Cleanup可以关闭流
  • @Builder被注解的类加个构造者模式
  • @Synchronized加个同步锁
  • @SneakyThrows等同于try/catch 捕获异常
  • @NonNull如果给参数加个这个注解 参数为null会抛出空指针异常
  • @EqualsAndHashCode 注在类上,提供对应的 equals 和 hashCode 方法

    6.6 在MBG中使用Lombok

    前文我们在直接“手写”的代码中使用Lombok的注解来简化实体类的编写,那么能否在MyBatis Generator自动生成的类代码中使用Lombok的注解呢?答案是可以。通过MBG的插件可以实现这一点。

在自定义MBG插件的时候,需要实现org.mybatis.generator.api.Plugin接口,并提供无参构造,当然,这里推荐继承org.mybatis.generator.api.PluginAdapter,这样可以避免重写太多方法。本节主要讨论如何使在MBG中使用自定义插件来应用Lombok注解,具体代码详见教程附件

当包含自定义插件的Jar文件包已经部署到本地的Repository以后,给generatorConfig.xml增加插件定义:

  1. <!--
  2. 用LombokPlugin插件生成实用了Lombok注解的实体类,@Data注解是默认使用的,其他支持的注解
  3. 包括@Builder、@NoArgsConstructor、@@AllArgsConstructor、@Accessors、@ToString,
  4. 如果想要支持其他Lombok注解,需要修改插件代码
  5. -->
  6. <plugin type="com.longser.mybatis.generator.plugins.LombokPlugin" >
  7. <property name="builder" value="true"/>
  8. <property name="builder.builderMethodName" value="myBuilder"/>
  9. <property name="noArgsConstructor" value="false" />
  10. </plugin>

这个插件定义要放在之后,之前

按照6.2.4中的方法运行MBG,完成后可以看到实体类变成了我们想要的实用了Lambok注解的样子。

  1. package com.longser.union.cloud.data.model;
  2. import java.util.Date;
  3. import lombok.Builder;
  4. import lombok.Data;
  5. import lombok.NoArgsConstructor;
  6. /**
  7. *
  8. * 工会会员信息表
  9. *
  10. * @author david
  11. * @version 1.0
  12. * @since Tue Nov 02 13:09:25 CST 2021
  13. *
  14. * Copyright by Longser Technologies Ltd.
  15. */
  16. @Data
  17. @Builder(builderMethodName="myBuilder")
  18. @NoArgsConstructor
  19. public class Member {
  20. /**
  21. */
  22. private Integer id;
  23. /**
  24. * 所在基层工会(201)编号
  25. */
  26. private Integer unionId;
  27. /**
  28. * 分工会(300)或工会小组(400)编号
  29. */
  30. private Integer groupId;
  31. /**
  32. * 会员真实姓名
  33. */
  34. private String realName;
  35. /**
  36. * 身份证号
  37. */
  38. private String identityNumber;
  39. /**
  40. * 性别1男2女(与微信一致)
  41. */
  42. private Byte gender;
  43. /**
  44. * 出生日期
  45. */
  46. private Date birthday;
  47. /**
  48. * 生日的月份
  49. */
  50. private Byte monthOfBirthday;
  51. /**
  52. * 生肖
  53. */
  54. private Byte zodiac;
  55. /**
  56. * 民族
  57. */
  58. private Byte nation;
  59. /**
  60. * 政治面貌
  61. */
  62. private Byte party;
  63. /**
  64. * 手机号码
  65. */
  66. private String mobile;
  67. /**
  68. * 电子邮件地址
  69. */
  70. private String email;
  71. /**
  72. * 最高学位
  73. */
  74. private Byte degree;
  75. /**
  76. * 最高学历
  77. */
  78. private Byte education;
  79. /**
  80. * 就业状态
  81. */
  82. private Byte employmentState;
  83. /**
  84. * 健康状况
  85. */
  86. private Byte health;
  87. /**
  88. * 工会会籍状态
  89. */
  90. private Byte memberState;
  91. /**
  92. * 审核的状态
  93. */
  94. private Byte approvalStatus;
  95. /**
  96. * 微信用户表中的id
  97. */
  98. private Integer wechatUserId;
  99. /**
  100. * 配偶的会员编号
  101. */
  102. private Integer spouseId;
  103. /**
  104. * 数据录入用户的id
  105. */
  106. private Integer userId;
  107. /**
  108. */
  109. private Byte enabled;
  110. /**
  111. */
  112. private Byte deleted;
  113. /**
  114. */
  115. private Date createTime;
  116. /**
  117. */
  118. private Date updateTime;
  119. }

然后我们再看看新生成的映射类代码,主题内容上,它之前生成的代码没有区别,但是它多了和实体类风格相同的类注释

  1. /**
  2. *
  3. * 工会会员信息表 映射定义
  4. *
  5. * @author david
  6. * @version 1.0
  7. * @since Tue Nov 02 13:09:25 CST 2021
  8. *
  9. * Copyright by Longser Technologies Ltd.
  10. */
  11. public interface MemberMapper {

在上一节(没有使用MBG自定义插件)生成代码的时候,没有映射类级别的注释,这是因为自定义的注释生成器没有单独处理映射类的方法,但MBG插件可以做到这一点。

6.7 讨论

显而易见,Lombok有如下的优点:

  • 提高了开发效率,能通过注解的形式自动生成等常规必要的方法
  • 让代码变得简洁,不用过多关注各种繁琐、相同的方法
  • 增强了可维护性,当属性发生变化时,不需要维护与这些属性对应的getter、setter、toString等方法

当然,Lombo也是有缺点的:

  • 不支持多种参数构造器的重载
  • 虽然省去了手动创建getter/setter方法的麻烦,但大大降低了源代码的完整性

像 Lombok 这种插件,已经不仅仅是插件了,它在编译器编译时通过操作AST(抽象语法树)改变字节码生成,变相的说它就是在改变Java语法,它改变了你编写源码的方式,它不像 Spring 的依赖注入是运行时的特性,而是编译时的特性。如果一个项目有非常多这样的插件,会极大的降低阅读源代码的舒适度。

Lombok 只是省去了一些人工生成代码的麻烦,但是这些getter/setter等方法用IDE的快捷键或者类似MBG这样的生成器也可以很方便的生成。况且,有时通过给getter/setter加一点点业务代码(但通常不建议这么加),能极大的简化某些业务场景的代码。

所以用还是不用,这中间如何取舍,自然是要看项目的需要灵活运用。就作者的观点来看,还是喜欢用的。毕竟这些体力活太消耗时间,还容易出错。

版权说明:本文由北京朗思云网科技股份有限公司原创,向互联网开放全部内容但保留所有权力。