SpringBoot 自定義注意 + AOP實現參數效驗,默認值賦值

SpringBoot 自定義注意 + AOP實現參數效驗,默認值賦值,和@RequestParam相同功能

寫自定義註解的意義在於,@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包,可以不用引入,接下來寫實現邏輯

用AOP實現註解功能

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();
    }

結語

當參數名傳了,參數值沒傳時,會拋出異常返回,參數名沒傳也一樣的效果,並且也可以賦予默認值,具體看代碼邏輯實現,也可以自行修改,只描述個大概。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章