一、概述
做一个,可以传入对象,方法声明的形式校验参数非空,想要实现的效果如下
//user类:Long id,String name;
User user = new User(1L,"");
JsonUtils.validEmpty(user,User::getId,User::getName);
说一下思路
传入对象,方法声明(即对象方法),可以用到有来有回的函数接口 R apply(T t)。
-
函数接口可以获取的信息有:
- 传入的类型
- 传入的调用方法
- 对象对应方法的返回值
- 对象对应方法返回值类型
校验到非空字段后,需要知道这个字段代表什么意思,这里使用一个自定义注解,将此注解到对应字段上,里面的value就是字段含义
-
逻辑:
- 根据传入的对象user,获取到对象对应方法的值(apply函数接口特性)
- 根据获取的值做非空校验,获取到为空的字段
- 获取字段Field类信息(知道 Class对象,调用方法)
- 获取字段的注解value
- 返回为空的字段信息。
二、自定义函数接口
import com.cuihq.testdemo.annotation.CommentTarget;
import org.apache.commons.lang3.StringUtils;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Optional;
//函数式接口注解
@FunctionalInterface
public interface SFunction<T,R> extends Serializable {
/**
* 函数接口方法,如User对象,利用user获取到其 id,name
* 此种方法可以使用 User::getId
* 其余需要传入 user::getId
* @param t
* @return
*/
R apply(T t);
/**
* 获取SerializedLambda,如果传入User::getId 含有内容
* implClass:接口实现类型 User
* implMethodName:调用方法 getId
* implMethodSignature:方法属性返回值类型
* @return SerializedLambda
* @throws Exception
*/
default SerializedLambda getSerializedLambda() throws Exception {
//writeReplace改了好像会报异常
Method write = this.getClass().getDeclaredMethod("writeReplace");
write.setAccessible(true);
return (SerializedLambda) write.invoke(this);
}
/**
* 获取接口实现类
* @return com.cuihq.testdemo.pojo.User
*/
default String getImplClass() {
try {
//这里拿到的数据是com/cuihq/。。。
//需要将 /转换成. 利用Class.forName才会生效
String implClass = getSerializedLambda().getImplClass();
String result = implClass.replace("/", ".");
return result;
} catch (Exception e) {
return null;
}
}
/**
* 获取调用方法
* User::getName -> getName();
* @return 调用方法名称
*/
default String getImplMethodName() {
try {
return getSerializedLambda().getImplMethodName();
} catch (Exception e) {
return null;
}
}
/**
* 获取bean属性,getName->name
* 只针对get方法使用
* @return bean属性
*/
default String getFieldName(){
String methodName = this.getImplMethodName();
String valueTmp = StringUtils
.removeStartIgnoreCase(methodName, "get");
String valuePre = valueTmp.substring(0, 1);
String valueSux = valueTmp.substring(1);
return valuePre.toLowerCase() + valueSux;
}
/**
* 获取CommentTarget注解的value(字段中文含义);
* 思路:SFunction函数中可以获取
* 传入类的类型,调用的方法,即可获取到对应的字段属性
* @return CommentTarget注解的value;
*/
default String getCommentValue(){
String fieldName = this.getFieldName();
String implClass = this.getImplClass();
Field f = null;
try {
f = getField(Class.forName(implClass), fieldName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
CommentTarget commentTarget = null;
if(f!=null){
commentTarget = f.getAnnotation(CommentTarget.class);
}
return commentTarget==null ? "": commentTarget.value();
}
/**
* 获取Class类的字段属性Field
* @param c class类型
* @param FieldName 属性名称
* @return Field类
*/
default Field getField( Class<?> c, String FieldName) {
try {
Field f = c.getDeclaredField(FieldName);
return f;
} catch (NoSuchFieldException e) {
if (c.getSuperclass() == null){
return null;
}
else{
return getField( c.getSuperclass(), FieldName);
}
} catch (Exception ex) {
return null;
}
}
}
二、自定义注解
package com.cuihq.testdemo.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @author cuihq
* 用于解释字段含义
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface CommentTarget {
/**
* 字段值(驼峰命名方式,该值可无)
*/
String value() default "";
}
三、工具类方法
/**
* 不需要继承接口的非空校验
* @param obj 需要被校验的对象
* @param functions 对象需要被校验的方法
* @param <T> 校验对应的泛型
* @throws Exception
*/
static <T> void validEmpty(Object obj, SFunction<T, ?>... functions){
String msg = Stream.of(functions)
.filter(x -> isAnyoneEmpty(x.apply((T) obj)))
.map(fun -> String.format("【%s】不能为空", fun.getCommentValue()))
.collect(Collectors.joining(","));
System.out.println(msg);
}
private static boolean isAnyoneEmpty(Object obj) {
if (obj == null) {
return obj == null;
} else if (obj instanceof Collection<?>) {
return ((Collection<?>) obj).isEmpty();
} else if (obj instanceof String) {
return obj.toString().length() == 0;
} else if (obj.getClass().isArray()) {
return ((Object[]) obj).length == 0;
} else if (obj instanceof Map) {
return ((Map<?, ?>) obj).isEmpty();
} else if (obj instanceof StringBuffer) {
return ((StringBuffer) obj).length() == 0;
} else if (obj instanceof StringBuilder) {
return ((StringBuilder) obj).length() == 0;
}
return false;
}
四、单元测试
/**
* 测试非空校验方法
*/
@Test
public void validEmptyTest() {
User user = new User(1L,"");
JsonUtils.validEmpty(user,User::getId,User::getName);
}
输出结果
【自定义name】不能为空