自定义注解

该代码段展示了如何使用自定义注解和AOP切面在Java中进行数据重复校验。注解定义了校验规则,切面处理校验逻辑,检查新建或更新的数据是否存在重复项,防止数据冲突。
摘要由CSDN通过智能技术生成

通过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);
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值