说明

Java中接口接收的表单类、代码中的实体类等,快速验证属性名是否为空,避免业务逻辑出错,为空则抛出异常
仅需一行代码,开箱即用,简便快捷,支持使用Java双冒号操作符或注解指定类字段,可自定义判空逻辑以满足不同需求,Jdk版本1.8/11/17均可使用

使用示例

ValueRuntimeException是我自己的自定义异常

代码demo演示:https://github.com/destinyol/attribute-check

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* 使用示例
*/
public static void main(String[] args) {
// 实例化链式构造调用
@Data
class Test {
private String name;
private String id;
}
Test test = new Test();
test.setName("测试1");
test.setId("");
try {
AttrCheckUtil.buildWith(test,Test::getId,Test::getName).check(); // 用法1 抛默认异常
}catch (ValueRuntimeException e){
System.out.println("拿到报错了,错误码是:"+e.getValue());
}
try {
AttrCheckUtil.buildWith(test,Test::getId,Test::getName).setException(ValueRuntimeException.class).check(); // 用法2 抛自定义异常
}catch (ValueRuntimeException e){
System.out.println("拿到报错了,错误码是:"+e.getValue());
}

try {
AttrCheckUtil.buildWith(test,Test::getId,Test::getName).setWrongCode(1).check(); // 用法3 抛自定义错误code
}catch (ValueRuntimeException e){
System.out.println("拿到报错了,错误码是:"+e.getValue());
}

try {
AttrCheckUtil.buildWith(test,Test::getId,Test::getName).setException(ValueRuntimeException.class).setWrongCode(1).check(); // 用法4 自定义异常+自定义错误
}catch (ValueRuntimeException e){
System.out.println("拿到报错了,错误码是:"+e.getValue());
}


// 注解调用
@Data
class Test2 {
@AttrNotBlank
private String name;
@AttrNotBlank(wrongCode = 10086)
private String id;
@AttrNotBlank(wrongCode = 1233211)
private String code;
}
try {
Test2 test2 = new Test2();
test2.setName("测试2");
test2.setId("xxxxxx");
AttrCheckUtil.check(test2); // 用法5 用注解
}catch (ValueRuntimeException e){
System.out.println("拿到报错了,错误码是:"+e.getValue());
}
}

源码

主方法类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
public class AttrCheckUtil {

/**
* 可自定义改成需要的自定义异常
*/
private static final int baseWrongCode = 1; // 默认抛出的异常code
private static final Class<? extends RuntimeException> baseWrongClass = ValueRuntimeException.class; // 默认抛出的异常类,可改成自定义异常类,继承RuntimeException,需要有一个int/Object参数放错误码,或者改动throwYourOwnExceptionStep构造函数
private static void throwYourOwnException(int wrongCode){
throwYourOwnException(baseWrongClass,wrongCode);
}
private static void throwYourOwnException(Class<? extends RuntimeException> wrongClass, int wrongCode){
Constructor<? extends RuntimeException> constructor = null;
try {
constructor = wrongClass.getConstructor(Object.class); // 可改动错误类构造函数
RuntimeException runtimeException = constructor.newInstance(wrongCode);
throw runtimeException; // 可改动错误类构造函数
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}

/**
* 判空逻辑函数 可根据需求自定义增改
* @return
*/
private static boolean isBlank(Object value, Field field) {
if (value == null) {
return true;
}
if (value instanceof String) {
return ((String) value).isEmpty();
}
if (value instanceof Collection) {
return ((Collection<?>) value).isEmpty();
}
if (value instanceof Optional) {
return !((Optional<?>) value).isPresent();
}
if (value instanceof URL || value instanceof MultipartFile) {
return false;
}
if (field.getType().isArray()) {
return Array.getLength(value) == 0;
}
if (value instanceof Object[]) {
return ((Object[]) value).length == 0;
}
if (field.getType().isPrimitive() || field.getType().equals(Integer.class) || field.getType().equals(Double.class) || field.getType().equals(Float.class) || field.getType().equals(Long.class) || field.getType().equals(Short.class) || field.getType().equals(Character.class) || field.getType().equals(Byte.class) || field.getType().equals(Boolean.class)) {
return false;
}
return false;
}


/**
* (静态调用主方法)
* 检查该类的属性中被 AttrNotBlank注解指定的字段,是否为空,为空则抛出异常,异常码在注解中定义
* (基础类型int,float等,会跳过不检测,因为这些类型必定有值)
* @param object
* @author pyf
*/
public static void check(Object object) {
if (object == null) {
throwYourOwnException(baseWrongCode);
}
Class<?> clazz = object.getClass();
// 递归检查类及其父类
checkClass(clazz, object);
}

/**
* 实例化构造检测工具
* 检查targetObject类的属性中 指定了getter方法的属性是否为空,为空则抛出错误
*
* @param targetObject 待检测对象
* @param fn 比如: Object::getXxxx()
*
* @author pyf
*/
@SafeVarargs
public static <T> AttrCheckUtil buildWith(T targetObject, FieldGetter<T>... fn) {
List<String> fieldNames = new ArrayList<>();
for (FieldGetter<T> tFieldGetter : fn) {
String fieldName = convertToFieldName(tFieldGetter);
fieldNames.add(fieldName);
}
return new AttrCheckUtil(targetObject, fieldNames);
}

private static void checkClass(Class<?> clazz, Object object) {
// 遍历当前类的字段
for (Field field : clazz.getDeclaredFields()) {
// 检查字段是否有AttrNotBlank注解
if (field.isAnnotationPresent(AttrNotBlank.class)) {
AttrNotBlank annotation = field.getAnnotation(AttrNotBlank.class);
field.setAccessible(true); // 设置字段为可访问
Object value = null; // 获取字段的值
try {
value = field.get(object);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}

if (isBlank(value, field)) {
// System.out.println("参数缺失 -> 参数名:"+field.getName()+" value:"+value);
throwYourOwnException(annotation.wrongCode());
}
}
}
// 如果当前类有父类,递归检查父类
if (clazz.getSuperclass() != null) {
checkClass(clazz.getSuperclass(), object);
}
}

private static final Map<Class, SerializedLambda> CLASS_LAMBDA_CACHE = new ConcurrentHashMap<>();
private final Object targetObject;
private final List<String> fieldNames;
private Class<? extends RuntimeException> exceptionClass = null;
private Integer wrongCode = null;

private AttrCheckUtil(Object targetObject, List<String> fieldNames) {
this.targetObject = targetObject;
this.fieldNames = fieldNames;
}

public AttrCheckUtil setException(Class<? extends RuntimeException> exceptionClassIn){
this.exceptionClass = exceptionClassIn;
return this;
}
public AttrCheckUtil setWrongCode(Integer code){
this.wrongCode = code;
return this;
}

/**
* (实例化调用主方法)
*/
public void check() {
for (String fieldName : this.fieldNames) {
Field field = null;
Class<?> clazz = targetObject.getClass();
while (clazz != null) {
try {
field = clazz.getDeclaredField(fieldName); // 查找属性字段
break;
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass(); // 如果当前类没有找到字段,检查父类
}
}
field.setAccessible(true);
Object value = null;
try {
value = field.get(targetObject);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
if (AttrCheckUtil.isBlank(value, field)) {
// System.out.println("参数缺失 -> 参数名:"+field.getName()+" value:"+value);
if (this.exceptionClass==null){
if (wrongCode == null) throwYourOwnException(baseWrongCode);
else throwYourOwnException(this.wrongCode);
}else{
if (wrongCode == null) throwYourOwnException(this.exceptionClass, baseWrongCode);
else throwYourOwnException(this.exceptionClass, this.wrongCode);
}

}
}
}

/***
* 转换方法引用为属性名
* @param fn
* @return
*/
private static <T> String convertToFieldName(FieldGetter<T> fn) {
SerializedLambda lambda = getSerializedLambda(fn);
// 获取方法名
String methodName = lambda.getImplMethodName();
String prefix = null;
if (methodName.startsWith("get")) {
prefix = "get";
} else if (methodName.startsWith("is")) {
prefix = "is";
}
if (prefix == null) {
System.out.println("无效的getter方法: " + methodName);
}
// 截取get/is之后的字符串并转换首字母为小写
return toLowerCaseFirstOne(methodName.replace(prefix, ""));
}

/**
* 首字母转小写
*
* @param s
* @return
*/
private static String toLowerCaseFirstOne(String s) {
if (Character.isLowerCase(s.charAt(0))) {
return s;
} else {
return Character.toLowerCase(s.charAt(0)) + s.substring(1);
}
}

private static SerializedLambda getSerializedLambda(Serializable fn) {
SerializedLambda lambda = CLASS_LAMBDA_CACHE.get(fn.getClass());
// 先检查缓存中是否已存在
if (lambda == null) {
try {
// 提取SerializedLambda并缓存
Method method = fn.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(Boolean.TRUE);
lambda = (SerializedLambda) method.invoke(fn);
CLASS_LAMBDA_CACHE.put(fn.getClass(), lambda);
} catch (Exception e) {
e.printStackTrace();
}
}
return lambda;
}
}

判空注解

1
2
3
4
5
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AttrNotBlank {
int wrongCode() default 1;
}
1
2
3
4
@FunctionalInterface
public interface FieldGetter<T> extends Serializable {
Object get(T source);
}