问题描述
在大热的Spring Boot 2.0
中,在将原来的泛型改为了Optional
,旨在让我们的代码更简洁。
实践
Optional
很简单的一个类,点开它的源代码,其中所有的方法都是与null
相关联的。
这是一个简化我们处理null
的类。
它就是一个容器,其中有我们想要的对象,但是该对象有时候会是空,所以我们需要使用Optional
封装好的方法来获取需要的对象。从而很好地避免了空指针异常。
错误示范
我看到网上很多人这么写:
catRepository.findById(id).get();
下面是Spring Boot 1.5
的写法,那请问:如果上面的写法是正确的,那为什么还要大费周章设计一个Optional
呢?
catRepository.findOne(id);
分析
通过get
是能获取到我们需要的对象。
但是看看get
的源代码,这样写,抛出了NoSuchElementException
异常,这个异常我们没法在全局中处理它。
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
为什么不能再全局中处理呢?大家可以思考一下:
因为NoSuchElementException
覆盖的范围太广了,只要是Optional
中有null
就会抛出NoSuchElementException
,很多情况下都会造成这种异常,那我们究竟要给用户一个什么样的提示信息好呢?最后还是给出500
服务器异常,那异常处理的意义何在呢?
所以我们需要用Optional
来抛出一个有特定范围的能被全局准确处理的异常。
Cat cat = catRepository.findOne(id);
if (null == cat) {
throw new EntityNotFoundException("该实体找不到");
}
return cat;
思想都是一样,我们不过是用一种更简洁的写法实现上面的功能。
实现
没错,就像下面一样,我们只需要一行代码!
public Cat findById(Long id) {
return catRepository.findById(id).orElseThrow(EntityNotFoundException::new);
}
findById
返回一个Optional
,然后调用该对象的orElseThrow
方法。
orElseThrow
方法,如果存在,返回包含的值,否则抛出异常。
该方法的参数是一个lamda
表达式。这里就不深究lamda
表达式的几种类型了,如果感兴趣可以自行研究下Function
、Consumer
、Predicate
、Supplier
这四个函数式接口的区别。
所以传一个lamda
表达式进去,然后IDEA
会给出警告:
Can be replaced with method reference
该lamda
表达式能被一个方法引用代替,Alt + Enter
,我们最终的代码就长这样:
这里的::
是lamda
表达式的一种简写,是Java8
中的新特性,看着可能有点奇怪,原来,编译器比程序员聪明多了。
异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity entityNotFoundHandler() {
return new ResponseEntity<>("您要找的实体不存在", HttpStatus.NOT_FOUND);
}
}
写个控制器增强,全局处理异常,这里的RestControllerAdvice
又是一个组合注解:
处理异常,同时以Json
的格式返回。
@Test
public void findById() throws Exception {
this.mockMvc.perform(get("/cat/1"))
.andDo(print());
}
写个控制器的单元测试,查询一个不存在的实体,运行,看控制台的打印输出:
一劳永逸
一劳永逸,这是我们最喜欢的东西了。
return catRepository.findById(id).orElseThrow(EntityNotFoundException::new);
以后再查询,就这一行,再也不用去判断null
了。
NotNull
正所谓条条大路通罗马,对null
的一劳永逸,我们这样实现,别人也可以那样实现。
如果你在Spring
的项目中打过断点调试的话,那我断定你一定见过下面这行代码:
Assert.notNull();
以下是该方法的源码,注意这里的Assert
是org.springframework.util
包下的:
刚方法用于判断null
,如果为空,则抛出异常。
随便点开一个方法,都会在第一行为不该为null
的参数进行判断。
这里,不禁对整个框架肃然起敬。
总结
之前一直抱怨Java
更新的太快:
其实,Java
的每次更新,都是为了我们更简洁优雅的代码而努力。去看看官方的描述,Java
让我们将更多的精力放在think
上,而不是code
上。