1. 为什么要有链式编程

当我们想方便的将其进行实例化和赋值,往往会调用其构造方法来创建对象。
但一个POJO有很多参数的时候,我们想灵活的为其赋值,往往需要调用其set方法,但在并发条件下,这种做法会将非完全初始化的对象暴漏给外界的其他线程,这样做会产生数据不全的风险。
所以我们采用更加安全的建造者模式,通过链式表达式创建新的对象。

2. 注意

1、@Builder不支持父类对象构建,建议使用@superBuilder进行代替。
2、@Accessors需要将属性chain = true,如此可将操作默认为get&set,令其可以支持链式表达。

3. @Builder

  1. @SuperBuilder
  2. public class Person1 {
  3. private Long id;
  4. private String username;
  5. private String passwd;
  6. }

编译后效果:

  1. package com.testLombok.pojo;
  2. public class Person1 {
  3. private Long id;
  4. private String username;
  5. private String passwd;
  6. protected Person1(Person1.Person1Builder<?, ?> b) {
  7. this.id = b.id;
  8. this.username = b.username;
  9. this.passwd = b.passwd;
  10. }
  11. public static Person1.Person1Builder<?, ?> builder() {
  12. return new Person1.Person1BuilderImpl();
  13. }
  14. private static final class Person1BuilderImpl extends Person1.Person1Builder<Person1, Person1.Person1BuilderImpl> {
  15. private Person1BuilderImpl() {
  16. }
  17. protected Person1.Person1BuilderImpl self() {
  18. return this;
  19. }
  20. public Person1 build() {
  21. return new Person1(this);
  22. }
  23. }
  24. public abstract static class Person1Builder<C extends Person1, B extends Person1.Person1Builder<C, B>> {
  25. private Long id;
  26. private String username;
  27. private String passwd;
  28. public Person1Builder() {
  29. }
  30. protected abstract B self();
  31. public abstract C build();
  32. public B id(Long id) {
  33. this.id = id;
  34. return this.self();
  35. }
  36. public B username(String username) {
  37. this.username = username;
  38. return this.self();
  39. }
  40. public B passwd(String passwd) {
  41. this.passwd = passwd;
  42. return this.self();
  43. }
  44. public String toString() {
  45. return "Person1.Person1Builder(id=" + this.id + ", username=" + this.username + ", passwd=" + this.passwd + ")";
  46. }
  47. }
  48. }

由此可以看出,在使用@Builder时,需要创建一个Build对象,再掉用其对应Build方法生成我们需要得到的对象,这样会占用更多的内存空间,代码也相对变得冗余。

具体使用代码:

  1. EmsUser emsUser = EmsUser.builder().mobile("").email("").build();

4. @Accessors

  1. @Accessors(chain = true)
  2. @Data
  3. public class Person2 {
  4. private Long id;
  5. private String username;
  6. private String passwd;
  7. }

编译后效果:

  1. package com.testLombok.pojo;
  2. public class Person2 {
  3. private Long id;
  4. private String username;
  5. private String passwd;
  6. public Person2() {
  7. }
  8. public Long getId() {
  9. return this.id;
  10. }
  11. public String getUsername() {
  12. return this.username;
  13. }
  14. public String getPasswd() {
  15. return this.passwd;
  16. }
  17. public Person2 setId(Long id) {
  18. this.id = id;
  19. return this;
  20. }
  21. public Person2 setUsername(String username) {
  22. this.username = username;
  23. return this;
  24. }
  25. public Person2 setPasswd(String passwd) {
  26. this.passwd = passwd;
  27. return this;
  28. }
  29. public boolean equals(Object o) {
  30. if (o == this) {
  31. return true;
  32. } else if (!(o instanceof Person2)) {
  33. return false;
  34. } else {
  35. Person2 other = (Person2)o;
  36. if (!other.canEqual(this)) {
  37. return false;
  38. } else {
  39. label47: {
  40. Object this$id = this.getId();
  41. Object other$id = other.getId();
  42. if (this$id == null) {
  43. if (other$id == null) {
  44. break label47;
  45. }
  46. } else if (this$id.equals(other$id)) {
  47. break label47;
  48. }
  49. return false;
  50. }
  51. Object this$username = this.getUsername();
  52. Object other$username = other.getUsername();
  53. if (this$username == null) {
  54. if (other$username != null) {
  55. return false;
  56. }
  57. } else if (!this$username.equals(other$username)) {
  58. return false;
  59. }
  60. Object this$passwd = this.getPasswd();
  61. Object other$passwd = other.getPasswd();
  62. if (this$passwd == null) {
  63. if (other$passwd != null) {
  64. return false;
  65. }
  66. } else if (!this$passwd.equals(other$passwd)) {
  67. return false;
  68. }
  69. return true;
  70. }
  71. }
  72. }
  73. protected boolean canEqual(Object other) {
  74. return other instanceof Person2;
  75. }
  76. public int hashCode() {
  77. int PRIME = true;
  78. int result = 1;
  79. Object $id = this.getId();
  80. int result = result * 59 + ($id == null ? 43 : $id.hashCode());
  81. Object $username = this.getUsername();
  82. result = result * 59 + ($username == null ? 43 : $username.hashCode());
  83. Object $passwd = this.getPasswd();
  84. result = result * 59 + ($passwd == null ? 43 : $passwd.hashCode());
  85. return result;
  86. }
  87. public String toString() {
  88. return "Person2(id=" + this.getId() + ", username=" + this.getUsername() + ", passwd=" + this.getPasswd() + ")";
  89. }
  90. }

借助简单的return this实现链式表达,代码实现简单规范,原理清晰透彻。

具体使用代码:

  1. EmsUser emsUser = new EmsUser().setEmail("").setMobile("");

5. 如何选择

由于@Accessors仍为一个不稳定版本的注解,在长期迭代的项目中不推荐使用,建议使用@Builder

6. @Accessors使用中的空指针异常

  1. java.lang.NullPointerException
  2. at net.sf.cglib.core.ReflectUtils.getMethodInfo(ReflectUtils.java:424)
  3. at net.sf.cglib.beans.BeanCopier$Generator.generateClass(BeanCopier.java:133)
  4. at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
  5. at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
  6. at net.sf.cglib.beans.BeanCopier$Generator.create(BeanCopier.java:90)
  7. at net.sf.cglib.beans.BeanCopier.create(BeanCopier.java:50)

当借助工具对实体类进行反射浅拷贝时,会校验其set返回值是否为void,此时使用@Accessors便会出现异常

解决办法:

  1. `BeanCopier.create` will throw exception when the `source` or `target` use `@Accessors(chain = true)` in lombok.
  2. But `org.springframework.beans.BeanUtils.copyProperties(source, target);` has no problem.