AOP + 注解 實現(xiàn)通用的接口參數(shù)校驗

大家好四康,我是小悟

寫移動端接口的時候搪搏,為了校驗參數(shù),傳統(tǒng)的做法是加各種判斷闪金,寫了很多重復(fù)的代碼疯溺,而且也不美觀论颅。為了增加代碼復(fù)用性,美觀的校驗參數(shù)囱嫩,采用AOP + 注解的方式來實現(xiàn)接口的參數(shù)校驗(使用攔截器也可以實現(xiàn))恃疯,在需要校驗參數(shù)的方法上加上自定義的注解即可。

代碼文件目錄

image.png

代碼實現(xiàn)

自定義異常:RRException

public class RRException extends RuntimeException {
  private static final long serialVersionUID = 1L;
  
    private String msg;
    private int code = 500;
    
    public RRException(String msg) {
    super(msg);
    this.msg = msg;
  }
  
  public RRException(String msg, Throwable e) {
    super(msg, e);
    this.msg = msg;
  }
  
  public RRException(String msg, int code) {
    super(msg);
    this.msg = msg;
    this.code = code;
  }
  
  public RRException(String msg, int code, Throwable e) {
    super(msg, e);
    this.msg = msg;
    this.code = code;
  }
  public String getMsg() {
    return msg;
  }
  public void setMsg(String msg) {
    this.msg = msg;
  }
  public int getCode() {
    return code;
  }
  public void setCode(int code) {
    this.code = code;
  }
}

全局異常處理器:RRExceptionHandler

@RestControllerAdvice
public class RRExceptionHandler {
  private Logger logger = LoggerFactory.getLogger(getClass());
  /**
   * 處理自定義異常
   */
  @ExceptionHandler(RRException.class)
  public R handleRRException(RRException e){
    R r = new R();
    r.put("code", e.getCode());
    r.put("msg", e.getMessage());
    return r;
  }
}

響應(yīng)數(shù)據(jù)封裝:R

public class R extends HashMap<String, Object> {
  private static final long serialVersionUID = 1L;
  
  public R() {
    put("code", 0);
    put("msg", "success");
  }
  
  public static R error() {
    return error(500, "未知異常墨闲,請聯(lián)系管理員");
  }
  
  public static R error(String msg) {
    return error(500, msg);
  }
  public static R ok(int code, String msg) {
    R r = new R();
    r.put("code", code);
    r.put("msg", msg);
    return r;
  }
  public static R error(int code, String msg) {
    R r = new R();
    r.put("code", code);
    r.put("msg", msg);
    return r;
  }
  public static R ok(String msg) {
    R r = new R();
    r.put("msg", msg);
    return r;
  }
  
  public static R ok(Map<String, Object> map) {
    R r = new R();
    r.putAll(map);
    return r;
  }
  
  public static R ok(List<Object> list) {
    R r = new R();
    r.put("msg", list);
    return r;
  }
  
  public static R ok() {
    return new R();
  }
  @Override
  public R put(String key, Object value) {
    super.put(key, value);
    return this;
  }
}

注解:ParamCheck

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RUNTIME)
public @interface ParamCheck {
    //字段校驗規(guī)則今妄,格式:字段名+校驗規(guī)則+冒號+錯誤信息,例如:name<11:名字必須少于11位
    String[] value();
}

工具類:ReflectionUtil

public class ReflectionUtil {
    private static final String SETTER_PREFIX = "set";
    private static final String GETTER_PREFIX = "get";
    private static final String CGLIB_CLASS_SEPARATOR = "$$";
    private static Logger logger = LoggerFactory.getLogger(ReflectionUtil.class);
    /**
     * 調(diào)用Getter方法.
     */
    public static Object invokeGetter(Object obj, String propertyName) {
        String getterMethodName = GETTER_PREFIX
                + StringUtils.capitalize(propertyName);
        return invokeMethod(obj, getterMethodName, new Class[]{},
                new Object[]{});
    }
    /**
     * 直接調(diào)用對象方法, 無視private/protected修飾符.
     * 用于一次性調(diào)用的情況鸳碧,否則應(yīng)使用getAccessibleMethod()函數(shù)獲得Method后反復(fù)調(diào)用. 同時匹配方法名+參數(shù)類型盾鳞,
     */
    public static Object invokeMethod(final Object obj,
                                      final String methodName, final Class<?>[] parameterTypes,
                                      final Object[] args) {
        Method method = getAccessibleMethod(obj, methodName, parameterTypes);
        if (method == null) {
            throw new IllegalArgumentException("Could not find method ["
                    + methodName + "] on target [" + obj + "]");
        }
        try {
            return method.invoke(obj, args);
        } catch (Exception e) {
            throw convertReflectionExceptionToUnchecked(e);
        }
    }
    /**
     * 循環(huán)向上轉(zhuǎn)型, 獲取對象的DeclaredMethod,并強制設(shè)置為可訪問. 如向上轉(zhuǎn)型到Object仍無法找到, 返回null.
     * 匹配函數(shù)名+參數(shù)類型。
     * <p>
     * 用于方法需要被多次調(diào)用的情況. 先使用本函數(shù)先取得Method,然后調(diào)用Method.invoke(Object obj, Object...
     * args)
     */
    public static Method getAccessibleMethod(final Object obj,
                                             final String methodName, final Class<?>... parameterTypes) {
        Validate.notNull(obj, "object can't be null");
        Validate.notBlank(methodName, "methodName can't be blank");
        for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType
                .getSuperclass()) {
            try {
                Method method = searchType.getDeclaredMethod(methodName,
                        parameterTypes);
                makeAccessible(method);
                return method;
            } catch (NoSuchMethodException e) {
                // Method不在當(dāng)前類定義,繼續(xù)向上轉(zhuǎn)型
            }
        }
        return null;
    }
    /**
     * 改變private/protected的方法為public瞻离,盡量不調(diào)用實際改動的語句腾仅,避免JDK的SecurityManager抱怨。
     */
    public static void makeAccessible(Method method) {
        if ((!Modifier.isPublic(method.getModifiers()) || !Modifier
                .isPublic(method.getDeclaringClass().getModifiers()))
                && !method.isAccessible()) {
            method.setAccessible(true);
        }
    }
    /**
     * 將反射時的checked exception轉(zhuǎn)換為unchecked exception.
     */
    public static RuntimeException convertReflectionExceptionToUnchecked(
            Exception e) {
        if ((e instanceof IllegalAccessException)
                || (e instanceof IllegalArgumentException)
                || (e instanceof NoSuchMethodException)) {
            return new IllegalArgumentException(e);
        } else if (e instanceof InvocationTargetException) {
            return new RuntimeException(
                    ((InvocationTargetException) e).getTargetException());
        } else if (e instanceof RuntimeException) {
            return (RuntimeException) e;
        }
        return new RuntimeException("Unexpected Checked Exception.", e);
    }
}

對不同類型的值進行校驗:DiffTypeParamCheck

*/
* @description 對不同類型的值進行校驗
 */
public class DiffTypeParamCheck {
    /**
     * 是否不為空
     * @param value       字段值
     * @param operatorNum 操作數(shù)套利,這里不需要推励,只是為了參數(shù)統(tǒng)一
     * @return 是否不為空
     */
    public static Boolean isNotNull(Object value, String operatorNum) {
        Boolean isNotNull = Boolean.TRUE;
        Boolean isStringNull = (value instanceof String) && StringUtils.isEmpty((String) value);
        Boolean isCollectionNull = (value instanceof Collection) && CollectionUtils.isEmpty((Collection) value);
        if (value == null) {
            isNotNull = Boolean.FALSE;
        } else if (isStringNull || isCollectionNull) {
            isNotNull = Boolean.FALSE;
        }
        return isNotNull;
    }
    /**
     * 是否大于
     * @param value       字段值
     * @param operatorNum 操作數(shù)
     * @return 是否大于
     */
    public static Boolean isGreaterThan(Object value, String operatorNum) {
        Boolean isGreaterThan = Boolean.FALSE;
        if (value == null) {
            return Boolean.FALSE;
        }
        Boolean isStringGreaterThen = (value instanceof String) && ((String) value).length() > Integer.valueOf(operatorNum);
        Boolean isLongGreaterThen = (value instanceof Long) && ((Long) value) > Long.valueOf(operatorNum);
        Boolean isIntegerGreaterThen = (value instanceof Integer) && ((Integer) value) > Integer.valueOf(operatorNum);
        Boolean isShortGreaterThen = (value instanceof Short) && ((Short) value) > Short.valueOf(operatorNum);
        Boolean isFloatGreaterThen = (value instanceof Float) && ((Float) value) > Float.valueOf(operatorNum);
        Boolean isDoubleGreaterThen = (value instanceof Double) && ((Double) value) > Double.valueOf(operatorNum);
        Boolean isCollectionGreaterThen = (value instanceof Collection) && ((Collection) value).size() > Integer.valueOf(operatorNum);
        if (isStringGreaterThen || isLongGreaterThen || isIntegerGreaterThen ||
                isShortGreaterThen || isFloatGreaterThen || isDoubleGreaterThen || isCollectionGreaterThen) {
            isGreaterThan = Boolean.TRUE;
        }
        return isGreaterThan;
    }
    /**
     * 是否大于等于
     * @param value       字段值
     * @param operatorNum 操作數(shù)
     * @return 是否大于等于
     */
    public static Boolean isGreaterThanEqual(Object value, String operatorNum) {
        Boolean isGreaterThanEqual = Boolean.FALSE;
        if (value == null) {
            return Boolean.FALSE;
        }
        Boolean isStringGreaterThenEqual = (value instanceof String) && ((String) value).length() >= Integer.valueOf(operatorNum);
        Boolean isLongGreaterThenEqual = (value instanceof Long) && ((Long) value) >= Long.valueOf(operatorNum);
        Boolean isIntegerGreaterThenEqual = (value instanceof Integer) && ((Integer) value) >= Integer.valueOf(operatorNum);
        Boolean isShortGreaterThenEqual = (value instanceof Short) && ((Short) value) >= Short.valueOf(operatorNum);
        Boolean isFloatGreaterThenEqual = (value instanceof Float) && ((Float) value) >= Float.valueOf(operatorNum);
        Boolean isDoubleGreaterThenEqual = (value instanceof Double) && ((Double) value) >= Double.valueOf(operatorNum);
        Boolean isCollectionGreaterThenEqual = (value instanceof Collection) && ((Collection) value).size() >= Integer.valueOf(operatorNum);
        if (isStringGreaterThenEqual || isLongGreaterThenEqual || isIntegerGreaterThenEqual ||
                isShortGreaterThenEqual || isFloatGreaterThenEqual || isDoubleGreaterThenEqual || isCollectionGreaterThenEqual) {
            isGreaterThanEqual = Boolean.TRUE;
        }
        return isGreaterThanEqual;
    }
    /**
     * 是否少于
     * @param value       字段值
     * @param operatorNum 操作數(shù)
     * @return 是否少于
     */
    public static Boolean isLessThan(Object value, String operatorNum) {
        Boolean isLessThan = Boolean.FALSE;
        if (value == null) {
            return Boolean.FALSE;
        }
        Boolean isStringLessThen = (value instanceof String) && ((String) value).length() < Integer.valueOf(operatorNum);
        Boolean isLongLessThen = (value instanceof Long) && ((Long) value) < Long.valueOf(operatorNum);
        Boolean isIntegerLessThen = (value instanceof Integer) && ((Integer) value) < Integer.valueOf(operatorNum);
        Boolean isShortLessThen = (value instanceof Short) && ((Short) value) < Short.valueOf(operatorNum);
        Boolean isFloatLessThen = (value instanceof Float) && ((Float) value) < Float.valueOf(operatorNum);
        Boolean isDoubleLessThen = (value instanceof Double) && ((Double) value) < Double.valueOf(operatorNum);
        Boolean isCollectionLessThen = (value instanceof Collection) && ((Collection) value).size() < Integer.valueOf(operatorNum);
        if (isStringLessThen || isLongLessThen || isIntegerLessThen ||
                isShortLessThen || isFloatLessThen || isDoubleLessThen || isCollectionLessThen) {
            isLessThan = Boolean.TRUE;
        }
        return isLessThan;
    }
    /**
     * 是否少于等于
     * @param value       字段值
     * @param operatorNum 操作數(shù)
     * @return 是否少于等于
     */
    public static Boolean isLessThanEqual(Object value, String operatorNum) {
        Boolean isLessThanEqual = Boolean.FALSE;
        if (value == null) {
            return Boolean.FALSE;
        }
        Boolean isStringLessThenEqual = (value instanceof String) && ((String) value).length() <= Integer.valueOf(operatorNum);
        Boolean isLongLessThenEqual = (value instanceof Long) && ((Long) value) <= Long.valueOf(operatorNum);
        Boolean isIntegerLessThenEqual = (value instanceof Integer) && ((Integer) value) <= Integer.valueOf(operatorNum);
        Boolean isShortLessThenEqual = (value instanceof Short) && ((Short) value) <= Short.valueOf(operatorNum);
        Boolean isFloatLessThenEqual = (value instanceof Float) && ((Float) value) <= Float.valueOf(operatorNum);
        Boolean isDoubleLessThenEqual = (value instanceof Double) && ((Double) value) <= Double.valueOf(operatorNum);
        Boolean isCollectionLessThenEqual = (value instanceof Collection) && ((Collection) value).size() <= Integer.valueOf(operatorNum);
        if (isStringLessThenEqual || isLongLessThenEqual || isIntegerLessThenEqual ||
                isShortLessThenEqual || isFloatLessThenEqual || isDoubleLessThenEqual || isCollectionLessThenEqual) {
            isLessThanEqual = Boolean.TRUE;
        }
        return isLessThanEqual;
    }
    /**
     * 是否不等于
     * @param value       字段值
     * @param operatorNum 操作數(shù)
     * @return 是否不等于
     */
    public static Boolean isNotEqual(Object value, String operatorNum) {
        Boolean isNotEqual = Boolean.FALSE;
        if (value == null) {
            return Boolean.FALSE;
        }
        Boolean isStringNotEqual = (value instanceof String) && !value.equals(operatorNum);
        Boolean isLongNotEqual = (value instanceof Long) && !value.equals(Long.valueOf(operatorNum));
        Boolean isIntegerNotEqual = (value instanceof Integer) && !value.equals(Integer.valueOf(operatorNum));
        Boolean isShortNotEqual = (value instanceof Short) && !value.equals(Short.valueOf(operatorNum));
        Boolean isFloatNotEqual = (value instanceof Float) && !value.equals(Float.valueOf(operatorNum));
        Boolean isDoubleNotEqual = (value instanceof Double) && !value.equals(Double.valueOf(operatorNum));
        Boolean isCollectionNotEqual = (value instanceof Collection) && ((Collection) value).size() != Integer.valueOf(operatorNum);
        if (isStringNotEqual || isLongNotEqual || isIntegerNotEqual ||
                isShortNotEqual || isFloatNotEqual || isDoubleNotEqual || isCollectionNotEqual) {
            isNotEqual = Boolean.TRUE;
        }
        return isNotEqual;
    }

切面類:ParamCheckAspect

@Aspect
@Component
public class ParamCheckAspect {
    private static final Logger logger = LoggerFactory.getLogger(ParamCheckAspect.class);
    @Autowired
    private CheckUtil checkUtil;
    @Around(value = "@annotation(com.smartMap.media.common.paramcheck.annotation.ParamCheck)")
    public Object check(ProceedingJoinPoint point) throws Throwable {
        Object obj;
        // 參數(shù)校驗
        String msg = checkUtil.doCheck(point);
        if (!StringUtils.isEmpty(msg)) {
            throw new RRException(msg, 400);
        }
        // 通過校驗,繼續(xù)執(zhí)行原有方法
        obj = point.proceed();
        return obj;
    }
}

測試驗證

參數(shù)實體類:SelectorObj

@Data
public class SelectorObj {
    private String value;
    private String label;
}

控制器:TestController

@RestController
@RequestMapping("/mobile/test")
public class TestController {
    @ParamCheck({"value:value 不能為空","label!=123:label 不能為123"})
    @RequestMapping("testParamCheck")
    public R testParamCheck(SelectorObj obj) {
        System.out.println(obj);
        return R.ok().put("obj",obj);
    }
}

結(jié)果:

1肉迫、非空檢驗

image.png
image.png

2吹艇、非特定值校驗

image.png
image.png

您的一鍵三連,是我更新的最大動力昂拂,謝謝

山水有相逢受神,來日皆可期,謝謝閱讀格侯,我們再會

我手中的金箍棒鼻听,上能通天,下能探海

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末联四,一起剝皮案震驚了整個濱河市撑碴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌朝墩,老刑警劉巖醉拓,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異收苏,居然都是意外死亡亿卤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門鹿霸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來排吴,“玉大人,你說我怎么就攤上這事懦鼠∽炅ǎ” “怎么了屹堰?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長街氢。 經(jīng)常有香客問我扯键,道長,這世上最難降的妖魔是什么珊肃? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任荣刑,我火速辦了婚禮,結(jié)果婚禮上近范,老公的妹妹穿的比我還像新娘。我一直安慰自己延蟹,他們只是感情好评矩,可當(dāng)我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阱飘,像睡著了一般斥杜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沥匈,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天蔗喂,我揣著相機與錄音,去河邊找鬼高帖。 笑死缰儿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的散址。 我是一名探鬼主播乖阵,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼预麸!你這毒婦竟也來了瞪浸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吏祸,失蹤者是張志新(化名)和其女友劉穎对蒲,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贡翘,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡蹈矮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鸣驱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片含滴。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖丐巫,靈堂內(nèi)的尸體忽然破棺而出谈况,到底是詐尸還是另有隱情勺美,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布碑韵,位于F島的核電站赡茸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏祝闻。R本人自食惡果不足惜占卧,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望联喘。 院中可真熱鬧华蜒,春花似錦、人聲如沸豁遭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蓖谢。三九已至捂蕴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闪幽,已是汗流浹背啥辨。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盯腌,地道東北人溉知。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像腕够,于是被迫代替她去往敵國和親着倾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容