自定义注解 @ValidNotEmpty对javaBean的非空校验判断及全局处理

时间:2024-04-11 11:54:36

1.项目中我们经常遇到前端对输入框的非空校验   当然我们后端也要对此进行校验  ,以下是我自定义的demo,首先建两个工具类ValidateUtils,ValidNotEmpty    ValidateUtils的代码如下:

package com.tone.utils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 入参校验 现只校验参数非空  其余待完善
 */
public class ValidateUtils {


    private static final Logger log = LoggerFactory.getLogger(ValidateUtils.class);

    /**
     * 用于校验ValidNotEmpty 注解上的属性值是否为空
     *
     * @param object
     * @throws Exception
     */
    public static void nullFieldValidate(Object object) throws Exception {
        if (null == object) {
            throw new Exception("T can't be null");
        }
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();
            Object fieldValue = runGetter(field, object);
            boolean isAnnotationNotNull = field.isAnnotationPresent(ValidNotEmpty.class);
            //获取对应注解
            ValidNotEmpty validNotEmpty = field.getAnnotation(ValidNotEmpty.class);
            //获取注解上的自定义值
            String annotationFileName = "";
            if (null != validNotEmpty) {
                annotationFileName = validNotEmpty.fileName();
            }
            //属性上有validNotEmpty注解,并且值为空
            if (isAnnotationNotNull && (null == fieldValue || StringUtils.isBlank(fieldValue.toString()))) {
                if (StringUtils.isNotBlank(annotationFileName)) {
                    throw new Exception(annotationFileName + "不能为空");
                } else {
                    throw new Exception(fieldName + "不能为空");
                }
            }
        }
    }

    // 由于所有子类的属性都是private的,所以必须要找到属性的getter方法
    public static Object runGetter(Field field, Object instance) {
        for (Method method : instance.getClass().getDeclaredMethods()) {
            //防止自定义非属性 getXXX方法影响
            if ((method.getName().startsWith("get")) && (method.getName().length() == (field.getName().length() + 3))) {
                if (method.getName().toLowerCase().endsWith(field.getName().toLowerCase())) {
                    try {
                        // 找到对应的get方法
                        return method.invoke(instance);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        //执行错误
                        log.info("Could not determine method: " + method.getName());
                        System.out.println("Could not determine method: " + method.getName());
                    }
                }
            }
        }
        return null;
    }


}
ValidNotEmpty的代码如下:

package com.tone.utils;


import javax.validation.Constraint;
import java.lang.annotation.*;

/**
 * 自定义非空 注解
 */
@Documented
@Constraint(
        validatedBy = {}
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME) //存活阶段
public @interface ValidNotEmpty {

    /**
     * 字段名称
     */
    String fileName() default "";

}

2.自定义常量工具类,代码如下:

package com.tone.utils;


/**
 * 常量工具类
 */
public class Contents {

    //redis0-15 库分配用户管理使用0
    public static int userIndexdb=0;

    //redis0-15 库分配初始化数据存入使用1
    public static int initResIndexdb=1;

    //redis0-15 库分配初始化数据存入使用1
    public static int initCaseIndexdb=2;

    //redis0-15 库分配初始化数据存入使用1
    public static int initSysRunIndexdb=3;

    //流水号存放位置
    public static int caseSerialIndexdb = 15;

    //登陆缓存有效期 单位秒
    public static int redisLoginTimeOut = 3600;

    //APP登陆缓存有效期
    public static int appRedisLoginTimeOut = 180000;

    //流水号锁定时间
    public static int caseSerialTimeOut = 3600;

    //webToken md5加密
    public static String keyStr = "longjin2019shanghai";


    //redis角色资源权限key串
    public static String roleResAuth = "resourcesAuth";

    //业务请求成功
    public static String requestSuccess = "200";

    //业务请求失败
    public static String requestFail = "201";

    //用户未登陆或登陆超时
    public static String noneLogin = "102";

    //两次Token不一致
    public static String authTokenFail = "103";

    //Webtoken验证不正确
    public static String authWebtokenFail = "105";

    //删除失败
    public static String deleteFail = "107";

    //新增失败
    public static String addFail = "108";

    //更新失败
    public static String updateFail = "109";

    //没有访问权限
    public static String requestAuthFail = "110";


    public static String getMsg4Code(String code) {
        String str = "未知";
        switch (code) {
            case "100":
                str = "登录成功";
                break;
            case "101":
                str = "用户名或密码不正确";
                break;
            case "102":
                str = "用户未登陆或登陆超时";
                break;
            case "103":
                str = "两次Token不一致";
                break;
            case "104":
                str = "退出登录成功";
                break;
            case "105":
                str = "Webtoken验证不正确";
                break;
            case "106":
                str = "验证码输入不正确";
                break;
            case "107":
                str = "删除失败";
                break;
            case "108":
                str = "新增失败";
                break;
            case "109":
                str = "更新失败";
                break;
            case "110":
                str = "没有访问权限";
                break;
            case "111":
                str = "前后端token不一致或已过期";
                break;
            case "200":
                str = "请求处理成功";
                break;
            case "201":
                str = "请求处理失败";
                break;
            default:
                str = "其他";
                break;
        }

        return str;
    }

}

3.自定义全局异常处理,代码如下:
 

package com.tone.handle;

import javax.servlet.http.HttpServletRequest;

import com.tone.entity.Results;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 全局异常处理
 */
@ControllerAdvice
public class GlobalDefaultExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalDefaultExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Object defaultExceptionHandler(HttpServletRequest req, Exception e) {
        log.error("发生未处理的异常={}",e.getMessage(),e);
        Results r = new Results();
        r.setStatusCode("202");
        r.setStatusMsg(e.getMessage());
        return r;
    }

    /**
     * 获取异常堆栈信息
     *
     * @param throwable
     * @return
     */
    public static String getStrackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        try {
            throwable.printStackTrace(pw);
            return sw.toString();
        } finally {
            pw.close();
        }
    }


}


4.创建一个返回状态码的实体类   代码如下:

package com.tone.entity;

import com.tone.utils.Contents;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashMap;
import java.util.Map;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Results {

    /**
     * 返回码
     */
    private String statusCode;
    /**
     * 返回消息
     */
    private String statusMsg;
    /**
     * 请求的时间戳
     */
    private String timestamp;
    /**
     * 返回请求内容
     */
    private Map<String, Object> resMap;


    public static Results success(String statusCode, String statusMsg, String timestamp, Map<String, Object> resMap) {
        Results results = new Results(statusCode, statusMsg, timestamp, resMap);
        return results;
    }

    public static Results success(String timestamp, Object data) {
        Map<String, Object> resMap = new HashMap<>();
        resMap.put("data", data);
        return success(Contents.requestSuccess, Contents.getMsg4Code(Contents.requestSuccess), timestamp, resMap);
    }


    public static Results success(String timestamp) {
        return success(Contents.requestSuccess, Contents.getMsg4Code(Contents.requestSuccess), timestamp, null);
    }

    public static Results fail(String statusCode, String statusMsg, String timestamp, Map<String, Object> resMap) {
        Results results = new Results(statusCode, statusMsg, timestamp, resMap);
        return results;
    }

    public static Results requestFail(String timestamp) {
        return fail(Contents.requestFail, Contents.getMsg4Code(Contents.requestFail), timestamp, null);
    }

    /**
     * msg自定义 返回
     *
     * @param timestamp
     * @param msg
     * @return
     */
    public static Results requestFailMsg(String timestamp, String msg) {
        return fail(Contents.requestFail, msg, timestamp, null);
    }

    public static Results addFail(String timestamp) {
        return fail(Contents.addFail, Contents.getMsg4Code(Contents.addFail), timestamp, null);
    }


    public static Results updateFail(String timestamp) {
        return fail(Contents.updateFail, Contents.getMsg4Code(Contents.updateFail), timestamp, null);
    }

    public static Results deleteFail(String timestamp) {
        return fail(Contents.deleteFail, Contents.getMsg4Code(Contents.deleteFail), timestamp, null);
    }

    public static Results noLoginFail(String timestamp) {
        return success(Contents.noneLogin, Contents.getMsg4Code(Contents.noneLogin), timestamp, null);
    }

    /**
     * 根据条数以及type决定返回code,msg
     *
     * @param timestamp
     * @param count
     * @param type
     * @return
     */
    public static Results returnByCount(String timestamp, int count, String type) {
        if (count >= 1) {
            return success(timestamp);
        } else if ("新增".equals(type) || "保存".equals(type)) {
            return addFail(timestamp);
        } else if ("修改".equals(type) || "更新".equals(type)) {
            return updateFail(timestamp);
        } else if ("删除".equals(type)) {
            return deleteFail(timestamp);
        } else {
            return requestFail(timestamp);
        }
    }
}

5.创建一个表t_customer  实体类如下:

package com.tone.entity;

import com.tone.utils.ValidNotEmpty;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * 客户信息(TCustomer)实体类
 *
 * @author makejava
 * @since 2020-05-05 21:54:25
 */
@Data
public class Customer implements Serializable {
    private static final long serialVersionUID = 209205595824413154L;
    /**
    * 主键id
    */
    private Integer id;
    /**
    * 姓名
    */
    @ValidNotEmpty(fileName = "姓名")
    private String name;
    /**
    * 年龄
    */
    @ValidNotEmpty(fileName = "年龄")
    private Integer age;
    /**
    * 性别
    */
    @ValidNotEmpty(fileName = "性别")
    private Integer sex;
    /**
    * 教育
    */
    @ValidNotEmpty(fileName = "教育")
    private String education;
    /**
    * 创建时间
    */
    private Date createTime;



}

6.dao层:

package com.tone.dao;

import com.tone.entity.Customer;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

/**
 * 客户信息(TCustomer)表数据库访问层
 *
 * @author makejava
 * @since 2020-05-05 21:54:26
 */
@Mapper
public interface CustomerMapper {

    /**
     * 新增数据
     *
     * @param customer 实例对象
     * @return 影响行数
     */
    @Insert("insert into t_customer(id,name,age,sex,education,create_time) values (null,#{name},#{age},#{sex},#{education},now())")
    int save(Customer customer);

}

7.Customer的Service层:

package com.tone.service;

import com.tone.entity.Customer;

import java.util.List;

/**
 * 客户信息(TCustomer)表服务接口
 *
 * @author makejava
 * @since 2020-05-05 21:54:27
 */
public interface CustomerService {
    /**
     * 新增数据
     *
     * @param tCustomer 实例对象
     * @return 实例对象
     */
    int save(Customer tCustomer);

}

8.Customer的Service实现层:

package com.tone.service.impl;

import com.tone.dao.CustomerMapper;
import com.tone.entity.Customer;
import com.tone.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * 客户信息(TCustomer)表服务实现类
 *
 * @author makejava
 * @since 2020-05-05 21:54:27
 */
@Service
public class CustomerServiceImpl implements CustomerService {
    @Resource
    private CustomerMapper customerMapper;


    /**
     * 新增数据
     *
     * @param customer 实例对象
     * @return 实例对象
     */
    @Override
    public int save(Customer customer) {
        return  this.customerMapper.save(customer);
    }

}

9.Customer的Controller层:

package com.tone.controller;

import com.tone.entity.Customer;
import com.tone.entity.Results;
import com.tone.service.CustomerService;
import com.tone.utils.ValidateUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * 客户信息(TCustomer)表控制层
 *
 * @author makejava
 * @since 2020-05-05 21:54:28
 */
@RestController
@RequestMapping("/customer")
public class CustomerController {
    /**
     * 服务对象
     */
    @Resource
    private CustomerService customerService;

    @GetMapping("save")
    public Results save(@RequestBody Customer customer) throws Exception{
        ValidateUtils.nullFieldValidate(customer);
        int save = this.customerService.save(customer);
        return Results.returnByCount(null,save,"新增");
    }

}

10.通过postman调用接口如下:

自定义注解 @ValidNotEmpty对javaBean的非空校验判断及全局处理