Skip to content

Commit 3a1b422

Browse files
authored
feat: 升级到 0.3.0-beta (#42)
2 parents a982eba + 13eba51 commit 3a1b422

32 files changed

+1471
-58
lines changed

.github/workflows/mvn-test.yml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: run mvn test
2+
3+
on: [ push, pull_request, workflow_dispatch ]
4+
5+
jobs:
6+
run-mvn-test:
7+
runs-on: ubuntu-latest
8+
9+
steps:
10+
- uses: actions/checkout@v4
11+
- uses: actions/setup-java@v4
12+
with:
13+
distribution: 'zulu'
14+
java-version: '8'
15+
cache: maven
16+
17+
- run: |
18+
java -version
19+
mvn -v
20+
21+
- run: mvn verify
22+
- run: mvn clean test jacoco:report coveralls:report -DrepoToken="${{secrets.COVERALLS_TOKEN}}"

.run/jacoco_report.run.xml

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<component name="ProjectRunConfigurationManager">
2+
<configuration default="false" name="jacoco:report" type="MavenRunConfiguration" factoryName="Maven">
3+
<MavenSettings>
4+
<option name="myGeneralSettings"/>
5+
<option name="myRunnerSettings"/>
6+
<option name="myRunnerParameters">
7+
<MavenRunnerParameters>
8+
<option name="cmdOptions"/>
9+
<option name="profiles">
10+
<set/>
11+
</option>
12+
<option name="goals">
13+
<list>
14+
<option value="clean"/>
15+
<option value="test"/>
16+
<option value="jacoco:report"/>
17+
</list>
18+
</option>
19+
<option name="multimoduleDir"/>
20+
<option name="pomFileName" value="pom.xml"/>
21+
<option name="profilesMap">
22+
<map>
23+
<entry key="release" value="false"/>
24+
</map>
25+
</option>
26+
<option name="projectsCmdOptionValues">
27+
<list/>
28+
</option>
29+
<option name="resolveToWorkspace" value="false"/>
30+
<option name="workingDirPath" value="$PROJECT_DIR$"/>
31+
</MavenRunnerParameters>
32+
</option>
33+
</MavenSettings>
34+
<extension name="net.ashald.envfile">
35+
<option name="IS_ENABLED" value="false"/>
36+
<option name="IS_SUBST" value="false"/>
37+
<option name="IS_PATH_MACRO_SUPPORTED" value="false"/>
38+
<option name="IS_IGNORE_MISSING_FILES" value="false"/>
39+
<option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false"/>
40+
<ENTRIES>
41+
<ENTRY IS_ENABLED="true" PARSER="runconfig"/>
42+
</ENTRIES>
43+
</extension>
44+
<method v="2"/>
45+
</configuration>
46+
</component>

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# SpEL Validator
22

3+
[![Coverage Status](https://coveralls.io/repos/github/stick-i/spel-validator/badge.svg?branch=main)](https://coveralls.io/github/stick-i/spel-validator?branch=main)
34
[![Maven Central](https://img.shields.io/maven-central/v/cn.sticki/spel-validator.svg)](https://central.sonatype.com/search?q=g:cn.sticki%20a:spel-validator)
45
[![license](https://img.shields.io/github/license/stick-i/spel-validator)](https://github.com/stick-i/spel-validator/blob/main/LICENSE)
56

67
一个强大的 Java 参数校验包,基于 SpEL 实现,扩展自 javax.validation 包,用于简化参数校验,几乎支持所有场景下的参数校验。
78

8-
99
## 项目地址
1010

1111
- GitHub:https://github.com/stick-i/spel-validator

pom.xml

+13-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
<groupId>cn.sticki</groupId>
99
<artifactId>spel-validator</artifactId>
10-
<version>0.2.0-beta</version>
10+
<version>0.3.0-beta</version>
1111
<packaging>jar</packaging>
1212
<name>Spel Validator</name>
1313
<url>https://github.com/stick-i/spel-validator</url>
@@ -139,6 +139,12 @@
139139
<encoding>${project.build.sourceEncoding}</encoding>
140140
</configuration>
141141
</plugin>
142+
<!-- 若要在GitHub Actions上执行mvn test,则必须添加此插件才能成功执行 -->
143+
<plugin>
144+
<groupId>org.apache.maven.plugins</groupId>
145+
<artifactId>maven-surefire-plugin</artifactId>
146+
<version>3.0.0-M5</version>
147+
</plugin>
142148
<!-- Source -->
143149
<plugin>
144150
<groupId>org.apache.maven.plugins</groupId>
@@ -169,15 +175,14 @@
169175
<goal>prepare-agent</goal>
170176
</goals>
171177
</execution>
172-
<execution>
173-
<id>report</id>
174-
<phase>test</phase>
175-
<goals>
176-
<goal>report</goal>
177-
</goals>
178-
</execution>
179178
</executions>
180179
</plugin>
180+
<!-- 测试报告上传Coveralls -->
181+
<plugin>
182+
<groupId>com.github.hazendaz.maven</groupId>
183+
<artifactId>coveralls-maven-plugin</artifactId>
184+
<version>4.5.0-M2</version>
185+
</plugin>
181186
</plugins>
182187
</build>
183188

src/main/java/cn/sticki/validator/spel/SpelValidExecutor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ private static <A extends Annotation> FieldValidResult doValidate(
218218
// noinspection unchecked
219219
result = ((SpelConstraintValidator<A>) validator).isValid(annotation, verifiedObject, verifiedField);
220220
} catch (SpelValidatorException e) {
221-
log.error("Spel validate error: {} ;Located in the annotation [{}] of class [{}] field [{}]",
221+
log.error("Spel validate error: {}; Located in the annotation [{}] of class [{}] field [{}]",
222222
e.getMessage(), annotation.annotationType().getName(), verifiedObject.getClass().getName(), verifiedField.getName());
223223
throw e;
224224
} catch (IllegalAccessException e) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package cn.sticki.validator.spel.constrain;
2+
3+
import cn.sticki.validator.spel.SpelConstraint;
4+
import cn.sticki.validator.spel.SpelValid;
5+
import cn.sticki.validator.spel.constraintvalidator.SpelMaxValidator;
6+
import org.intellij.lang.annotations.Language;
7+
8+
import java.lang.annotation.Documented;
9+
import java.lang.annotation.Repeatable;
10+
import java.lang.annotation.Retention;
11+
import java.lang.annotation.Target;
12+
13+
import static java.lang.annotation.ElementType.FIELD;
14+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
15+
16+
/**
17+
* 被标记的元素值必须小于或等于指定的最小值。{@code null} 元素被认为是有效的。
18+
* <p>
19+
* 支持所有 {@link Number} 类型及它们的基本数据类型。
20+
*
21+
* @author 阿杆
22+
* @version 1.0
23+
* @since 2024/9/29
24+
*/
25+
@Documented
26+
@Retention(RUNTIME)
27+
@Target(FIELD)
28+
@Repeatable(SpelMax.List.class)
29+
@SpelConstraint(validatedBy = SpelMaxValidator.class)
30+
public @interface SpelMax {
31+
32+
/**
33+
* 校验失败时的错误消息。
34+
*/
35+
String message() default "必须小于或等于 {value}";
36+
37+
/**
38+
* 约束开启条件,必须为合法的SpEL表达式,计算结果必须为boolean类型。
39+
* <p>
40+
* 当 表达式为空 或 计算结果为true 时,会对带注解的元素进行校验。
41+
* <p>
42+
* 默认情况下,开启校验。
43+
*/
44+
@Language("SpEL")
45+
String condition() default "";
46+
47+
/**
48+
* 分组条件,必须为合法的SpEL表达式。
49+
* <p>
50+
* 当分组信息不为空时,只有当 {@link SpelValid#spelGroups()} 中的分组信息与此处的分组信息有交集时,才会对带注解的元素进行校验。
51+
* <p>
52+
* 其计算结果可以是任何类型,但只有两个计算结果完全相等时,才被认为是相等的。
53+
*/
54+
@Language("SpEL")
55+
String[] group() default {};
56+
57+
/**
58+
* 指定元素最大值。必须为合法的SpEL表达式,
59+
* <p>
60+
* 表达式的计算结果必须为 {@link Number} 类型。
61+
*/
62+
@Language("SpEL")
63+
String value() default "0";
64+
65+
@Documented
66+
@Target(FIELD)
67+
@Retention(RUNTIME)
68+
@interface List {
69+
70+
SpelMax[] value();
71+
72+
}
73+
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package cn.sticki.validator.spel.constrain;
2+
3+
import cn.sticki.validator.spel.SpelConstraint;
4+
import cn.sticki.validator.spel.SpelValid;
5+
import cn.sticki.validator.spel.constraintvalidator.SpelMinValidator;
6+
import org.intellij.lang.annotations.Language;
7+
8+
import java.lang.annotation.Documented;
9+
import java.lang.annotation.Repeatable;
10+
import java.lang.annotation.Retention;
11+
import java.lang.annotation.Target;
12+
13+
import static java.lang.annotation.ElementType.FIELD;
14+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
15+
16+
/**
17+
* 被标记的元素值必须大于或等于指定的最小值。{@code null} 元素被认为是有效的。
18+
* <p>
19+
* 支持所有 {@link Number} 类型及它们的基本数据类型。
20+
*
21+
* @author oddfar、阿杆
22+
* @version 1.0
23+
* @since 2024/8/25
24+
*/
25+
@Documented
26+
@Retention(RUNTIME)
27+
@Target(FIELD)
28+
@Repeatable(SpelMin.List.class)
29+
@SpelConstraint(validatedBy = SpelMinValidator.class)
30+
public @interface SpelMin {
31+
32+
/**
33+
* 校验失败时的错误消息。
34+
*/
35+
String message() default "必须大于或等于 {value}";
36+
37+
/**
38+
* 约束开启条件,必须为合法的SpEL表达式,计算结果必须为boolean类型。
39+
* <p>
40+
* 当 表达式为空 或 计算结果为true 时,会对带注解的元素进行校验。
41+
* <p>
42+
* 默认情况下,开启校验。
43+
*/
44+
@Language("SpEL")
45+
String condition() default "";
46+
47+
/**
48+
* 分组条件,必须为合法的SpEL表达式。
49+
* <p>
50+
* 当分组信息不为空时,只有当 {@link SpelValid#spelGroups()} 中的分组信息与此处的分组信息有交集时,才会对带注解的元素进行校验。
51+
* <p>
52+
* 其计算结果可以是任何类型,但只有两个计算结果完全相等时,才被认为是相等的。
53+
*/
54+
@Language("SpEL")
55+
String[] group() default {};
56+
57+
/**
58+
* 指定元素最小值。必须为合法的SpEL表达式,
59+
* <p>
60+
* 表达式的计算结果必须为 {@link Number} 类型。
61+
*/
62+
@Language("SpEL")
63+
String value() default "0";
64+
65+
@Documented
66+
@Target(FIELD)
67+
@Retention(RUNTIME)
68+
@interface List {
69+
70+
SpelMin[] value();
71+
72+
}
73+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package cn.sticki.validator.spel.constraintvalidator;
2+
3+
import cn.sticki.validator.spel.SpelConstraintValidator;
4+
import cn.sticki.validator.spel.exception.SpelParserException;
5+
import cn.sticki.validator.spel.parse.SpelParser;
6+
import cn.sticki.validator.spel.result.FieldValidResult;
7+
8+
import java.lang.annotation.Annotation;
9+
import java.util.Collections;
10+
import java.util.HashSet;
11+
import java.util.Set;
12+
13+
/**
14+
* 约束注解 Max、Min 的抽象校验器。
15+
*
16+
* @author oddfar、阿杆
17+
* @version 1.0
18+
* @since 2024/8/25
19+
*/
20+
public abstract class AbstractSpelNumberCompareValidator<T extends Annotation> implements SpelConstraintValidator<T> {
21+
22+
public FieldValidResult isValid(Number fieldValue, String spel, String errorMessage, Object obj) {
23+
// 元素为null是被允许的
24+
if (fieldValue == null) {
25+
return FieldValidResult.success();
26+
}
27+
// 计算表达式的值,基本数据类型会自动装箱
28+
Object minValue = SpelParser.parse(spel, obj);
29+
if (!(minValue instanceof Number)) {
30+
throw new SpelParserException("Expression [" + spel + "] calculate result must be Number.");
31+
}
32+
// 比较大小,其中一个是Not-a-Number (NaN)默认失败
33+
if (!this.compare(fieldValue, (Number) minValue)) {
34+
// todo 目前对Double的边界值处理不太友好,message的展示类似为:不能小于等于 NaN。后续考虑去掉对Double Float类型的支持,或者对边界值抛出异常。
35+
// 构建错误信息
36+
String replacedMessage = errorMessage.replace("{value}", String.valueOf(minValue));
37+
return new FieldValidResult(false, replacedMessage);
38+
}
39+
40+
return FieldValidResult.success();
41+
}
42+
43+
/**
44+
* 比较两个数值的大小,返回比较结果。
45+
*
46+
* @param fieldValue 当前元素的值
47+
* @param compareValue 比较的值,最大值或最小值
48+
* @return 成功时返回true,失败时返回false
49+
*/
50+
protected abstract boolean compare(Number fieldValue, Number compareValue);
51+
52+
static final Set<Class<?>> SUPPORT_TYPE;
53+
54+
static {
55+
HashSet<Class<?>> hashSet = new HashSet<>();
56+
hashSet.add(Number.class);
57+
hashSet.add(int.class);
58+
hashSet.add(long.class);
59+
hashSet.add(float.class);
60+
hashSet.add(double.class);
61+
hashSet.add(short.class);
62+
hashSet.add(byte.class);
63+
SUPPORT_TYPE = Collections.unmodifiableSet(hashSet);
64+
}
65+
66+
@Override
67+
public Set<Class<?>> supportType() {
68+
return SUPPORT_TYPE;
69+
}
70+
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package cn.sticki.validator.spel.constraintvalidator;
2+
3+
import cn.sticki.validator.spel.constrain.SpelMax;
4+
import cn.sticki.validator.spel.result.FieldValidResult;
5+
import cn.sticki.validator.spel.util.NumberComparatorUtil;
6+
7+
import java.lang.reflect.Field;
8+
9+
/**
10+
* {@link SpelMax} 注解校验器。
11+
*
12+
* @author 阿杆
13+
* @version 1.0
14+
* @since 2024/9/29
15+
*/
16+
public class SpelMaxValidator extends AbstractSpelNumberCompareValidator<SpelMax> {
17+
18+
@Override
19+
protected boolean compare(Number fieldValue, Number compareValue) {
20+
return NumberComparatorUtil.compare(fieldValue, compareValue, NumberComparatorUtil.GREATER_THAN) <= 0;
21+
}
22+
23+
@Override
24+
public FieldValidResult isValid(SpelMax annotation, Object obj, Field field) throws IllegalAccessException {
25+
return super.isValid((Number) field.get(obj), annotation.value(), annotation.message(), obj);
26+
}
27+
28+
}

0 commit comments

Comments
 (0)