通过AOP切面,进行数据校验问题(数据重复校验)
1.自定义注解
package com.jeesite.modules.common.aop;
import com.jeesite.common.entity.BaseEntity;
import com.jeesite.common.entity.DataEntity;
import java.lang.annotation.*;
/**
* 新建、更新时数据重复的校验
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DuplicateCheck {
/**
* 不可重复的类型,默认是 or类型
* @return
*/
EnumDuplicateType duplicateType() default EnumDuplicateType.OR;
/***
* 要验证的重复数据的实体类
* @return
*/
Class<? extends DataEntity> cls();
/**
* or条件的不可重复的字段
*
* @return
*/
String[] orFields();
/**
* and 条件的不可重复的字段
*
* @return
*/
String[] andFields();
/**
* 不可重复的字段中文名称
*
* @return
*/
String fieldsNameWarning();
/**
* 判重查询时需要额外添加的字段(特殊关联字段,如titleId,examStudentId等)
*
* @return
*/
String[] otherQueryAddFields();
enum EnumDuplicateType {
AND,
OR
}
}
2.功能逻辑
package com.jeesite.modules.common.aop;
import com.jeesite.common.constant.CodeConstant;
import com.jeesite.common.entity.DataEntity;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.mybatis.annotation.Column;
import com.jeesite.common.mybatis.annotation.Table;
import com.jeesite.common.mybatis.mapper.SqlMap;
import com.jeesite.common.mybatis.mapper.query.QueryType;
import com.jeesite.common.mybatis.mapper.query.QueryWhere;
import com.jeesite.common.reflect.ReflectUtils;
import com.jeesite.common.utils.BoolReference;
import com.jeesite.common.utils.SpringUtils;
import com.jeesite.modules.common.util.WxUtil;
import com.jeesite.modules.wx.common.request.CommonResult;
import com.jeesite.modules.wx.common.utils.UserUtils;
import com.jeesite.modules.wx.entity.Car;
import com.jeesite.modules.wx.entity.ExamStudent;
import org.apache.poi.ss.formula.functions.T;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
/**
* @Description 新建、更新时数据重复的校验
*/
@Aspect
@Component
public class DuplicateCheckAspect {
private static RuntimeException canUseAspectExcp = new RuntimeException("无法使用该切面");
// @Pointcut("@annotation(DuplicateCheck)")
// public void pointCut() {
//
// }
@Around(value = "@annotation(duplicateCheck)")
public Object around(ProceedingJoinPoint point, DuplicateCheck duplicateCheck) {
ExamStudent es = UserUtils.getExamStudent();
if (es == null) return new CommonResult(CodeConstant.FAILED, "请重新登录");
// Method m = ((MethodSignature) point.getSignature()).getMethod();
//todo 获取 接口请求参数类型
Object[] args = point.getArgs();
if (args.length != 1)
throw canUseAspectExcp;
Object arg = args[0];
//合并
String[] queryAddFields = duplicateCheck.otherQueryAddFields();
HashSet<String> queryFieldSet = new HashSet<>();
for (int i = 0; i < queryAddFields.length; i++) {
queryFieldSet.add(queryAddFields[i]);
}
int duplicateNum = duplicateCheck.orFields().length;
for (int i = 0; i < duplicateNum; i++) {
queryFieldSet.add(duplicateCheck.orFields()[i]);
}
//检验参数是否正确
checkArg(arg, queryFieldSet);
String id = ReflectUtils.getFieldValue(arg, "id");
String examStudentId = es.getId();
boolean isInsert = StringUtils.isBlank(id);
try {
Class cls = duplicateCheck.cls();
DataEntity entity = (DataEntity) cls.newInstance();
Method getSqlMap = cls.getSuperclass().getSuperclass().getMethod("getSqlMap", null);
SqlMap sqlMap = (SqlMap) getSqlMap.invoke(entity);
QueryWhere where = sqlMap.getWhere();
for (int i = 0; i < duplicateNum; i++) {
String field = duplicateCheck.orFields()[i];
String fieldVal = "";
fieldVal = ReflectUtils.getFieldValue(arg, field);
if (i == 0) {
where.andBracket(getJdbcField(cls, field), QueryType.EQ, fieldVal);
} else {
if (duplicateCheck.duplicateType() == DuplicateCheck.EnumDuplicateType.OR) {
where.or(getJdbcField(cls, field), QueryType.EQ, fieldVal);
} else {
where.and(getJdbcField(cls, field), QueryType.EQ, fieldVal);
}
}
}
where.endBracket();
for (String queryAddField : queryAddFields) {
String fieldVal = "";
if (queryAddField.equals("examStudentId")) {
fieldVal = examStudentId;
} else {
fieldVal = ReflectUtils.getFieldValue(arg, queryAddField);
}
where.and(getJdbcField(cls, queryAddField), QueryType.EQ, fieldVal);
}
//更新时添加id not equal
if (!isInsert) {
where.and("id", QueryType.NE, id);
}
//查询
//找到service类
String entityClsSimpleName = cls.getSimpleName();
String serivceName = entityClsSimpleName + "Service";
String serviceFullName = WxUtil.servicePackage + "." + serivceName;
Class<?> serviceCls = Class.forName(serviceFullName);
Object serviceBean = SpringUtils.getBean(StringUtils.uncap(serivceName));
Method findListM = serviceCls.getMethod("findList", cls);
findListM.setAccessible(true);
List<T> exsitList = (List<T>) findListM.invoke(serviceBean, entity);
//重复了
if (exsitList.size() != 0) {
return new CommonResult<>(CodeConstant.FAILED, "存在相同" + duplicateCheck.fieldsNameWarning() + "的数据");
}
return point.proceed(point.getArgs());
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("切面异常");
} catch (Throwable throwable) {
throwable.printStackTrace();
throw new RuntimeException("切面异常");
}
}
/**
* 判断参数是否有id,titleId等字段
*
* @param arg
* @return
*/
private void checkArg(Object arg, HashSet<String> queryFieldSet) {
//是否有id字段
BoolReference existId = BoolReference.getInstance();
ReflectUtils.tryGetFieldValue(arg, "id", existId);
if (existId == null)
throw canUseAspectExcp;
for (String s : queryFieldSet) {
//是否有查询规定的字段,且有值
BoolReference existTitleId = BoolReference.getInstance();
String v = ReflectUtils.tryGetFieldValue(arg, s, existTitleId);
if (!existTitleId.isB() || StringUtils.isBlank(v))
throw canUseAspectExcp;
}
}
private String getJdbcField(Class cls, String javaField) {
Table table = (Table) cls.getAnnotation(Table.class);
Column[] cs = table.columns();
for (int i = 0; i < cs.length; i++) {
Column c = cs[i];
if (javaField.equals(c.attrName())) {
return c.name();
}
}
throw new RuntimeException("没有找到对应的值");
}
public static void main(String[] args) {
DuplicateCheckAspect d = new DuplicateCheckAspect();
Car car = new Car();
//d.checkArg(car);
String titleId = d.getJdbcField(Car.class, "titleId");
System.out.println(titleId);
}
}
反射工具
/**
* Copyright (c) 2005-2012 springside.org.cn
*
* Licensed under the Apache License, Version 2.0 (the "License");
*/
package com.jeesite.common.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Map;
import com.jeesite.common.utils.BoolReference;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.poi.ss.usermodel.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jeesite.common.lang.DateUtils;
import com.jeesite.common.lang.ObjectUtils;
/**
* 反射工具类.
* 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
*/
@SuppressWarnings("rawtypes")
public class ReflectUtils {
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(ReflectUtils.class);
private static Class baseEntityClass = null;
/**
* 调用Getter方法,
* 支持多级,如:对象名.对象名.方法,
* 支持静态类及方法调用,
* 支持Map
*/
@SuppressWarnings("unchecked")
public static <E> E invokeGetter(Object obj, String propertyName) {
Object object = obj;
for (String name : StringUtils.split(propertyName, ".")){
if (obj instanceof Map){
object = ((Map)obj).get(name);
}else{
String methodName = GETTER_PREFIX + StringUtils.capitalize(name);
object = invokeMethod(object, methodName, new Class[] {}, new Object[] {});
}
}
return (E)object;
}
/**
* 调用Setter方法,仅匹配方法名,
* 支持多级,如:对象名.对象名.方法,
* 支持静态类及方法调用,
* 支持Map
*/
@SuppressWarnings("unchecked")
public static <E> void invokeSetter(Object obj, String propertyName, E value) {
Object object = obj;
String[] names = StringUtils.split(propertyName, ".");
for (int i=0; i<names.length; i++){
if(i<names.length-1){
if (obj instanceof Map){
object = ((Map)obj).get(names[i]);
}else{
String methodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
Object childObj = invokeMethod(object, methodName, new Class[] {}, new Object[] {});
// 如果 get 获取对象为空,并且返回值类型继承自 BaseEntity,则 new 对象,并通过 set 赋予它
if (childObj == null && object != null){
Method method = getAccessibleMethod(object, methodName, new Class[] {});
if (method != null) {
Class<?> returnType = method.getReturnType();
try {
if (baseEntityClass == null) {
baseEntityClass = Class.forName("com.jeesite.common.entity.BaseEntity");
}
if (baseEntityClass.isAssignableFrom(returnType)) {
childObj = returnType.getDeclaredConstructor().newInstance();
methodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
invokeMethodByName(object, methodName, new Object[] { childObj });
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
object = childObj;
}
}else{
if (obj instanceof Map){
((Map)obj).put(names[i], value);
}else{
String methodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
invokeMethodByName(object, methodName, new Object[] { value });
}
}
}
}
/**
* 直接读取对象属性值,无视private/protected修饰符,不经过getter函数
*/
@SuppressWarnings("unchecked")
public static <E> E getFieldValue(final Object obj, final String fieldName) {
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
//throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
if (obj != null) {
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
}
return null;
}
E result = null;
try {
field.setAccessible(true);
result = (E)field.get(obj);
} catch (IllegalAccessException e) {
logger.error("不可能抛出的异常: {}", e.getMessage());
}
return result;
}
public static <E> E tryGetFieldValue(final Object obj, final String fieldName, BoolReference exist) {
exist.setB(true);
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
exist.setB(false);
return null;
}
E result = null;
try {
field.setAccessible(true);
result = (E)field.get(obj);
} catch (IllegalAccessException e) {
logger.error("不可能抛出的异常: {}", e.getMessage());
}
return result;
}
/**
* 直接设置对象属性值,无视private/protected修饰符,不经过setter函数
*/
public static <E> void setFieldValue(final Object obj, final String fieldName, final E value) {
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
//throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
return;
}
try {
field.setAccessible(true);
field.set(obj, value);
} catch (IllegalAccessException e) {
logger.error("不可能抛出的异常: {}", e.getMessage());
}
}
/**
* 直接调用对象方法,无视private/protected修饰符,
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用,
* 同时匹配方法名+参数类型,
* 支持静态类及方法调用
*/
@SuppressWarnings("unchecked")
public static <E> E invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,
final Object[] args) {
if (obj == null || methodName == null){
return null;
}
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
if (method == null) {
//throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
if (obj != null) {
logger.debug("在 [" + (obj.getClass() == Class.class ? obj : obj.getClass()) + "] 中,没有找到 [" + methodName + "] 方法 ");
}
return null;
}
try {
return (E)method.invoke(obj.getClass() == Class.class ? null : obj, args);
} catch (Exception e) {
String msg = "method: "+method+", obj: "+obj+", args: "+args+"";
throw convertReflectionExceptionToUnchecked(msg, e);
}
}
/**
* 直接调用对象方法,无视private/protected修饰符,
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用,
* 只匹配函数名,如果有多个同名函数调用第一个,
* 支持静态类及方法调用
*/
@SuppressWarnings("unchecked")
public static <E> E invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
Method method = getAccessibleMethodByName(obj, methodName, args.length);
if (method == null) {
// 如果为空不报错,直接返回空。
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
if (obj != null) {
logger.debug("在 [" + (obj.getClass() == Class.class ? obj : obj.getClass()) + "] 中,没有找到 [" + methodName + "] 方法 ");
}
return null;
}
try {
// 类型转换(将参数数据类型转换为目标方法参数类型)
Class<?>[] cs = method.getParameterTypes();
for (int i=0; i<cs.length; i++){
if (args[i] != null && !args[i].getClass().equals(cs[i])){
if (cs[i] == String.class){
args[i] = ObjectUtils.toString(args[i]);
if(StringUtils.endsWith((String)args[i], ".0")){
args[i] = StringUtils.substringBefore((String)args[i], ".0");
}
}else if (cs[i] == Integer.class){
args[i] = ObjectUtils.toInteger(args[i]);
}else if (cs[i] == Long.class){
args[i] = ObjectUtils.toLong(args[i]);
}else if (cs[i] == Double.class){
args[i] = ObjectUtils.toDouble(args[i]);
}else if (cs[i] == Float.class){
args[i] = ObjectUtils.toFloat(args[i]);
}else if (cs[i] == Date.class){
if (args[i] instanceof String){
args[i] = DateUtils.parseDate(args[i]);
}else{
// POI Excel 日期格式转换
args[i] = DateUtil.getJavaDate((Double)args[i]);
}
}
}
}
return (E)method.invoke(obj.getClass() == Class.class ? null : obj, args);
} catch (Exception e) {
String msg = "method: "+method+", obj: "+obj+", args: "+args+"";
throw convertReflectionExceptionToUnchecked(msg, e);
}
}
/**
* 循环向上转型,获取对象的DeclaredField,并强制设置为可访问,
* 如向上转型到Object仍无法找到,返回null
*/
public static Field getAccessibleField(final Object obj, final String fieldName) {
// 为空不报错。直接返回 null
// Validate.notNull(obj, "object can't be null");
if (obj == null){
return null;
}
Validate.notBlank(fieldName, "fieldName can't be blank");
for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
Field field = superClass.getDeclaredField(fieldName);
makeAccessible(field);
return field;
} catch (NoSuchFieldException e) {//NOSONAR
// Field不在当前类定义,继续向上转型
continue;// new add
}
}
return null;
}
/**
* 循环向上转型,获取对象的DeclaredMethod,并强制设置为可访问,
* 如向上转型到Object仍无法找到,返回null,
* 匹配函数名+参数类型。
* 用于方法需要被多次调用的情况,先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethod(final Object obj, final String methodName,
final Class<?>... parameterTypes) {
// 为空不报错。直接返回 null
// Validate.notNull(obj, "object can't be null");
if (obj == null){
return null;
}
Class<?> clazz = obj.getClass();
if (clazz == Class.class){
clazz = (Class) obj;
}
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = clazz; searchType != Object.class; searchType = searchType.getSuperclass()) {
try {
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
makeAccessible(method);
return method;
} catch (NoSuchMethodException e) {
// Method不在当前类定义,继续向上转型
continue;// new add
}
}
return null;
}
/**
* 循环向上转型,获取对象的DeclaredMethod,并强制设置为可访问,
* 如向上转型到Object仍无法找到,返回null,
* 只匹配函数名。
* 用于方法需要被多次调用的情况,先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
*/
public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) {
// 为空不报错。直接返回 null
// Validate.notNull(obj, "object can't be null");
if (obj == null){
return null;
}
Class<?> clazz = obj.getClass();
if (clazz == Class.class){
clazz = (Class) obj;
}
Validate.notBlank(methodName, "methodName can't be blank");
for (Class<?> searchType = clazz; searchType != Object.class; searchType = searchType.getSuperclass()) {
Method[] methods = searchType.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) {
makeAccessible(method);
return method;
}
}
}
return null;
}
/**
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers())
|| !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
&& !method.isAccessible()) {
method.setAccessible(true);
}
}
/**
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
*/
public static void makeAccessible(Field field) {
if ((!Modifier.isPublic(field.getModifiers())
|| !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
field.setAccessible(true);
}
}
/**
* 通过反射,获得Class定义中声明的泛型参数的类型,注意泛型必须定义在父类处,
* 如无法找到,返回Object.class,
* 如 public UserDao extends CrudDao<User>
* @param clazz The class to introspect
* @return the first generic declaration, or Object.class if cannot be determined
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> getClassGenricType(final Class clazz) {
return getClassGenricType(clazz, 0);
}
/**
* 通过反射,获得Class定义中声明的父类的泛型参数的类型,
* 如无法找到,返回Object.class,
* 如 public UserDao extends CrudDao<User, Long>
* @param clazz clazz The class to introspect
* @param index the Index of the generic ddeclaration,start from 0.
* @return the index generic declaration, or Object.class if cannot be determined
*/
public static Class getClassGenricType(final Class clazz, final int index) {
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType)) {
logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ params.length);
return Object.class;
}
if (!(params[index] instanceof Class)) {
logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
return Object.class;
}
return (Class) params[index];
}
/**
* 获取类的Class,如果为内部类,则返回上级类Class
*/
public static Class<?> getUserClass(Object instance) {
if (instance == null){
throw new RuntimeException("Instance must not be null");
}
Class clazz = instance.getClass();
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && !Object.class.equals(superClass)) {
return superClass;
}
}
return clazz;
}
/**
* 将反射时的checked exception转换为unchecked exception
*/
public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) {
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|| e instanceof NoSuchMethodException) {
return new IllegalArgumentException(msg, e);
} else if (e instanceof InvocationTargetException) {
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
}
return new RuntimeException(msg, e);
}
}