开发过程中我们经常会用到@Validated这个来校验参数信息,同理也能实现一些其他的一些校验信息,所以这里就利用切面的方式来实现类似@Validated的功能,@Validated有自己的实现方式,具体的我也没有深究过。

实现原理

其实原理很简单,就是通过两个注解或者多个注解和springboot的AOP功能,两个注解分别作用为:一个注解作为切点来确认需要判断的方法,另外一个则是标记需要验证的参数信息;当方法进入的切面中,利用反射来处理切面中的参数和对象信息。

代码

所列代码仅供参考,大概看看就好,里面用到的一些方法可能也不是最优的,我没有深入的去看api介绍。

添加AOPmaven配置

1
2
3
4
5
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

EnableVerify 注解

该注解可作用于方法上或类上,用于切面的切点使用,也只有加了此注解,该功能才能生效

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 启用验证的注解
*
* @author Lengff
* @version 1.0
* @date 2021-10-13 14:29
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableVerify {

}

Verify注解

该注解可以在方法的参数上使用,或者是方法参数对象中的字段上使用

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
/**
* 验证注解
*
* @author Lengff
* @version 1.0
* @date 2021-10-13 14:28
*/
@Target({ElementType.PARAMETER, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Verify {

/**
* 验证条件
*
* @return
*/
VerifyEnum value() default VerifyEnum.NOTBANK;

/**
* 错误提示
*
* @return
*/
String message() default "参数有误";
}

VerifyEnum 枚举

此枚举作用是列举需要判断的类别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 验证枚举
*
* @author Lengff
* @version 1.0
* @date 2021-10-13 14:50
*/
public enum VerifyEnum {
/**
* 不为空
*/
NOTBANK,
/**
* 大于0
*/
THENZERO
}

VerifyAspect切面类

基本的业务逻辑都在该切面类中实现

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

/**
* 验证切面
*
* @author Lengff
* @version 1.0
* @date 2021-10-13 14:30
*/
@Aspect
@Component
public class VerifyAspect {
private Logger log = LoggerFactory.getLogger(VerifyAspect.class);

/**
* 定义切点
*/
@Pointcut("@annotation(com.lengff.demo.annotation.EnableVerify)||@within(com.lengff.demo.annotation.EnableVerify)")
public void pointcut() {
}

/**
* 使用前置通知
*
* @param point
* @return
*/
@Before("pointcut()")
public void Before(JoinPoint point) {
MethodSignature method = (MethodSignature) point.getSignature();
// 获取方法上的参数集合
Parameter[] parameters = method.getMethod().getParameters();
if (parameters.length < 1) {
// 无参数方法不处理
return;
}
// 参数
Object[] args = point.getArgs();
for (Parameter parameter : parameters) {
// 字段的排序下标
Integer index = Integer.valueOf(parameter.getName().replace("arg", ""));
Object arg = args[index];
log.info("参数值为:", arg);
if (parameter.isAnnotationPresent(Verify.class)) {
// 这里表明是一个参数
Verify annotation = parameter.getAnnotation(Verify.class);
if (!verify(annotation, arg)) {
return;
}
} else {
// 否则当一个对象来判断,获取对象中的所有字段
Field[] declaredFields = arg.getClass().getDeclaredFields();
if (declaredFields.length < 1) {
continue;
}
for (Field field : declaredFields) {
if (field.isAnnotationPresent(Verify.class)) {
Verify annotation = field.getAnnotation(Verify.class);
Object argValue = getArgValue(arg, field.getName());
if (!verify(annotation, argValue)) {
return;
}
}
}
}
}
}

/**
* 获取参数中的对应字段的值
*
* @param arg
* @param fieldName
* @return
*/
private Object getArgValue(Object arg, String fieldName) {
String names = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
String setMethodname = String.format("set%s", names);
String getMethodname = String.format("get%s", names);
try {
Method method = arg.getClass().getMethod(getMethodname, null);
return method.invoke(arg, null);
} catch (NoSuchMethodException e) {
log.warn("实体对象无get/set方法");
} catch (IllegalAccessException | InvocationTargetException e) {
log.warn("执行实体对象get/set方法出现异常");
}
return null;
}

/**
* 验证
* 这里需自行添加逻辑,我这里只是示例
*
* @param annotation
* @param arg
*/
private boolean verify(Verify annotation, Object arg) {
switch (annotation.value()) {
case NOTBANK:
if (arg instanceof String) {
String arg1 = (String) arg;
if (arg1 == null || "".equals(arg)) {
// 抛出错误
returnMessage("参数" + arg + annotation.message());
return false;
}
}
break;
case THENZERO:
if (arg instanceof Integer || arg instanceof Long) {
// 只处理这两种数据类型
if (Long.valueOf(arg.toString()) <= 0) {
// 强转成long类型处理
// 抛出错误
returnMessage("参数" + arg + annotation.message());
return false;
}
}
break;
}
return true;
}

/**
* 响应数据
*
* @param message
*/
private void returnMessage(String message) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = requestAttributes.getResponse();
try {
response.setHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.print(message);
writer.flush();
writer.close();
} catch (IOException e) {
log.info(e.getMessage());
}
}
}

测试代码

controller类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* VerifyController
*
* @author Lengff
* @version 1.0
* @date 2021-10-13 14:35
*/
@EnableVerify
@RestController
@RequestMapping("verify")
public class VerifyController {

@GetMapping("param")
public String verifyParam(@Verify(message = "参数信息不能为空", value = VerifyEnum.NOTBANK) String param) {
return param;
}

@GetMapping("field")
public String verifyField(VerifyParam param) {
return param.toString();
}
}

参数类

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

/**
* 验证参数
*
* @author Lengff
* @version 1.0
* @date 2021-10-13 14:48
*/
public class VerifyParam implements Serializable {

@Verify(value = VerifyEnum.THENZERO, message = "年龄不能为空")
private Integer age;

@Verify(message = "姓名不能为空")
private String name;

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "VerifyParam{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}

示例代码

示例代码下载