Spring Boot【定制化】~ AOP统一结果处理以及异常拦截
1、简介
2、配置步骤
2.1、创建一个spring boot项目(idea)
完整项目结构!
2.2、导入依赖(需要使用到简化环境搭建)
org.springframework.boot spring-boot-starter-aop com.alibaba fastjson 1.2.76 org.apache.commons commons-lang3 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.junit.vintage junit-vintage-engine
2.3、yml
注意:我们这个测试不需要yml(写上来防止你们疑惑!)
spring: application: name: springboot-day02-aop-exception #服务名称(项目名)
2.4、需要使用的工具类
注意:直接复制使用,工具类方法太多所以直接抽取几个用到的方法(不是重点)。
1、StringUtils
/** * @author cms * @version 1.0.0.0 * @Date: 2022/5/21 20:10 */public class StringUtils extends org.apache.commons.lang3.StringUtils{ public static boolean isEmpty(String str) { return isNull(str) || "".equals(str.trim()); } public static boolean isNotEmpty(String str) { return !isEmpty(str); } public static boolean isNull(Object object) { return object == null; } public static boolean isNotNull(Object object) { return !isNull(object); } }
2、ServletUtils
/** * @author cms * @version 1.0.0.0 * @Date: 2022/5/21 20:10 */public class ServletUtils{ /** * 响应字符串 * @param response 响应 * @param string 内容 * @return 结果 */ public static String parseString(HttpServletResponse response, String string) { try { response.setStatus(200); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().print(string); } catch (IOException e) { e.printStackTrace(); } return null; }}
2.5、需要使用的枚举以及状态
1、ErrorEnum
/** * @author cms * @version 1.0.0.0 * @Date: 2022/3/16 13:17 */public enum ErrorEnum { /* 参数错误P0100 - P0200 */ P_ERROR_NULL("P0100", "参数%s不能为空"), P_NOT_EXIST("P0101", "%s不存在"), P_FORMAT_ERROR("P0102", "%s格式不正确"), /* 系统内部错误S0100 - Sxxx */ S_CATCH_AUTH("S0104", "%s"), S_TOKEN_EXPIRED("S0104", "Token过期"), S_ERROR("S0105","系统异常 练习管理员!佳佳吖!"), ; private String code; private String message; ErrorEnum() { } ErrorEnum(String code, String message) { this.code = code; this.message = message; } public String getCode() { return code; } public String getMessage() { return message; } /** * @param message 填充内容 * @return 结果 */ public String getMessage(String message) { if (StringUtils.isNotEmpty(message)) { return String.format(this.message, message); } return ""; } public void setCode(String code) { this.code = code; } public void setMessage(String message) { this.message = message; } }
2、状态码常量类
/** * 返回状态码 * * @author cms * @version 1.0.0.0 * @Date: 2022/5/21 20:05 */public class HttpStatus { /** * 操作成功 */ public static final int SUCCESS = 200; /** * 不允许的http方法 */ public static final int BAD_METHOD = 405; /** * 系统内部错误 */ public static final int ERROR = 500; }
2.6、统一结果返回格式对象
/** * 统一结果格式返回控制类 * * @author cms * @version 1.0.0.0 * @Date: 2022/5/21 19:23 */public class ResultReturn extends LinkedHashMap{ public static final Date newDateTime = new Date(); public static final String CODE = "code"; public static final String MSG = "message"; public static final String DATE_TIME = "datetime"; public static final String DATA = "data"; public ResultReturn() {} public ResultReturn(Object code, String msg, Date datetime, Object data) { super.put(CODE, code); super.put(MSG, msg); super.put(DATE_TIME, datetime.getTime()); if (StringUtils.isNotNull(data)) { super.put(DATA, data); } } /** * 成功状态码统一:200 * * @return 返回结果 */ public static Object success(Object data) { return new ResultReturn(200, "success", newDateTime, data); } public static Object success() { return ResultReturn.success(null); } /** * 错误返回 * * @param errorEnum 错误枚举信息 * @return 返回结果 */ public static Object error(ErrorEnum errorEnum) { return new ResultReturn(errorEnum.getCode(), errorEnum.getMessage(), new Date(), null); } public static Object error(Object code, String errorMsg) { return new ResultReturn(code, errorMsg, newDateTime, null); } public static ResultReturn error(RuanjiaExceptionHandler e) { return new ResultReturn(e.getCode(), e.getMessage(), newDateTime, null); }}
2.7、Controller统一返回结果(AOP)
1、自定义注解
作用:如果有不想被Aop拦截的controller方法,就是用该注解标识。
/** * 默认返回结果 * * @author cms * @version 1.0.0.0 * @Date: 2022/5/21 19:17 */@Target({ ElementType.TYPE, ElementType.METHOD }) //用于方法上@Retention(RetentionPolicy.RUNTIME) //保留政策:运行时@Documentedpublic @interface OriginalReturn{}
2、统一结果拦截类(AOP切面类)
作用(重点):使用Aop实现统一结果拦截。
/** * 使用Aop拦截统一结果 * * @author cms * @version 1.0.0.0 * @Date: 2022/5/21 19:15 */@Aspect@Slf4j@Componentpublic class ResultAspectj{ @Pointcut("execution(public * com.ruanjia.controller..*.*(..))") public void result() {}; /** AOP拦截所有Controller控制器结果 */ @Around("result()") public Object afterReturningMethod(ProceedingJoinPoint point) throws Throwable { Object proceed = point.proceed(); /* 模型和视图 -> 直接放行 */ if (proceed instanceof ModelAndView || proceed instanceof View) { return proceed; } Method method = ((MethodSignature) point.getSignature()).getMethod(); /* 方法上标志@OriginalReturn -> 直接放行 */ if (method.isAnnotationPresent(OriginalReturn.class)) { return proceed; } return ResultReturn.success(proceed); }}
2.8、全局异常拦截
1、自定义异常类
作用:处理系统业务异常(这里还可以细写,可以根据自己需要增加自定义异常!)
/** * 自定义异常 * * @author cms * @version 1.0.0.0 * @Date: 2022/5/21 20:02 */@Datapublic class RuanjiaExceptionHandler extends RuntimeException{ private Object code; private String message; public RuanjiaExceptionHandler(ErrorEnum errorCodeEnum) { this.code = errorCodeEnum.getCode(); this.message = errorCodeEnum.getMessage(); } /** * @param errorCodeEnum 枚举 * @param message 替换字符串 */ public RuanjiaExceptionHandler(ErrorEnum errorCodeEnum, String message) { super(errorCodeEnum.getMessage(message)); if (StringUtils.isNotEmpty(message)) { this.message = errorCodeEnum.getMessage(message); } else { this.message = errorCodeEnum.getMessage(); } this.code = errorCodeEnum.getCode(); } /** * @param message 异常提示信息 */ public RuanjiaExceptionHandler(String message) { this.code = HttpStatus.ERROR; this.message = message; }}
2、全局异常类
作用:拦截所有Controller异常
/** * 全局异常捕获: * @ControllerAdvice 包含@Component,可以被扫描到。统一处理异常。 * @ExceptionHandler(Exception.class) 用在方法上面表示遇到这个异常就执行以下方法。 * * @author cms * @version 1.0.0.0 * @Date: 2022/5/21 19:15 */@Slf4j@ControllerAdvicepublic class SystemGlobalExceptionHandler{ /** * 自定义异常 * * @param e 算术异常 * @param response 响应结果 */ @ExceptionHandler(RuanjiaExceptionHandler.class) public void ruanjiaExceptionHandler(RuanjiaExceptionHandler e, HttpServletResponse response) { log.error("===========RuanjiaExceptionHandler(自定义异常)=========="); e.printStackTrace(); ServletUtils.parseString(response, JSON.toJSONString(ResultReturn.error(e.getCode(), e.getMessage()))); } /** * 算术异常 * * @param e 异常对象 * @param response 响应结果 */ @ExceptionHandler(ArithmeticException.class) public void handlerArithmeticException(ArithmeticException e, HttpServletResponse response) { log.error("===========ArithmeticException(算术异常)=========="); e.printStackTrace(); ServletUtils.parseString(response, JSON.toJSONString(ResultReturn.error(HttpStatus.ERROR, e.getMessage()))); } /** * GET/POST不支持异常 * * @param e 异常对象 * @param request 请求 * @param response 响应结果 */ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public void handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request, HttpServletResponse response) { log.error("===========HttpRequestMethodNotSupportedException(GET/POST 不支持异常)=========="); String requestURI = request.getRequestURI(); log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); e.printStackTrace(); ServletUtils.parseString(response, JSON.toJSONString(ResultReturn .error(HttpStatus.BAD_REQUEST,"请求地址【"+requestURI+"】,不支持【"+e.getMethod()+"】请求"))); }}
此致!配置完成,进入测试环节!
2.9、编写Controller测试类
/** * @author cms * @version 1.0.0.0 * @Date: 2022/5/21 19:11 */@RestControllerpublic class TestController{ /** * 统一结果返回测试 * * @return 结果 */ @GetMapping("/result") public Object result() { return "同意结果返回测试!"; } /** * 算术异常(全局异常处理程序拦截) * * @return 结果 */ @GetMapping("/error1") public Object error() { int a = 10/0; return a; } /** * 自定义异常拦截(全局异常处理程序拦截) * * @param username 用户名 * @return 返回结果 */ @GetMapping("/error2") public Object error2(String username) { if (StringUtils.isEmpty(username)) { throw new RuanjiaExceptionHandler(ErrorEnum.S_CATCH_AUTH, "用户名不能为空!"); } return username; }}
效果图:
1、统一结果返回测试
2、系统算术异常拦截
3、自定义异常拦截
结束语:不要半途而废!