需求:
1.自定义注解 ,用于标注需要进行校验的参数
2.AOP配合自定义注解使用
3.实现公共的返回参
4.实现全局异常捕获
所有的类
1.pom.xml依赖
<!--aop--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.9</version></dependency><!--fastjson--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.58</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
2.新建自定义注解,ParamCheck.java :
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/*** 自定义注解* @author beyond* @since 2021/4/11*/@Target(ElementType.PARAMETER) // 使用于参数@Retention(RetentionPolicy.RUNTIME)public @interface ParamCheck {/*** 是否非空,默认不能为空* 要求参数不为空,默认开启,可以自己传*/boolean notNull() default true;/**** 默认值*/String defaultValue() default "";}
3.新建 参数校验的AOP实现类,ParamValidAop.java
import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.AfterReturning;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import java.lang.annotation.Annotation;import java.lang.reflect.Method;/*** 参数校验的AOP实现类* @author beyond* @since 2021/4/11*/@Component@Aspectpublic class ParamValidAop {/*** 定义有一个切入点,范围为web包下的类*/@Pointcut("execution(public * com.beyond.demo.controller..*.*(..))")public void checkParam() {}@Before("checkParam()")public void doBefore(JoinPoint joinPoint) {}/*** 检查参数是否为空*/@Around("checkParam()")public Object doAround(ProceedingJoinPoint pjp) throws Throwable {MethodSignature signature = ((MethodSignature) pjp.getSignature());//得到拦截的方法Method method = signature.getMethod();//获取方法参数注解,返回二维数组是因为某些参数可能存在多个注解Annotation[][] parameterAnnotations = method.getParameterAnnotations();if (parameterAnnotations == null || parameterAnnotations.length == 0) {return pjp.proceed();}//获取方法参数名String[] paramNames = signature.getParameterNames();//获取参数值Object[] paranValues = pjp.getArgs();//获取方法参数类型Class<?>[] parameterTypes = method.getParameterTypes();for (int i = 0; i < parameterAnnotations.length; i++) {for (int j = 0; j < parameterAnnotations[i].length; j++) {//如果该参数前面的注解不为空并且是ParamCheck的实例,并且notNull()=true,并且默认值为空,则进行非空校验if (parameterAnnotations[i][j] != null && parameterAnnotations[i][j] instanceof ParamCheck && ((ParamCheck) parameterAnnotations[i][j]).notNull() && StringUtils.isEmpty(((ParamCheck)parameterAnnotations[i][j]).defaultValue())) {paramIsNull(paramNames[i], paranValues[i], parameterTypes[i] == null ? null : parameterTypes[i].getName());break;}//如果该参数前面的注解不为空并且是ParamCheck的实例,并且默认值不为空,并且参数值为空,则进行赋默认值if(parameterAnnotations[i][j] != null && parameterAnnotations[i][j] instanceof ParamCheck && !StringUtils.isEmpty(((ParamCheck)parameterAnnotations[i][j]).defaultValue()) && (paranValues[i] == null || StringUtils.isEmpty(paranValues[i].toString()))){paranValues[i] = putParam(((ParamCheck)parameterAnnotations[i][j]).defaultValue(), parameterTypes[i]);}}}return pjp.proceed(paranValues);}/*** 在切入点return内容之后切入内容(可以用来对处理返回值做一些加工处理)** @param joinPoint*/@AfterReturning("checkParam()")public void doAfterReturning(JoinPoint joinPoint) {}/*** 参数非空校验,如果参数为空,则抛出ParamIsNullException异常* @param paramName* @param value* @param parameterType*/private void paramIsNull(String paramName, Object value, String parameterType) {if (value == null || "".equals(value.toString().trim())) {throw new ParamIsNullException(paramName, parameterType,"参数为空");}}private Object putParam(Object value, Class<?> parameterType) {return CastValueTypeUtil.parseValue(parameterType, value.toString());}}
4.校验参数里面使用到的参数转换工具类,CastValueTypeUtil.java:
import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;/*** 转换object类型* @author beyond* @since 2021/4/11*/public class CastValueTypeUtil {public static Object parseValue(Class<?> parameterTypes, String value) {if(value==null || value.trim().length()==0){return null;}value = value.trim();if (Byte.class.equals(parameterTypes) || Byte.TYPE.equals(parameterTypes)) {return parseByte(value);} else if (Boolean.class.equals(parameterTypes) || Boolean.TYPE.equals(parameterTypes)) {return parseBoolean(value);}/* else if (Character.class.equals(fieldType) || Character.TYPE.equals(fieldType)) {return value.toCharArray()[0];}*/ else if (String.class.equals(parameterTypes)) {return value;} else if (Short.class.equals(parameterTypes) || Short.TYPE.equals(parameterTypes)) {return parseShort(value);} else if (Integer.class.equals(parameterTypes) || Integer.TYPE.equals(parameterTypes)) {return parseInt(value);} else if (Long.class.equals(parameterTypes) || Long.TYPE.equals(parameterTypes)) {return parseLong(value);} else if (Float.class.equals(parameterTypes) || Float.TYPE.equals(parameterTypes)) {return parseFloat(value);} else if (Double.class.equals(parameterTypes) || Double.TYPE.equals(parameterTypes)) {return parseDouble(value);} else if (Date.class.equals(parameterTypes)) {return parseDate(value);} else {throw new RuntimeException("request illeagal type, type must be Integer not int Long not long etc, type=" + parameterTypes);}}public static Byte parseByte(String value) {try {value = value.replaceAll(" ", "");return Byte.valueOf(value);} catch(NumberFormatException e) {throw new RuntimeException("parseByte but input illegal input=" + value, e);}}public static Boolean parseBoolean(String value) {value = value.replaceAll(" ", "");if (Boolean.TRUE.toString().equalsIgnoreCase(value)) {return Boolean.TRUE;} else if (Boolean.FALSE.toString().equalsIgnoreCase(value)) {return Boolean.FALSE;} else {throw new RuntimeException("parseBoolean but input illegal input=" + value);}}public static Integer parseInt(String value) {try {value = value.replaceAll(" ", "");return Integer.valueOf(value);} catch(NumberFormatException e) {throw new RuntimeException("parseInt but input illegal input=" + value, e);}}public static Short parseShort(String value) {try {value = value.replaceAll(" ", "");return Short.valueOf(value);} catch(NumberFormatException e) {throw new RuntimeException("parseShort but input illegal input=" + value, e);}}public static Long parseLong(String value) {try {value = value.replaceAll(" ", "");return Long.valueOf(value);} catch(NumberFormatException e) {throw new RuntimeException("parseLong but input illegal input=" + value, e);}}public static Float parseFloat(String value) {try {value = value.replaceAll(" ", "");return Float.valueOf(value);} catch(NumberFormatException e) {throw new RuntimeException("parseFloat but input illegal input=" + value, e);}}public static Double parseDouble(String value) {try {value = value.replaceAll(" ", "");return Double.valueOf(value);} catch(NumberFormatException e) {throw new RuntimeException("parseDouble but input illegal input=" + value, e);}}public static Date parseDate(String value) {try {String datePattern = "yyyy-MM-dd HH:mm:ss";SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);return dateFormat.parse(value);} catch(ParseException e) {throw new RuntimeException("parseDate but input illegal input=" + value, e);}}}
5. 新建一个自定义异常,专门用于校验参数为空的时候抛出,ParamIsNullException.java:
package com.beyond.demo.paramCheck;/*** 自定义异常,用于校验参数为空的时候抛出** @author beyond* @since 2021/4/11*/public class ParamIsNullException extends RuntimeException {private final String parameterName;private final String parameterType;private final String message;public ParamIsNullException(String parameterName, String parameterType, String message) {super();this.parameterName = parameterName;this.parameterType = parameterType;this.message = message;}@Overridepublic String getMessage() {return "请求参数类型:" + this.parameterType + ",参数名: \'" + this.parameterName + message;}public final String getParameterName() {return this.parameterName;}public final String getParameterType() {return this.parameterType;}}
至此,自定义注解以及AOP已经实现完毕
6.统一的返回参以及全局异常捕获
/*** 此接口用于返回码枚举使用* @author beyond* @since 2021/4/11*/public interface BaseErrorInfoInterface {/** 错误码*/String getResultCode();/** 错误描述*/String getResultMsg();}
返回码的枚举类,CommonEnum.java:
/*** 返回码的枚举类** @author beyond* @since 2021/4/11*/public enum CommonEnum implements BaseErrorInfoInterface {// 数据操作错误定义SUCCESS("200", "成功!"),BODY_NOT_MATCH("400", "请求的数据格式不符!"),SIGNATURE_NOT_MATCH("401", "请求的数字签名不匹配!"),NOT_FOUND("404", "未找到该资源!"),INTERNAL_SERVER_ERROR("500", "服务器内部错误!"),SERVER_BUSY("503", "服务器正忙,请稍后再试!"),REQUEST_METHOD_SUPPORT_ERROR("40001", "当前请求方法不支持");/*** 错误码*/private String resultCode;/*** 错误描述*/private String resultMsg;CommonEnum(String resultCode, String resultMsg) {this.resultCode = resultCode;this.resultMsg = resultMsg;}@Overridepublic String getResultCode() {return resultCode;}@Overridepublic String getResultMsg() {return resultMsg;}}
定义一个简单的返回体专用类,ResultBody.java:
package com.beyond.demo.paramCheck;import com.alibaba.fastjson.JSONObject;/*** 返回体专用类** @author beyond* @since 2021/4/11*/public class ResultBody {/*** 响应代码*/private String code;/*** 响应消息*/private String message;/*** 响应结果*/private Object result;public ResultBody() {}public ResultBody(BaseErrorInfoInterface errorInfo) {this.code = errorInfo.getResultCode();this.message = errorInfo.getResultMsg();}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public Object getResult() {return result;}public void setResult(Object result) {this.result = result;}/*** 成功** @return*/public static ResultBody success() {return success(null);}/*** 成功** @param data* @return*/public static ResultBody success(Object data) {ResultBody rb = new ResultBody();rb.setCode(CommonEnum.SUCCESS.getResultCode());rb.setMessage(CommonEnum.SUCCESS.getResultMsg());rb.setResult(data);return rb;}/*** 失败*/public static ResultBody error(BaseErrorInfoInterface errorInfo) {ResultBody rb = new ResultBody();rb.setCode(errorInfo.getResultCode());rb.setMessage(errorInfo.getResultMsg());rb.setResult(null);return rb;}/*** 失败*/public static ResultBody error(String code, String message) {ResultBody rb = new ResultBody();rb.setCode(code);rb.setMessage(message);rb.setResult(null);return rb;}/*** 失败*/public static ResultBody error(String message) {ResultBody rb = new ResultBody();rb.setCode("-1");rb.setMessage(message);rb.setResult(null);return rb;}@Overridepublic String toString() {return JSONObject.toJSONString(this);}}
自定义的业务异常类,和全局异常捕获类
package com.beyond.demo.paramCheck;/*** 自定义的业务异常类** @author beyond* @since 2021/4/11*/public class BizException extends RuntimeException {private static final long serialVersionUID = 1L;/*** 错误码*/protected String errorCode;/*** 错误信息*/protected String errorMsg;public BizException() {super();}public BizException(BaseErrorInfoInterface errorInfoInterface) {super(errorInfoInterface.getResultCode());this.errorCode = errorInfoInterface.getResultCode();this.errorMsg = errorInfoInterface.getResultMsg();}public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {super(errorInfoInterface.getResultCode(), cause);this.errorCode = errorInfoInterface.getResultCode();this.errorMsg = errorInfoInterface.getResultMsg();}public BizException(String errorMsg) {super(errorMsg);this.errorMsg = errorMsg;}public BizException(String errorCode, String errorMsg) {super(errorCode);this.errorCode = errorCode;this.errorMsg = errorMsg;}public BizException(String errorCode, String errorMsg, Throwable cause) {super(errorCode, cause);this.errorCode = errorCode;this.errorMsg = errorMsg;}public String getErrorCode() {return errorCode;}public void setErrorCode(String errorCode) {this.errorCode = errorCode;}public String getErrorMsg() {return errorMsg;}public void setErrorMsg(String errorMsg) {this.errorMsg = errorMsg;}public String getMessage() {return errorMsg;}@Overridepublic Throwable fillInStackTrace() {return this;}}
package com.beyond.demo.paramCheck;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.web.HttpRequestMethodNotSupportedException;import org.springframework.web.bind.MissingServletRequestParameterException;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.bind.annotation.RestControllerAdvice;import javax.servlet.http.HttpServletRequest;/*** 全局异常捕获类* @author beyond* @since 2021/4/11*/@RestControllerAdvicepublic class GlobalExceptionHandler {private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 处理自定义的业务异常* @param req* @param e* @return*/@ExceptionHandler(value = BizException.class)@ResponseBodypublic ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){logger.error("发生业务异常!原因是:{}",e.getErrorMsg());return ResultBody.error(e.getErrorCode(),e.getErrorMsg());}/*** 处理空指针的异常* @param req* @param e* @return*/@ExceptionHandler(value =NullPointerException.class)@ResponseBodypublic ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){logger.error("发生空指针异常!原因是:",e);return ResultBody.error(CommonEnum.BODY_NOT_MATCH);}/*** 处理请求方法不支持的异常* @param req* @param e* @return*/@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)@ResponseBodypublic ResultBody exceptionHandler2(HttpServletRequest req, HttpRequestMethodNotSupportedException e){logger.error("发生请求方法不支持异常!原因是:",e);return ResultBody.error(CommonEnum.REQUEST_METHOD_SUPPORT_ERROR);}/*** 处理请求方法不支持的异常* @param req* @param e* @return*/@ExceptionHandler(value = {ParamIsNullException.class, MissingServletRequestParameterException.class})@ResponseBodypublic ResultBody exceptionHandler3(HttpServletRequest req, Exception e){logger.error("参数为空!原因是:",e);return ResultBody.error(CommonEnum.SIGNATURE_NOT_MATCH.getResultCode(),e.getMessage());}/*** 处理其他异常* @param req* @param e* @return*/@ExceptionHandler(value =Exception.class)@ResponseBodypublic ResultBody exceptionHandler(HttpServletRequest req, Exception e){logger.error("未知异常!原因是:",e);return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);}}
接下来看看这套实现的东西效果如何:
给需要的校验的参数加上我们的自定义注解即可,如:
@GetMapping("/hello1")public ResultBody hello1(/*@ParamCheck(defaultValue = "banbu") */@ParamCheck String name) {return ResultBody.success();}

那么我们试试不传参数name:
