需求:
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
@Aspect
public 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;
}
@Override
public 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;
}
@Override
public String getResultCode() {
return resultCode;
}
@Override
public 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;
}
@Override
public 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;
}
@Override
public 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
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理自定义的业务异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = BizException.class)
@ResponseBody
public 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)
@ResponseBody
public 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)
@ResponseBody
public 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})
@ResponseBody
public 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)
@ResponseBody
public 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: