一、背景
我們用json物件作為接收引數的包裝器,最後要轉化為dto進行業務操作,操作之前要做非空校驗,我們可以實現2個註解來實現這個通用的操作。@NotNullField @CheckNull
二、思路
1.實現@NotNullField註解,註解標記在dto欄位名上面
@Target(ElementType.FIELD) // 目標為欄位
@Retention(RetentionPolicy.RUNTIME) // 執行時可用
public @interface NotNullField {
String fieldName() default ""; // 可自定義欄位名
String message() default "欄位不能為空"; // 預設錯誤訊息
}
2.實現@CheckNull註解,註解標記在呼叫方法上面,這個是要接受dto.class的
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckNull {
//要校驗的dto
Class<?> value();
}
三、程式碼實現
1.@NotNullField,這個我實現了一個靜態方法
/**
* 校驗標註了非空欄位的dto
* @param obj
* @throws NullParamsException
*/
public static void validateNotNull(Object obj) throws NullParamsException {
Class<?> clazz = obj.getClass();
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(NotNullField.class)) {
field.setAccessible(true); // 允許訪問私有欄位
try {
Object value = field.get(obj);
if (value == null) {
NotNullField annotation = field.getAnnotation(NotNullField.class);
throw new NullParamsException(
String.format("驗證錯誤: %s - %s", annotation.fieldName(), annotation.message())
);
}else{
//如果是集合型別遞迴呼叫
//感覺遞迴呼叫 是一個危險的方式 大資料量會導致棧記憶體溢位 先進行實驗
//程式碼中如果有集合的方式必須 業務自己提取出來data自己去遍歷校驗
if(Collections.class.isAssignableFrom(field.getType())){
field.setAccessible(true); // 允許訪問私有欄位
Collection<?> collection = (Collection<?>) value;
Iterator<?> iterator = collection.iterator();
while (iterator.hasNext()) {
Object item = iterator.next();
// 處理集合中的每個物件
validateNotNull(item);
}
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException("訪問錯誤", e);
}
}
}
// 移動到父類 要支援繼承
clazz = clazz.getSuperclass();
}
}
2.@CheckNull,這裡的@Order 註解是標記註解起作用的順序,因為我還寫了其他校驗註解
@Component
@Aspect
@Order(2)
public class CheckNullAspect {
@Autowired
private MyAutoConfig myAutoConfig;
/**
* 環繞處理
* 連線點
* 註解的全限定名稱
* @return 結果
* @throws Throwable 異常
*/
@Pointcut(value = "@annotation(具體包)")
public void checkAround() throws Throwable {
}
@Before("checkAround()")
public void around(JoinPoint proceedingJoinPoint) throws Throwable {
// 從切面織入點處透過反射機制獲取織入點處的方法
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
//獲取切入點所在的方法
Method method = signature.getMethod();
//獲取操作
CheckNull annotation = method.getAnnotation(CheckNull.class);
Class<?> dtoClass = annotation.value();
// 獲取入參
Object[] objects = proceedingJoinPoint.getArgs();
if (Objects.nonNull(objects) && objects.length > 0) {
JSONObject jsonObject = (JSONObject) objects[0];
Object dto = jsonObject.toJavaObject(dtoClass);
CheckUtil.validateNotNull(dto);
}
}
}
四、後記
自定義註解實現在spring型別的專案裡面超好用!