在我们学习springboot的时候,老师讲到的异常处理机制在商业应用大多不会被采用,因为在实际开发中,为了方便管理,通常都是将所有的异常放在一个controller统一管理。并且处理的异常类都是我们自己定义的异常,不仅对新手小白编程十分友好,而且在团队中开发也能起到一定的作用。
异常处理之前,我们通常都是自己try catch捕捉异常,但是这样处理通常费时费力,而且代码不美观。
public void changeAvatar() {
try {
Integer uid = 20;
String username = "头像管理员";
String avatar = "/upload/avatar.png";
iUserService.changeAvatar(uid, username, avatar);
System.out.println("OK.");
} catch (ServiceException e) {
System.out.println(e.getClass().getSimpleName());
System.out.println(e.getMessage());
}
}
这是控制层调用服务层的时普通的异常处理机制,我们的异常没有统一的管理,代码维护的时候也比较费时费力。
使用统一管理的异常处理方式我们可以先定义自己的异常类并统一集中管理,这里的异常应该是服务层操作数据库的异常,所以创建异常类可以有一个服务异常父类,其余操作数据库异常去继承这个服务异常类,其中服务异常要先继承运行时异常,因为程序在运行启动的时候,我们操作数据库要是查出数据不存在,或着数据被逻辑删除,那么就可以抛出我们自己定义的异常。
服务异常父类
public class ServiceException extends RuntimeException{
public ServiceException() {
super();
}
public ServiceException(String message) {
super(message);
}
public ServiceException(String message, Throwable cause) {
super(message, cause);
}
public ServiceException(Throwable cause) {
super(cause);
}
protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
创建出这个异常并且继承RuntimeException运行时 按住CRTL+O键,然后重载上面五个方法,这五个方法基本就包含了我们所有异常处理的样本方法。
剩下的自定义异常就看开发的时候我们需要什么异常,就自己定义什么异常,然后剩下的异常类被定义后就继承上面的服务异常类,这里我举两个例子,InsertException和PasswordNotMatchException这两个异常类的处理方法。
public class InsertException extends ServiceException{
public InsertException() {
super();
}
public InsertException(String message) {
super(message);
}
public InsertException(String message, Throwable cause) {
super(message, cause);
}
public InsertException(Throwable cause) {
super(cause);
}
protected InsertException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
public class PasswordNotMatchException extends ServiceException{
public PasswordNotMatchException() {
super();
}
public PasswordNotMatchException(String message) {
super(message);
}
public PasswordNotMatchException(String message, Throwable cause) {
super(message, cause);
}
public PasswordNotMatchException(Throwable cause) {
super(cause);
}
protected PasswordNotMatchException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
如果你需要其余的类,依旧创建异常类然后继承服务异常类然后重载五个方法
当定义好所有的异常管理类之后,怎么管理呢?标题所提到的ExceptionHandler注解是一个非常方便的注解,通过这个注解,可以将异常处理集中处理,不必在其余Controller中捕捉异常和抛出异常。
这里就不介绍怎么使用,我们直接将代码放出,自己在开发项目的时候可以自己复制上去就行,然后定义出自己需要管理的异常类,先创建一个BaseController
public class BaseController {
/** 操作成功的状态码 */
public static final int OK = 200;
/**
* 从HttpSession对象中获取uid
* @param session HttpSession对象
* @return 当前登录的用户的id
*/
protected final Integer getUidFromSession(HttpSession session) {
return Integer.valueOf(session.getAttribute("uid").toString());
}
/**
* 从HttpSession对象中获取用户名
* @param session HttpSession对象
* @return 当前登录的用户名
*/
protected final String getUsernameFromSession(HttpSession session) {
return session.getAttribute("username").toString();
}
/** @ExceptionHandler用于统一处理方法抛出的异常 */
@ExceptionHandler({ServiceException.class, FileUploadException.class})
public JsonResult handleException(Throwable e) {
JsonResult result = new JsonResult(e);
if (e instanceof UsernameDuplicateException){
result.setState(4000);
result.setMessage("用户名已经被占用");
}else if (e instanceof InsertException){
result.setState(5000);
result.setMessage("注册时产生未知的异常");
}else if (e instanceof UserNotFoundException){
result.setState(5001);
result.setMessage("用户不存在,请从新输入");
}else if (e instanceof PasswordNotMatchException){
result.setState(5002);
result.setMessage("密码错误,请重新输入");
}
return result;
}
}
当写到这一步异常处理就完成了,小伙伴不需要担心怎么使用,不知道为什么这样就能做到异常处理,我们也写一个服务的代码例子马,让小伙伴对比一下传统的异常处理和和注解处理异常的例子。我们以经典的登录功能作为例子
服务层登录功能代码
public User login(String username, String password) {
// 调用userMapper的findByUsername()方法,根据参数username查询用户数据
User result = userMapper.findByUsername(username);
// 判断查询结果是否为null
if (result==null){
// 是:抛出UserNotFoundException异常
throw new UserNotFoundException("用户不存在,请重新输入");
}
if (result.getIsDelete()==1){
throw new UserNotFoundException("用户不存在,请重新输入");
}
//运行到这里就说明查询到用户了
String salt = result.getSalt();
String md5Password = getMd5Password(password, salt);
//这里的密码可以都换成md5比较,不用将md5密码转换为普通密码比较
if (!result.getPassword().equals(md5Password)){
throw new PasswordNotMatchException("密码错误,请重新输入");
}
//不过返回的对象不建议直接返回result,因为里面封装了太多的数据,建议从新创建数据
// 创建新的User对象
User user = new User();
// 将查询结果中的uid、username、avatar封装到新的user对象中
user.setUid(result.getUid());
user.setUsername(result.getUsername());
user.setAvatar(result.getAvatar());
// 返回新的user对象
return user;
}
这样处理的时候就不需要在花费额外的心思处理异常啦,数据库操作异常的时候,直接抛出异常就行啦