使用攔截器處理繁瑣的前置條件判定
背景
在開發過程中,爲了提高程序的健壯性,對參數的校驗是必不可少的,然而使用傳統的方式進行參數校驗時,導致程序中存在了if xxx return xxx;處理不夠優雅。雖然jfinal提供了Validator,但是使用過於繁瑣,對前後端分離不友好。 在guaua工具包中的Preconditions啓發下,本人利用攔截器和自定義異常實現了一個較爲優雅的參數校驗方法。(注:未經全面測試,僅供參考)
正文(直接上代碼)
- ParaExceptionInterceptor 自定義異常攔截器(僅攔截返回值爲Ret的方法)
public class ParaExceptionInterceptor implements Interceptor {
@SuppressWarnings("unchecked")
private static final List<Class<? extends Exception>> DEFAULT_EXCPTIONS = Lists.newArrayList(ParaExcetion.class);
private static List<Class<? extends Exception>> getConfigWithExceptionConfig(Invocation inv) {
ExceptionConfig config = inv.getMethod().getAnnotation(ExceptionConfig.class);
if (config == null)
config = inv.getTarget().getClass().getAnnotation(ExceptionConfig.class);
if (config != null) {
Class<? extends Exception>[] value = config.value();
return Arrays.asList(value);
}
return DEFAULT_EXCPTIONS;
}
/**
* (non-Javadoc)
* Title: intercept
* Description:對指定異常進行攔截並封裝爲錯誤信息返回
* @param inv
* @see com.jfinal.aop.Interceptor#intercept(com.jfinal.aop.Invocation)
*/
@Override
public void intercept(Invocation inv) {
try {
inv.invoke();
} catch (Exception e) {
// 若返回值類型不是Ret則將異常繼續往上拋
Class<?> returnType = inv.getMethod().getReturnType();
if (!(returnType.equals(Ret.class))) {
throw e;
}
List<Class<? extends Exception>> exceptionClasses = getConfigWithExceptionConfig(inv);
for (Class<? extends Exception> exceptionClass : exceptionClasses) {
if (Objects.equals(e.getClass(), exceptionClass) || Objects.equals(e.getClass().getSuperclass(), exceptionClass)) {
inv.setReturnValue(MRetKit.buildFail(e.getMessage()));
return;
}
}
throw e;
}
}
}
- ParaExcetion 自定義異常
public class ParaExcetion extends com.iipcloud.api.exception.ParaExcetion {
/** serialVersionUID */
private static final long serialVersionUID = 4888200095167386189L;
/**
* <p>Title: </p>
* <p>Description: </p>
*/
public ParaExcetion() {
super();
}
/**
* <p>Title: </p>
* <p>Description: </p>
* @param message
* @param cause
* @param enableSuppression
* @param writableStackTrace
*/
public ParaExcetion(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
/**
* <p>Title: </p>
* <p>Description: </p>
* @param message
* @param cause
*/
public ParaExcetion(String message, Throwable cause) {
super(message, cause);
}
/**
* <p>Title: </p>
* <p>Description: </p>
* @param message
*/
public ParaExcetion(String message) {
super(message);
}
/**
* <p>Title: </p>
* <p>Description: </p>
* @param cause
*/
public ParaExcetion(Throwable cause) {
super(cause);
}
}
- ExceptionConfig 異常註解
@Retention(RUNTIME)
@Target({ TYPE, METHOD })
public @interface ExceptionConfig {
Class<? extends Exception>[] value();
}
- ParaCheckKit 配套工具類
public class ParaCheckKit {
private ParaCheckKit() {
super();
}
public static void requireFalse(boolean expression, String errMsgFormat, Object... args) {
requireTrue(!expression, String.format(errMsgFormat, args));
}
/**
* Title: checkPara
* Description: 檢查表達式是否滿足,若不滿足則拋出異常
* Date: 2020年2月25日
* @param expression
* @param errMsg
* @throws IllegalParaException
*/
public static void requireTrue(boolean expression, String errMsg) {
if (!expression) {
throw new IllegalParaException(errMsg);
}
}
public static void requireTrue(boolean expression, String errMsgFormat, Object... args) {
if (!expression) {
throw new IllegalParaException(String.format(errMsgFormat, args));
}
}
/**
* Title: requireNotNull
* Description: 判斷指定對象是否爲空,爲空則拋出異常
* Date: 2020年2月25日
* @param obj
* @param errMsg
* @throws IllegalParaException
*/
public static void requireNotNull(Object obj, String errMsg, Object... args) {
requireTrue(Objects.nonNull(obj), errMsg, args);
}
public static void requireNull(Object obj, String errMsg, Object... args) {
requireTrue(Objects.isNull(obj), errMsg, args);
}
/**
* Title: requireNotBlank
* Description:判斷指定字符串是否爲空,爲空則拋出異常
* Date: 2020年2月25日
* @param str
* @param errMsg
* @throws com.iipmes.exception.IllegalParaException
*/
public static void requireNotBlank(String str, String errMsg, Object... args) {
requireTrue(StrKit.notBlank(str), errMsg, args);
}
public static void requireBlank(String str, String errMsg, Object... args) {
requireTrue(StrKit.isBlank(str), errMsg, args);
}
/**
* Title: requireNotEmpty
* Description:
* Date: 2020年2月25日
* @param obj
* @param errMsg
* @throws com.iipmes.exception.IllegalParaException
*/
public static void requireNotEmpty(Object obj, String errMsg, Object... args) {
requireTrue(IIPUtil.notEmpty(obj), errMsg, args);
}
public static void requireEmpty(Object obj, String errMsg, Object... args) {
requireTrue(IIPUtil.isEmpty(obj), errMsg, args);
}
/**
* Title: checkModel
* Description: 檢查model中的指定字段是否爲空,爲空則拋出異常
* Date: 2020年2月25日
* @param model
* @param fields
*/
public static void checkModel(Model<? extends Model<?>> model, List<String> fields, String errMsg, Object... args) {
requireTrue(fieldNotEmpty(model, fields), errMsg, args);
}
public static boolean fieldNotEmpty(Model<? extends Model<?>> model, String... fields) {
return fieldNotEmpty(model, Arrays.asList(fields));
}
/**
* Title: fieldNotEmpty
* Description: 檢查Model中指定的字段
* Date: 2020年2月25日
* @param model
* @param fields
* @return
*/
public static boolean fieldNotEmpty(Model<? extends Model<?>> model, List<String> fields) {
for (String field : fields) {
if (ObjectKit.isEmpty(model.get(field))) {
return false;
}
}
return true;
}
}
- 使用demo
@Before(ParaExceptionInterceptor.class)
public class DemoService {
public Ret doSomeThing(Record record, Model<? extends Model<?>> model) {
ParaCheckKit.requireNotNull(record.getStr("id"), "id can not be null");
ParaCheckKit.checkModel(model, Lists.newArrayList("id,name"), "id required, name required");
// do Something
return Ret.ok();
}
}
注意事項
-
配合靜態方法導入更爲舒爽
-
以上提供的只是一個簡單的demo,適用於servce層,Controller層使用需要改動(默認無返回值,可自定義一個攔截器處理返回值返回給前端頁面,下面是一個簡單示例)。
public class ActionInterceptor implements Interceptor {
/**
* (non-Javadoc)
* <p>Title: intercept</p>
* <p>Description: </p>
* @param inv
* @see com.jfinal.aop.Interceptor#intercept(com.jfinal.aop.Invocation)
*/
@Override
public void intercept(Invocation inv) {
inv.invoke();
Class<?> returnType = inv.getMethod().getReturnType();
if (returnType.equals(Void.class)) {
return;
}
Object returnValue = inv.getReturnValue();
if (returnType.equals(String.class)) {
inv.getController().render((String) returnValue);
} else if (returnType.equals(Ret.class)) {
inv.getController().renderJson((Ret) returnValue);
} else {
inv.getController().renderJson(RetKit.buildOk(returnValue));
}
}
}