在Spring中,通常会有一个Controller类,它会处理来自客户端的请求。比如,
客户端想要访问一个用户的信息,也许只是一个展示用户资料的前台发来的请求。
那么,你会在Controller里面写这样一个方法:
@RequestMapping(value={/id},method=RequestMethod.GET)
public @ResponseBody Item ItemID
(@PathVariable long id){
//@PathVariable 为路径参数
return ItemRepository.findOne(id);
}
很棒,如果数据库存在ID=参数id的用户,那么这个请求会执行的很完美。
为了程序的健壮性,假如这个用户不存在呢?你也许会想到从数据库中
把用户资料取出来,然后判断对象的引用是否为空!这很好,但并不是一个很完美的解决方案—-因为错误处理代码与业务执行逻辑耦合!
耦合度高的代码会在以后的维护中是一个噩梦
那么,接下来,就是一个错误处理的解决方案,它能够把错误处理的逻辑提取出来,对所有出现某个特定异常的Controller中的方法进行异常处理。
耦合度更低的解决方案
异常描述对象+异常类+异常处理方法(句柄)+重构后的Ccntroller中的方法
异常描述对象是一个普通的Java对象–POJO,它存储描述异常的信息
Error.java
public class Error{
private int code;
private String message;
public Error(int code,String message){
this.code=code;
this.message=message;
}
public int getCode(){
}
public String getMessage(){
}
}
异常类-ItemNotFoundException
@ResponseStatus(value=HttpStatus.NOT_FOUND,
reason="Item not found")
public class ItemNotFoundException
extends RuntimeException{
//这是一个普通的异常类,带有请求失败响应值404,继承未检查异常
//所以你不被强制去catch它
}
一个异常处理句柄,是当请求处理方法出现异常时能够被调用的异常处理方法,当然,在此情况下为ItemNotFouindHandler.java
,@ExceptionHandler
注解代表这是一个异常处理句柄,注解中传递的异常类
//表示这个句柄处理ItemNotFoundException
@ExceptionHandler(ItemNotFoundException.class)
//表示这个句柄处理时,请求响应值会被设定为HttpStatus.NOT_FOUND-404
@ResponseStatus(HttpStatus.NOT_FOUND)
public Error ItemNotFound(ItemNotFoundException e){
long Item=e.getItemId();
return new Error(4,"Item["+ItemId+"] not Found");
}
这一切都妥妥的了,接下来,终于可以重构我们的方法来把讨厌的异常处理去掉,
这跟我们想在Controller类ItemID()
中的实现的逻辑完全没有关系好不啦!
请求处理方法
@RequestMapping(value={/id},method=RequestMethod.GET)
public @ResponseBody Item ItemID(@PathVariable long id){
//@PathVariable 为路径参数,比如/item/23456,23456就是id
//跟参数/item?id=23456不一样
Spittle item=ItemRepository.findOne(id);
if(item==null){ throw new ItemNotFoundException(id);}
return item;
}
以上这就是重构后的代码。除了抛出一个异常之外,好像没有任何异常处理代码。
听起来,好像没有省下多少工作量,但是注意当一个Controller中的方法多的时候
,你要对**这个**Controller类中的方法中出现的此类异常都加一个这样的处理,这样的固定模式代码应该被提取出来。
这个Controller被加粗的原因是,这个模式可能出现在多个Controller类中,而我们上述的解决方案,异常处理句柄方法ItemNotFoundException()
只能对它所在的类出现的ItemNotFoundException
类异常进行处理,这对我们想要的作用域–所有Controller中出现的ItemNotFoundException
异常进行处理来说,太小了!
作用域更广的Advising Controller
Spring 3.2+引入的Advising Controller类能够处理所有Controller中出现的ItemNotFoundException
,太棒了!
@ControllerAdvice //表示这是一个Advising Controller
public class ItemControllerAdvice{
//处理ItemNotFoundException的异常处理句柄
@ExceptionHandler(ItemNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public Error ItemNotFound(ItemNotFoundException e){
long Item=e.getItemId();
return new Error(4,"Item["+ItemId+"] not Found");
}
@ExceptionHandler(ItemExpiredException.class)
public String ItemExpiredHandler(){
return "error/ItemExpired";
}
}
那么按照以上的思路来讲,你应该能够提取一定的设计模式,好的设计模式简洁,
强大,好维护!你会发现你最好将所有关于Item实体的异常全部写在一个Advise Controller
类中,哈哈,unbelivable,异常处理本质上是一种面向切面的编程思想,当然,Spring引入@ControllerAdvice
是为了防止糟糕的切面命名吧,这种比较特定的切面注解也能够闻名知其义!