• JAVA 利用切面、注解 动态判断请求信息中字段是否需要为空


    项目中遇到一个需求,保存医生信息时,执业范围在医师编号为23开头时为必填项,其他医师编号时,执业范围为非必填项。当然这样的需求可以使用简单的if判断来解决,但是最近学习了注解的使用,刚才此需求可以用到,基本思路如下:

    1、创建有条件判断字段为空的注解 ConditionalNotEmpty

    2、在医生实体类--》执业范围字段上添加 ConditionalNotEmpty,并给出相应条件

    3、切面类中使用java反射机制拿到注解信息,并根据传入的条件动态判断配置了该注解的字段是否需要校验空值

    以下是大致代码:

    1、注解

        @Target({ElementType.FIELD})

        @Retention(RetentionPolicy.RUNTIME)

    @Documented
    public @interface ConditionalNotEmpty {

    //分组标识
    String groupIdentify() default "";

    //错误提示信息
    String message();

    //业务操作标识
    String[] operation() default {};
    //判断依据的字段名称,如医师编号
    String conditionFiledName() default "";

    //判断条件,如本例中为医师编号起始值为23或者25,那么配置23,25
    String contionString() default "";
    }

    2、医生信息实体中,在执业范围字段配置该注解
    @Data
    @Table(name = "t_ins_staff")
    public class Doctor implements Serializable{
    private static final long serialVersionUID = -7355208328761399457L;
    @Id
    private String id;
    @NotEmpty(message = "专业技术职务代码不能为空",groups = {InsertGroup.class})
    @Length(max = 3,message = "专业技术职务代码不能超过3位",groups = {InsertGroup.class})
    private String zyjszwdm;
    @Length(max = 30,message = "执业证书编码不能超过30位",groups = {InsertGroup.class})
    @ConditionalNotEmpty(groupIdentify = "1",message = "当专业技术职务代码前缀为 23(医师)及前缀为 25(护理)的编码时,执业证书编码必填",operation = {ValidateConstants.INSERT},conditionFiledName = "zyjszwdm",contionString = "23,25")
    private String zyzsbm;

    }

    3、切面类中获取注解并进行判断
    package com.xikang.common.validator.aop;

    import com.xikang.common.exception.business.BusinessException;
    import com.xikang.common.util.UUIDUtils;
    import com.xikang.common.validator.annotation.ConditionalNotEmpty;
    import com.xikang.common.validator.annotation.CustomValidated;
    import com.xikang.common.validator.annotation.GroupNotEmpty;
    import com.xikang.common.validator.annotation.ValidateObj;
    import org.apache.commons.lang3.ArrayUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;

    import javax.validation.ConstraintViolation;
    import javax.validation.ConstraintViolationException;
    import javax.validation.Validator;
    import java.io.IOException;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.util.*;

    /**
    * @author jiaorui
    * @version 2018-09-29
    * @desc 校验结果处理
    */

    @Aspect
    @Component
    public class CustomValidatedAop {

    @Autowired
    protected Validator validator;

    /**
    * @desc 判断是否存在@CustomValidated注解
    */

    @Before("@annotation(org.springframework.web.bind.annotation.PostMapping) || @annotation(org.springframework.web.bind.annotation.DeleteMapping) || @annotation(org.springframework.web.bind.annotation.PutMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping )|| @annotation(org.springframework.web.bind.annotation.RequestMapping )")
    public void doBefore(JoinPoint joinPoint) throws IOException {
    //切入点方法参数
    Object[] params = joinPoint.getArgs();
    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    Method method = methodSignature.getMethod();
    //获取方法参数类型
    Class<?>[] paramsType = method.getParameterTypes();
    Set<String> errorMessages = new HashSet<>();
    //获取方法参数上的注解(因为方法可以有多参数;参数上可以有注解,返回二维数组)
    Annotation[][] an = method.getParameterAnnotations();
    int index = 0;
    //循环参数
    for (Annotation[] an1 : an) {
    Object param = params[index];
    //循环参数上的注解
    for (Annotation an2 : an1) {
    //有自定义校验注解
    if (an2 instanceof CustomValidated) {
    //参数是List
    if (param instanceof List) {
    List list = (List) param;
    for (Object l : list) {
    errorMessages.addAll(this.beanValidator(l, ((CustomValidated) an2).value()));
    }
    } else {//参数非list
    //获取该参数类型
    Class clazz = paramsType[index];
    //反射所有字段
    Field[] fields = clazz.getDeclaredFields();
    for(Field field : fields){
    ValidateObj validateObj = field.getAnnotation(ValidateObj.class);
    //如果字段上有自定义注解@ValidateObj
    if(validateObj != null){
    field.setAccessible(true);
    try {
    Object obj = field.get(param);
    errorMessages.addAll(this.beanValidator(obj, ((CustomValidated) an2).value()));
    } catch (IllegalAccessException e) {
    e.printStackTrace();
    }
    }
    }
    errorMessages.addAll(this.beanValidator(param, ((CustomValidated) an2).value()));
    }

    //执行分组校验 begin
    Set<String> errorMessage = this.groupNotEmpty(param,((CustomValidated) an2).operation());
    if(errorMessage != null && errorMessage.size() > 0){
    errorMessages.addAll(errorMessage);
    }
    //执行分组校验 end

    //执行有条件动态校验 begin
    Set<String> errorMessageCon = this.conditionalNotEmpty(param,((CustomValidated) an2).operation());
    if(errorMessageCon != null && errorMessageCon.size() > 0){
    errorMessages.addAll(errorMessageCon);
    }
    //执行有条件动态校验 end

    if (errorMessages.size() > 0) {
    List rtnStr = new ArrayList<>();
    rtnStr.addAll(errorMessages);
    throw new BusinessException(this.listToString(rtnStr));
    }
    }
    }
    index++;
    }
    }

    /**
    * @desc 通过validator进行分组校验返回错误信息
    */

    protected List<String> beanValidator(Object object, Class<?>... groups) {
    List<String> errorMessages = new ArrayList();
    try {
    Set constraintViolations = validator.validate(object, groups);
    if (!constraintViolations.isEmpty()) {
    return this.extractMessage(constraintViolations);
    }
    } catch (ConstraintViolationException ex) {
    return errorMessages;
    }
    return errorMessages;
    }

    /**
    * 转换Set<ConstraintViolation>为List<message>
    */
    protected List<String> extractMessage(Set<? extends ConstraintViolation> constraintViolations) {
    List<String> errorMessages = new ArrayList();
    for (ConstraintViolation violation : constraintViolations) {
    errorMessages.add(violation.getMessage());
    }
    return errorMessages;
    }

    /**
    * 工具方法 list转string
    */

    protected String listToString(List<String> mList) {
    String convertedListStr = "";
    if (null != mList && mList.size() > 0) {
    String[] mListArray = mList.toArray(new String[mList.size()]);
    for (int i = 0; i < mListArray.length; i++) {
    if (i < mListArray.length - 1) {
    convertedListStr += mListArray[i] + ",";
    } else {
    convertedListStr += mListArray[i];
    }
    }
    return convertedListStr;
    } else {
    return "";
    }
    }

    /***
    * Description: 分组校验属性不能同时为空
    * Date: 2019/3/15 8:56
    * @param
    * @return
    * @author: jiaorui
    */
    protected Set<String> groupNotEmpty(Object param,String[] operation){
    //本类及其父类的属性集合
    List<Field> fieldList = new ArrayList<>() ;
    Class tempClass = param.getClass();
    //递归获取本身及父类的属性
    //当父类不是object并且为null的时候说明到达了最上层的父类(form继承的Entity类).
    while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {
    //反射本类所有属性,包括私有属性
    fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
    //得到父类,然后赋给自己
    tempClass = tempClass.getSuperclass();
    }
    // key--groupId value true 都为空 false 不全为空
    Map<String, Boolean> isNotEmptyMap = new HashMap<>();
    // key--groupId value 所有message
    Map<String, Set> emptyMessageMap = new HashMap<>();
    for(Field field : fieldList){
    GroupNotEmpty groupNotEmpty = field.getAnnotation(GroupNotEmpty.class);
    if(groupNotEmpty != null){
    //分组标识
    String groupId = groupNotEmpty.groupIdentify();
    if(StringUtils.isBlank(groupId)) {
    groupId = UUIDUtils.generateUuid();
    }
    //操作标识
    String[] operations = groupNotEmpty.operation();
    //判断是否匹配controller中的校验操作
    boolean isValidat = this.checkArray(operations,operation);
    //不匹配跳出本次循环
    if(isValidat){
    continue;
    }
    //错误提示
    String message = groupNotEmpty.message();
    field.setAccessible(true);
    try {
    //带GroupNotEmpty注解的字段的值
    Object obj = field.get(param);
    boolean isNull = this.isNull(obj);
    if (isNull) {
    // 1. 为空则
    if (isNotEmptyMap.containsKey(groupId)) {
    emptyMessageMap.get(groupId).add(message);
    } else {
    isNotEmptyMap.put(groupId, true);
    Set set = new HashSet();
    set.add(message);
    emptyMessageMap.put(groupId, set);
    }
    } else {
    // 2. 不为空
    isNotEmptyMap.put(groupId, false);
    emptyMessageMap.put(groupId, new HashSet());
    }
    } catch (IllegalAccessException e) {
    e.printStackTrace();
    }
    }


    }
    Set returnSet = new HashSet();
    for(String groupId: isNotEmptyMap.keySet()) {
    if (isNotEmptyMap.get(groupId)) {
    returnSet.addAll(emptyMessageMap.get(groupId));
    }
    }
    return returnSet;
    }
    /***
    * Description: 校验当某属性属于特定值时,该注解字段校验非空
    * Date: 2020/06/01 8:56
    * @param
    * @return
    * @author: zhangzihui
    */
    protected Set<String> conditionalNotEmpty(Object param,String[] operation){
    //本类及其父类的属性集合
    List<Field> fieldList = new ArrayList<>() ;
    Class tempClass = param.getClass();
    //递归获取本身及父类的属性
    //当父类不是object并且为null的时候说明到达了最上层的父类(form继承的Entity类).
    while (tempClass != null && !tempClass.getName().toLowerCase().equals("java.lang.object")) {
    //反射本类所有属性,包括私有属性
    fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
    //得到父类,然后赋给自己
    tempClass = tempClass.getSuperclass();
    }
    Set returnSet = new HashSet();

    try{

    for(Field field : fieldList){

    ConditionalNotEmpty conditionalNotEmpty = field.getAnnotation(ConditionalNotEmpty.class);

    if(conditionalNotEmpty != null){
    String conditionFiledName=conditionalNotEmpty.conditionFiledName();
    String contionString=conditionalNotEmpty.contionString();
    String[] prefixStrs=null;
    if(StringUtils.isNotEmpty(contionString)){
    prefixStrs=contionString.split(",");
    }
    //根据conditionFiledName 的值动态判断当前field是否需要不为空
    boolean needCheckEmptyFlag=false;

    //获取条件字段的值
    Field conditionField = param.getClass().getDeclaredField(conditionFiledName);
    conditionField.setAccessible(true);
    //该参数为想要获取值得对象
    Object conditionFieldValue = conditionField.get(param);

    for(String prefixStr:prefixStrs){
    if(conditionFieldValue.toString().startsWith(prefixStr)){
    //需要校验当前字段不为空
    needCheckEmptyFlag=true;
    }
    }

    if(needCheckEmptyFlag){
    //操作标识
    String[] operations = conditionalNotEmpty.operation();
    //判断是否匹配controller中的校验操作
    boolean isValidat = this.checkArray(operations,operation);
    //不匹配跳出本次循环
    if(isValidat){
    continue;
    }
    //错误提示
    String message = conditionalNotEmpty.message();
    field.setAccessible(true);

    //带ConditionalNotEmpty注解的字段的值
    Object obj = field.get(param);
    boolean isNull = this.isNull(obj);
    if (isNull) {
    returnSet.add(message);
    }
    }
    }
    }
    }
    catch (IllegalAccessException e) {
    e.printStackTrace();
    }
    catch(Exception e){
    e.printStackTrace();
    }
    return returnSet;
    }
    //判空
    protected boolean isNull(Object obj){
    boolean isNull = false;
    //字段是数组类型
    if(obj instanceof java.lang.String[]){
    String[] array = (String[]) obj;
    if(array == null || array.length == 0){
    isNull = true;
    }
    }
    //字段是集合类型
    else if(obj instanceof java.util.Collection){
    Collection c = (Collection) obj;
    if(c == null || c.size() == 0){
    isNull = true;
    }
    }
    //字段是其他对象类型
    else {
    if(obj == null){
    isNull = true;
    }else {
    isNull = org.springframework.util.StringUtils.isEmpty(obj);
    }
    }
    return isNull;
    }

    //判断一个数组中的元素是否在另一个数组中出现
    private boolean checkArray(String [] array1,String [] array2){
    boolean flag = true;
    if((array1 != null && array1.length > 0) && (array2 != null && array2.length > 0)){
    for(String s1 : array1){
    if(ArrayUtils.contains(array2,s1)){
    flag = false;
    break;
    }
    }
    }
    return flag;
    }
    }
     
     
  • 相关阅读:
    第十周学习进度条
    第九周学习进度条
    Runner站立会议08
    Runner站立会议07
    构建之法阅读笔记02
    Runner站立会议04
    学习进度条
    Runner站立会议01
    进度条
    返回一个一维整数数组中最大子数组的和
  • 原文地址:https://www.cnblogs.com/DylanZ/p/13035941.html
Copyright © 2020-2023  润新知