1.需求/目的
- 快速抛出异常
- 异常统一处理
- 接口返回异常内容
2.使用环境
- spring boot 2.0.3
- maven
- log4j
3. 异常与提示信息枚举
- 系统的常用异常和提示信息都以枚举的形式定义,这里只定义了一些最基础的.其他信息可以创建新的枚举类进行定义.
public enum MsgEnum implements MsgEnumInterface {
SUCCESS("1","请求成功!"),
UNKNOWN_ERROR("-1","未知异常!"),
PARA_ERROR("-100","参数错误!");
String code=null;
String message=null;
private MsgEnum(String code, String message){
this.code=code;
this.message=message;
}
@Override
public String getCode() {
return this.code;
}
@Override
public String getMessage() {
return this.message;
}
- 所有异常与提示的枚举需要有一个接口,方便其他类进行识别与使用.
public interface MsgEnumInterface {
String getCode();
String getMessage();
}
4.自定义异常类
- 首先创建一个自定义异常类的父类,方便进行扩展修改,这是非必要的,也可以不定义.
这里添加了两条新的属性,code为消息代码,data为消息的附件数据(非必要).
异常类定义多个构造方法,支持不同参数,同时允许使用异常信息枚举创建异常
public class BasicException extends RuntimeException {
public final static String DEFAULT_CODE="-1";
String code;
Object data;
public BasicException() {
super();
}
public BasicException(String message) {
super(message);
code=DEFAULT_CODE;
}
public BasicException(String code, String message){
this(message);
this.code=code;
}
public BasicException(String code, String message, Object data){
this(message);
this.code=code;
this.data=data;
}
//通过消息枚举创建异常
public BasicException(MsgEnumInterface msgEnum){
this(msgEnum.getMessage());
this.code=msgEnum.getCode();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
- 创建两个异常类MsgException,ErrorException
MsgException表示一些业务上的错误信息,如:已提交申请,不能再次提交;密码不能小于13位字符等等
ErrorException表示执行过程中出现的一些错误,如:数据可能为空,进行捕获要抛出;sql执行失败等
public class ErrorException extends BasicException {
public ErrorException() {
super();
}
public ErrorException(String message) {
super(message);
this.code=DEFAULT_CODE;
}
public ErrorException(String code, String message){
this(message);
this.code=code;
}
public ErrorException(String code, String message, Object data){
this(message);
this.code=code;
this.data=data;
}
}
public class MsgException extends BasicException {
public MsgException() {
super();
}
public MsgException(String message) {
super(message);
code=DEFAULT_CODE;
}
public MsgException(String code,String message){
this(message);
this.code=code;
}
public MsgException(String code,String message,Object data){
this(message);
this.code=code;
this.data=data;
}
}
5.异常抛出工具类
-便捷的抛出异常
/**
* 用于快捷的抛出异常
* @author authstr
*/
public class Assert {
/**
* 为false时抛出一个"未知错误"的异常
* @param exp 为false抛出异常
* @time 2018年9月17日10:10:33
* @author authstr
*/
public static void isTrue(boolean exp) {
Assert.isTrue(exp, "执行过程中出现未知错误!");
}
/**
* 为false时抛出一个指定message的异常
* @param exp 为false抛出异常
* @param message 该异常要显示的信息
* @time 2018年9月17日10:13:05
* @author authstr
*/
public static void isTrue(boolean exp, String message) {
Assert.isTrue(exp, message, false);
}
public static void isTrue(boolean exp, String code,String message) {
Assert.isTrue(exp, code, message,null,false);
}
/**
* 为false时抛出一个指定message的消息异常或者错误异常
* @param exp 为false抛出异常
* @param message 该异常要显示的信息
* @param isInside 该异常是否为错误异常
* @time 2018年9月17日10:13:17
* @author authstr
*/
public static void isTrue(boolean exp, String message, boolean isInside) {
Assert.isTrue(exp, null, message,null,isInside);
}
/**
* 为false时抛出一个指定信息的消息异常或者错误异常
* @param exp 为false抛出异常
* @param message 异常的详细说明
* @param data 该异常要附带的数据信息
* @param isInside 该异常是否为错误异常
* @time 2018年9月26日10:11:19
* @author authstr
*/
public static void isTrue(boolean exp, String code, String message,Object data,boolean isInside){
if (!exp) {
if (!isInside) {
throw new MsgException(code,message,data);
}else{
throw new ErrorException(code,message,data);
}
}
}
/**
* 对象为空时,抛出一个异常
* @param o
* @time 2019年3月21日20:07:56
*/
public static void notNull(Object o){
if(o==null){
throw new ErrorException("系统未知异常:null");
}
}
}
6.控制层异常捕获
- 创建一个控制层,作为所有接口型控制层的父类,使用@ExceptionHandler(value={Exception.class})注解来捕获异常
- 局部异常处理 @Controller + @ExceptionHandler
- 全局异常处理 @ControllerAdvice + @ExceptionHandler
- 当前没有对页面跳转相关异常进行处理,因此不能使用全局异常处理
- MsgException异常需要在接口返回异常内容,提示调用者,ErrorException不需要告知调用者,返回"未知异常"即可
- 此类当前不完善,其他的异常没有进行差异化处理(暂时没有对应的需求)
/**
* 控制层的父类,主要对控制层的异常进行捕获,通过接口返回或者进行其他处理
*/
@Component
public class AbstractController {
protected Logger log = LogManager.getLogger(this.getClass());
@ExceptionHandler(value={Exception.class})
@ResponseBody
public Map exceptionHandler(Exception ex) {
HashMap<String, Object> model = new HashMap<String, Object>();
//如果是消息型异常
if (ex instanceof MsgException) {
MsgException msg=(MsgException) ex;
//尝试从异常获取错误代码,没有则使用默认的错误代码
if(StringUtils.hasText(msg.getCode())){
model.put(ControllerConstant.CODE, msg.getCode());
}else{
model.put(ControllerConstant.CODE,MsgEnum.UNKNOWN_ERROR.getCode());
}
//尝试从异常获取错误说明,没有则使用默认的错误说明
if(StringUtils.hasText(msg.getMessage())){
model.put(ControllerConstant.MSG, msg.getMessage());
}else{
model.put(ControllerConstant.MSG,MsgEnum.UNKNOWN_ERROR.getMessage());
}
//获取异常详细数据,如果有,进行返回
if(msg.getData()!=null){
model.put(ControllerConstant.DATA, msg.getData());
}
} else if(ex instanceof ErrorException){
//出现错误型异常,在返回值显示未知异常,具体错误消息进行记录和打印
model.put(ControllerConstant.CODE,MsgEnum.UNKNOWN_ERROR.getCode());
model.put(ControllerConstant.MSG,MsgEnum.UNKNOWN_ERROR.getMessage());
ex.printStackTrace();
log.error("系统执行出现异常:" + ex.getMessage());
}
else {
model.put(ControllerConstant.CODE,MsgEnum.UNKNOWN_ERROR.getCode());
model.put(ControllerConstant.MSG,MsgEnum.UNKNOWN_ERROR.getMessage());
ex.printStackTrace();
this.log.error( "系统出现未知异常 :" + ex.getMessage());
}
return model;
}
public Map success() {
HashMap<String, String> map = new HashMap<String, String>();
map.put(MsgEnum.SUCCESS.getCode(),MsgEnum.SUCCESS.getMessage());
return map;
}
}
7.使用示例
- 控制层使用
@RestController
@RequestMapping("user")
public class UserController extends AbstractController {
@Autowired
UserService userService;
@RequestMapping("query")
public Map query(QueryCommonPage query, HttpServletRequest request) {
Map m = super.success();
RequestPara para=new RequestPara(request);
m.put("page", userService.query(query,para));
return m;
}
@RequestMapping("save")
public Map save(BaseUser user) {
Map m = super.success();
m.put("data", userService.save(user));
return m;
}
}
- 服务层使用
@Service
public class UserServiceImpl extends AbstractService implements UserService{
@Autowired
private UserDao userDao;
@Override
public Page query(QueryCommonPage query, RequestPara para){
return userDao.query(query,para);
}
@Transactional
@Override
public String save(BaseUser user){
Assert.isTrue(StringUtils.hasText(user.getUsername()),"用户名不能为空");
Assert.isTrue(StringUtils.hasText(user.getPassword()),"密码不能为空");
Integer id=(Integer) super.save(user);
//密码md5加密后,用数据自身id作为盐,再次加密
String md5Password= null;
try {
md5Password = Md5Salt.sec(String.valueOf(id), DigestUtils.md5Hex(user.getPassword()));
} catch (Exception e) {
throw new ErrorException("-3","加密出错!");//异常提示实际应写在枚举里
}
BaseUser afterUser=super.get(BaseUser.class,id);
Assert.notNull(afterUser);
afterUser.setPassword(md5Password);
super.updata(afterUser);
return "保存成功";
}
}
网友评论