写自定义注解的意义在于,@RequestParam
没有对参数值进行效验,如空值;
经过测试发现,@RequestParam
只对本次请求中带不带参数名进行了效验,如参数是?userName=
,@RequestParam
则会放行,只有当userName不存在参数列表中是,才会提示报错,这就会导致很多问题,需要后台每次对数据都进行空值判断;
又或者前端人员参数为定义,传入了一个undefined
,那造成的麻烦更多,所以自定义注解 + AOP来解决这个问题。
开发的这个自定义注解,能实现和@RequestParam
一样的功能,但是进行了加强,对参数空值或者undefined进行效验,还可以实现默认值赋值
自定义注解相比怎么写应该都知道,其实就是一个接口,在接口上添加一个其他注解,实现了自己的注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Retention 作用:标识此注解的生命周期,有三种可选值
* 1.RetentionPolicy.SOURCE:在源文件中有效(即源文件保留)
* 2.RetentionPolicy.CLASS:在class文件中有效(即class保留)
* 3.RetentionPolicy.RUNTIME:在运行时有效(即运行时保留)
*
* @Target 作用:标识此注解能用在什么地方
* 1.ElementType.CONSTRUCTOR:用于构造器
* 2.ElementType.FIELD:用于属性
* 3.ElementType.LOCAL_VARIABLE:用于局部变量
* 4.ElementType.METHOD:用于方法
* 5.ElementType.PACKAGE:用于包
* 6.ElementType.PARAMETER:用于参数
* 7.ElementType.TYPE:用于类、接口(包括注解类型) 或enum声明
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ParamCheck {
/**
* 是否非空,默认不能为空
*/
boolean notNull() default true;
/**
* 默认值
* @return
*/
String defaultValue() default "";
}
自定义注解是写好了,Spring Boot项目中带有了AOP的jar包,可以不用引入,接下来写实现逻辑
import com.hm.common.util.cast.CastValueTypeUtil;
import com.hm.common.util.exception.ParamIsNullException;
import com.hm.spss.annotation.ParamCheck;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
/**
* 参数效验AOP
*/
@Component
@Aspect
public class ParamCheckAOP {
/**
* 定义有一个切入点,范围为web包下的类
*/
@Pointcut("execution(public * com.hm.spss.controller..*.*(..))")
public void checkParam() {
}
/**
* 方法执行前执行
* @param joinPoint
*/
@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内容之后切入内容(可以用来对处理返回值做一些加工处理)
* TODO 留着做以后处理返回值用
* @param joinPoint
*/
@AfterReturning(value = "checkParam()", returning = "result")
public void doAfterReturning(JoinPoint joinPoint, Object result) {
}
/**
* 参数非空校验,如果参数为空,则抛出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, ",不能为空!");
}
if ("undefined".equals(value.toString().trim())){
throw new ParamIsNullException(paramName, parameterType, ",不能为undefined!");
}
}
private Object putParam(Object value, Class<?> parameterType){
return CastValueTypeUtil.parseValue(parameterType, value.toString());
}
}
其中有两个Java类,一个是Value转换,一个是自定义异常,转换类的意义在于,默认值在赋予的时候是String字符串,但是实际赋值的时候,给参数赋值,是不确定元素,可以确实的是,这个参数肯定是八大类型中的其中一个
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 转换Object类型
*
*/
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);
}
}
}
自定义异常类
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;
}
}
以上就是完成了,非常的简单,接下来看如何使用
@GetMapping("/getRisk")
public Result getRisk(@ParamCheck(defaultValue = "1") Integer page,
@ParamCheck(defaultValue = "10") Integer size,
@ParamCheck(notNull = false) String worksName) {
return ResultUtil.success();
}
当参数名传了,参数值没传时,会抛出异常返回,参数名没传也一样的效果,并且也可以赋予默认值,具体看代码逻辑实现,也可以自行修改,只描述个大概。