全局异常处理用于前端统一错误显示,还有就是后端分页插件,使用的mybatis-plus。
SpringBoot的项目已经对有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理。SpringBoot中有一个ControllerAdvice的注解,使用该注解表示开启了全局异常的捕获,我们只需在自定义一个方法使用ExceptionHandler注解然后定义捕获异常的类型即可对这些捕获的异常进行统一的处理。
@ControllerAdvice,是Spring3.2提供的新注解,它是一个Controller增强器,可对controller中被 @RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理,需要配合@ExceptionHandler使用。当将异常抛到controller时,可以对异常进行统一处理,规定返回的json格式或是跳转到一个错误页面
@ExceptionHandler注解我们一般是用来自定义异常的,可以认为它是一个异常拦截器(处理器)。@ExceptionHandler必须要求该方法必须要和出现问题的控制器在一个类中,才能生效。如果别的类报空指针异常,则不会进该异常处理方法,必须要结合@ControllerAdvice和@ExceptionHandler一起使用。
自定义异常类,用于处理我们发生的业务异
package com.zjlovelt.exception;
import com.zjlovelt.common.result.Result;
/**
* 自定义异常类
*/
public class AllException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误信息
*/
protected String errorMsg;
public AllException() {
super();
}
public AllException(Result errorInfoInterface) {
this.errorMsg = errorInfoInterface.getMsg();
}
public AllException(Result errorInfoInterface, Throwable cause) {
this.errorMsg = errorInfoInterface.getMsg();
}
public AllException(String errorMsg) {
super(errorMsg);
this.errorMsg = errorMsg;
}
public AllException(String errorMsg, Throwable cause) {
super(errorMsg, cause);
this.errorMsg = errorMsg;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getMessage() {
return errorMsg;
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
自定义全局异常处理类
package com.zjlovelt.exception;
import com.zjlovelt.common.result.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.sql.SQLException;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理自定义的业务异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = AllException.class)
@ResponseBody
public Result bizExceptionHandler(HttpServletRequest req, AllException e){
logger.error("URL : " + req.getRequestURL().toString());
logger.error("HTTP_METHOD : " + req.getMethod());
logger.error("发生业务异常!原因是:{}",e.getErrorMsg());
return Result.fail(e.getErrorMsg());
}
/**
* 处理空指针的异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =NullPointerException.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, NullPointerException e) {
logger.error("URL : " + req.getRequestURL().toString());
logger.error("HTTP_METHOD : " + req.getMethod());
logger.error("发生空指针异常!原因是:",e);
return Result.fail("系统异常");
}
/**
* 处理索引越界异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =IndexOutOfBoundsException.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, IndexOutOfBoundsException e){
logger.error("URL : " + req.getRequestURL().toString());
logger.error("HTTP_METHOD : " + req.getMethod());
logger.error("索引越界异常!原因是:",e);
return Result.fail("系统异常");
}
/**
* 处理类未找到异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =ClassNotFoundException.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, ClassNotFoundException e) {
logger.error("URL : " + req.getRequestURL().toString());
logger.error("HTTP_METHOD : " + req.getMethod());
logger.error("发生类未找到异常!原因是:",e);
return Result.fail("系统异常");
}
/**
* 处理SQL异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = SQLException.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, SQLException e) {
logger.error("URL : " + req.getRequestURL().toString());
logger.error("HTTP_METHOD : " + req.getMethod());
logger.error("发生SQL异常!原因是:",e);
return Result.fail("系统异常");
}
/**
* 处理IO异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = IOException.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, IOException e) {
logger.error("URL : " + req.getRequestURL().toString());
logger.error("HTTP_METHOD : " + req.getMethod());
logger.error("发生IO异常!原因是:",e);
return Result.fail("系统异常");
}
/**
* 处理其他异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =Exception.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, Exception e){
logger.error("URL : " + req.getRequestURL().toString());
logger.error("HTTP_METHOD : " + req.getMethod());
logger.error("未知异常!原因是:",e);
return Result.fail("系统异常");
}
}
Result统一返回类
controller层写个测试接口:
@GetMapping(value = "/test")
public String test(String a){
int b = Integer.parseInt(a);
System.out.print(b);
return "";
}
最后我们来调用这个接口试一下这些异常的处理情况
首先试一下空指针异常
然后类型转换异常
我们直接抛出所有异常信息,对用户而言是非常不友好的。可以看到遇到这些异常不再是返回异常信息而是自己定义的内容了,对用户比较友好些。
自义定全局异常处理除了可以处理上述的数据格式之外,也可以处理页面的跳转,只需在新增的异常方法的返回处理上填写该跳转的路径并不使用ResponseBody 注解即可。在GlobalExceptionHandler类中使用的是ControllerAdvice注解,而非RestControllerAdvice注解,如果是用的RestControllerAdvice注解,它会将数据自动转换成JSON格式,这种于Controller和RestController类似,所以我们在使用全局异常处理的之后可以进行灵活的选择处理。
后端使用mybatis-plus框架的分页插件
因为之前是用的mybatis,引入mybatis-plus后注意一些修改
删掉pom中mybatis-spring-boot-starter的配置,把配置文件中的mybatis配置统统改为
#springboot整合mybatis的配置
#指定实体类位置,在mapper中就不用写全路径
mybatis-plus.type-aliases-package: com.zjlovelt.entity
#映射mapper的位置,和dao层接口对应,一定要对应mapper映射xml文件的所在路径
mybatis-plus.mapper-locations=classpath:mapping/*.xml
# 配置打印 SQL 语句
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
然后就是配置分页插件
新增一个配置类MybatisConfig
package com.zjlovelt.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
分页模型:采用Page作为统一的简单分页模型,封装了查询数据列表、总数、每页显示条数(默认 10)、当前页、排序字段信息等内容。
直接使用
controller:
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@RequestMapping("/pageList")
public Page pageList(Long pageIndex, Long pageSize,SysDataDictionary sysDataDictionary) {
if(pageIndex==null || pageIndex == 0){
pageIndex = 1L;
}
if(pageSize==null || pageSize == 0){
pageSize = 10L;
}
Page page = new Page<>(pageIndex, pageSize);
return dataDictionaryService.selectByPage(page, sysDataDictionary);
}
service:
public Page selectByPage(Page page, SysDataDictionary dataDictionary) {
List list = sysDataDictionaryMapper.selectByPage(page, dataDictionary);
page.setRecords(list);
return page;
}
mapper:
List selectByPage(Page page, SysDataDictionary dataDictionary);
这样一个分页查询就写好了,用postman调用测试
传入参数pageIndex 为当前页码 pageSize为每页显示条数
结果:
最后查询结果,可以看到返回数据已经分好页了。