说到Lombok,大部分人可能都停留在@Data注解可以同时生成getter/settertoStringequalshashCode等方法;@NoArgsConstructor/@AllArgsConstructor可以生成无参和全参构造器。确实Lombok的设计初衷也是为了生成代码。

有些人因为需要安装插件而拒绝了Lombok,觉得这样会要求团队内所有成员都安装插件。诚然,这是一个还说得过去的理由。但是这并不足以让我们拒绝LombokLombok所做的事情是在改变Java,有些人觉得它改变了Java语法是破坏了Java,那有没有想过,也许Java一直以来的设计就是不好的存在呢?业务系统中出现的无数getter/setter方法,动辄几百行的实体类,阅读体验真的好吗?Lombok是在改变Java,我们只不过是在拥抱变化。Spring Boot已经把Lombok加入预定义依赖中了;Apache ShardingSphere的开发规范中明确要求优先使用Lombok代替构造器、getter/setter方法和log变量;就连Java14也借鉴了Lombok思想提出了record语法。我们为什么还要去拒绝Lombok呢?

所以,从今天起,开始拥抱Lombok吧!

一个冷门注解:@RequiredArgsConstructor

很多人都知道@RequiredArgsConstructor这个注解,但是却从未使用过。如果你也一样,那就得好好看看我这篇文章了,看完之后也许你就会发现新的大陆!

Spring的依赖注入

故事还得从Spring的依赖注入开始说起…

依赖注入的两大变体:

  • 构造函数注入
  • setter方法注入

但是现在业务开发中却很少使用这两种依赖注入方式,而是使用@Autowired注解进行自动装配。不管是业务层Service还是仓储层MyBatis框架的Mapper或者其它等等,业务代码写起来大概是下面这个样子的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class AutowiredService {
@Autowired
private IAService iaService;
@Autowired
private IBService ibService;
@Autowired
private ICService icService;
@Autowired
private AMapper aMapper;
@Autowired
private BMapper bMapper;

...

// do business
}

这样的@Autowired字段少则三五个,多则一二十个。而且在IDEA中,XXXService上面的@Autowired注解会有一条黄色的下划线:Field injection is not recommended ;对于XXXMapper类来说,变量名还会有一条红色的下划线:Could not autowire. No beans of 'XXXMapper' type found.,虽然运行起来不会报错,但这样的警告和报错着实让人看起来很不舒服。

有人说可以用@Resource注解来代替@Autowired,这样就不会出现警告和报错了。诚然,这样做可以解决问题,@Resource默认优先按beanName自动注入,@Autowired默认优先按beanType自动注入。这是解决问题的一种方式。

@Resource注解是JSR-250规范提供的注解,而@Autowired注解是Spring提供的。这点不足以称为暇疵。

Lombok的优雅

如何使用Lombok来解决这个问题?

我们关注一下构造函数注入的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Service
public class ConstructorService {
private final IAService iaService;
private final IBService ibService;
private final ICService icService;
private final AMapper aMapper;
private final BMapper bMapper;

@Autowired
public ConstructorService(IAService iaService, IBService ibService, ICService icService, AMapper aMapper, BMapper bMapper) {
this.iaService = iaService;
this.ibService = ibService;
this.icService = icService;
this.aMapper = aMapper;
this.bMapper = bMapper;
}
}

它要求成员变量是final类型,同时构造函数上的@Autowired是可选的。

@RequiredArgsConstructor刚好是为被声明为final的字段生成构造器的注解。

于是我们的写法就变得优(zhuang)雅(bi)起来:

1
2
3
4
5
6
7
8
9
10
11
@Service
@RequiredArgsConstructor
public class LombokService {
private final IAService iaService;
private final IBService ibService;
private final ICService icService;
private final AMapper aMapper;
private final BMapper bMapper;

// do business
}

这样编译后的字节码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class LombokService {
private final IAService iaService;
private final IBService ibService;
private final ICService icService;
private final AMapper aMapper;
private final BMapper bMapper;

public LombokService(IAService iaService, IBService ibService, ICService icService, AMapper aMapper, BMapper bMapper) {
this.iaService = iaService;
this.ibService = ibService;
this.icService = icService;
this.aMapper = aMapper;
this.bMapper = bMapper;
}
}

这不就是标准的Spring构造函数注入方式吗?甚至我们还可以把@Autowired注解给加上:

1
2
3
4
5
6
7
8
9
10
11
@Service
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
public class LombokService {
private final IAService iaService;
private final IBService ibService;
private final ICService icService;
private final AMapper aMapper;
private final BMapper bMapper;

// do business
}

这样编译后的构造函数上就会加上@Autowired注解。

onConstructor = @_(@Autowired)中间的@_还可以继续加下划线_

1
@RequiredArgsConstructor(onConstructor = @______________________________(@Autowired))

即使你像上面这样写,代码也不会报错,能正常运行。

这样我们就只用写一个注解就可以完成任意多个属性的注入,Lombok就是这么优(zhuang)雅(bi)!所以请尽情地拥抱Lombok吧!