1 Commits

Author SHA1 Message Date
08e228c5f7 feat: 新增 package-info.java 并初步集成 JSpecify
- 添加 JSpecify 依赖到 pom.xml
- 创建 validator/package-info.java
- 创建 function/package-info.java
- 添加 @Nullable 注解到核心接口和类
2026-05-27 18:29:17 +08:00
27 changed files with 207 additions and 104 deletions

2
.gitignore vendored
View File

@@ -4,8 +4,6 @@ target/
!**/src/main/**/target/
!**/src/test/**/target/
.flattened-pom.xml
### STS ###
.apt_generated
.classpath

View File

@@ -9,7 +9,6 @@
<groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-validator-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>plusone-validator</artifactId>
@@ -19,11 +18,37 @@
Plusone Validator 是一个校验库,使用 lambda 表达式(包括方法引用)和流式 API 构建校验规则,对对象进行校验。
</description>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<name>Zhou Xingyi (周兴毅)</name>
<url>https://gitea.zhouxy.xyz/plusone</url>
</developer>
</developers>
<scm>
<connection>scm:git:https://gitea.zhouxy.xyz/plusone/plusone-validator.git</connection>
<developerConnection>scm:git:ssh://gitea.zhouxy.xyz/plusone/plusone-validator.git</developerConnection>
<url>https://gitea.zhouxy.xyz/plusone/plusone-validator</url>
</scm>
<dependencies>
<dependency>
<groupId>xyz.zhouxy.plusone</groupId>
<artifactId>plusone-commons</artifactId>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>

View File

@@ -31,7 +31,7 @@ import xyz.zhouxy.plusone.commons.util.AssertTools;
*
* @param <T> 待校验对象的类型
* @param <E> 数组元素的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class ArrayPropertyValidator<T, E>
extends BasePropertyValidator<T, E[], ArrayPropertyValidator<T, E>> {

View File

@@ -29,7 +29,7 @@ import com.google.common.collect.Range;
* @param <TProperty> 待校验属性的类型,必须实现 {@code Comparable} 接口
* @param <TPropertyValidator> 具体校验器类型,用于支持链式调用
* @see Range
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public abstract class BaseComparablePropertyValidator<
T,

View File

@@ -24,6 +24,8 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
/**
* 属性校验器。包含针对属性的校验规则。
*
@@ -33,7 +35,7 @@ import java.util.function.Supplier;
* @param <T> 待校验对象的类型
* @param <TProperty> 待校验属性的类型
* @param <TPropertyValidator> 具体校验器类型,用于支持链式调用
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public abstract class BasePropertyValidator<
T,
@@ -135,9 +137,9 @@ public abstract class BasePropertyValidator<
/**
* 校验属性
*
* @param obj 属性所在的对象
* @param obj 属性所在的对象,允许为 {@code null}
*/
public final void validate(T obj) {
public final void validate(@Nullable T obj) {
for (Consumer<? super TProperty> consumer : consumers) {
consumer.accept(getter.apply(obj));
}

View File

@@ -28,13 +28,15 @@ import java.util.function.Supplier;
import xyz.zhouxy.plusone.validator.function.*;
import org.jspecify.annotations.Nullable;
/**
* 校验器基类
* <p>
* 子类可通过添加不同的校验规则,构建完整的校验逻辑,用于校验对象。
*
* @param <T> 待校验对象的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public abstract class BaseValidator<T> implements IValidator<T> {
@@ -311,7 +313,7 @@ public abstract class BaseValidator<T> implements IValidator<T> {
/** {@inheritDoc} */
@Override
public void validate(T obj) {
public void validate(@Nullable T obj) {
this.rules.forEach(rule -> rule.accept(obj));
}
}

View File

@@ -27,7 +27,7 @@ import java.util.function.Supplier;
* 用于构建校验 {@code Boolean} 类型属性的规则链。
*
* @param <T> 待校验对象的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class BoolPropertyValidator<T>
extends BasePropertyValidator<T, Boolean, BoolPropertyValidator<T>> {

View File

@@ -32,7 +32,7 @@ import xyz.zhouxy.plusone.commons.util.AssertTools;
*
* @param <T> 待校验对象的类型
* @param <E> 集合元素的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class CollectionPropertyValidator<T, E>
extends BasePropertyValidator<T, Collection<E>, CollectionPropertyValidator<T, E>> {

View File

@@ -24,7 +24,7 @@ import java.util.function.Function;
* @param <T> 待校验对象的类型
* @param <TProperty> 待校验属性的类型,必须实现 {@code Comparable} 接口
* @see com.google.common.collect.Range
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class ComparablePropertyValidator<T, TProperty extends Comparable<? super TProperty>>
extends BaseComparablePropertyValidator<T, TProperty, ComparablePropertyValidator<T, TProperty>> {

View File

@@ -27,7 +27,7 @@ import java.util.function.Supplier;
* 用于构建校验 {@code Double} 类型属性的规则链。
*
* @param <T> 待校验对象的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class DoublePropertyValidator<T>
extends BaseComparablePropertyValidator<T, Double, DoublePropertyValidator<T>> {

View File

@@ -19,7 +19,7 @@ package xyz.zhouxy.plusone.validator;
/**
* 自带校验方法,校验不通过时直接抛异常。
*
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*
* @see BaseValidator
*/

View File

@@ -15,6 +15,8 @@
*/
package xyz.zhouxy.plusone.validator;
import org.jspecify.annotations.Nullable;
/**
* 校验器
*
@@ -23,14 +25,14 @@ package xyz.zhouxy.plusone.validator;
* </p>
*
* @param <T> 待校验对象的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public interface IValidator<T> {
/**
* 校验指定对象是否符合预定义规则
*
* @param obj 待校验的对象实例
* @param obj 待校验的对象实例,允许为 {@code null}
*/
void validate(T obj);
void validate(@Nullable T obj);
}

View File

@@ -27,7 +27,7 @@ import java.util.function.Supplier;
* 用于构建校验 {@code Integer} 类型属性的规则链。
*
* @param <T> 待校验对象的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class IntPropertyValidator<T>
extends BaseComparablePropertyValidator<T, Integer, IntPropertyValidator<T>> {

View File

@@ -27,7 +27,7 @@ import java.util.function.Supplier;
* 用于构建校验 {@code Long} 类型属性的规则链。
*
* @param <T> 待校验对象的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class LongPropertyValidator<T>
extends BaseComparablePropertyValidator<T, Long, LongPropertyValidator<T>> {

View File

@@ -23,13 +23,15 @@ import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.jspecify.annotations.Nullable;
/**
* 对 Map 进行校验的校验器
*
* <p>
* 校验后拷贝出一个新的 Map 对象,仅保留指定的 key。
*
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public abstract class MapValidator<K, V> extends BaseValidator<Map<K, V>> {
@@ -59,10 +61,10 @@ public abstract class MapValidator<K, V> extends BaseValidator<Map<K, V>> {
/**
* 校验并拷贝,仅保留指定 key 的属性。
*
* @param obj 待校验的 map
* @param obj 待校验的 map,允许为 {@code null}
* @return 拷贝后的 map
*/
public final Map<K, V> validateAndCopy(Map<K, V> obj) {
public final Map<K, V> validateAndCopy(@Nullable Map<K, V> obj) {
return validateAndCopyInternal(obj, this.keys);
}
@@ -89,7 +91,7 @@ public abstract class MapValidator<K, V> extends BaseValidator<Map<K, V>> {
return validateAndCopyInternal(obj, Arrays.asList(keys));
}
private final Map<K, V> validateAndCopyInternal(Map<K, V> obj, Collection<K> keys) {
private final Map<K, V> validateAndCopyInternal(@Nullable Map<K, V> obj, Collection<K> keys) {
validate(obj);
return obj.entrySet().stream()
.filter(kv -> keys.contains(kv.getKey()))

View File

@@ -23,7 +23,7 @@ import java.util.function.Function;
*
* @param <T> 待校验对象的类型
* @param <TProperty> 待校验属性的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class ObjectPropertyValidator<T, TProperty>
extends BasePropertyValidator<T, TProperty, ObjectPropertyValidator<T, TProperty>> {

View File

@@ -27,7 +27,7 @@ import java.util.function.Supplier;
* @param <T> 被验证对象类型
* @param <V1> 第一个元素的类型
* @param <V2> 第二个元素的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class PairPropertyValidator<T, V1, V2>
extends BasePropertyValidator<T, Entry<V1, V2>, PairPropertyValidator<T, V1, V2>> {

View File

@@ -35,7 +35,7 @@ import xyz.zhouxy.plusone.commons.util.StringTools;
* 用于构建校验 {@code String} 类型属性的规则链。
*
* @param <T> 待校验对象的类型
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class StringPropertyValidator<T>
extends BaseComparablePropertyValidator<T, String, StringPropertyValidator<T>> {
@@ -547,7 +547,7 @@ public class StringPropertyValidator<T>
private static Predicate<String> length(int length) {
AssertTools.checkArgument(length >= 0,
"The expected length must be non-negative.");
"The expected length must be non-negative.");
return input -> input == null || input.length() == length;
}

View File

@@ -15,10 +15,12 @@
*/
package xyz.zhouxy.plusone.validator;
import org.jspecify.annotations.Nullable;
/**
* 校验失败异常
*
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
public class ValidationException extends RuntimeException {
@@ -30,11 +32,11 @@ public class ValidationException extends RuntimeException {
super(message);
}
private ValidationException(Throwable cause) {
private ValidationException(@Nullable Throwable cause) {
super(cause);
}
private ValidationException(String message, Throwable cause) {
private ValidationException(String message, @Nullable Throwable cause) {
super(message, cause);
}
@@ -71,10 +73,10 @@ public class ValidationException extends RuntimeException {
/**
* 创建 {@code ValidationException} 实例
*
* @param cause 导致校验失败的根本异常
* @param cause 导致校验失败的根本异常,允许为 {@code null}
* @return {@code ValidationException} 实例
*/
public static ValidationException withCause(Throwable cause) {
public static ValidationException withCause(@Nullable Throwable cause) {
return new ValidationException(cause);
}
@@ -82,10 +84,10 @@ public class ValidationException extends RuntimeException {
* 创建 {@code ValidationException} 实例
*
* @param message 异常信息
* @param cause 导致校验失败的根本异常
* @param cause 导致校验失败的根本异常,允许为 {@code null}
* @return {@code ValidationException} 实例
*/
public static ValidationException withMessageAndCause(String message, Throwable cause) {
public static ValidationException withMessageAndCause(String message, @Nullable Throwable cause) {
return new ValidationException(message, cause);
}
}

View File

@@ -21,7 +21,7 @@ import java.util.function.Function;
/**
* Function&lt;T, Boolean&gt;
*
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
@FunctionalInterface
public interface ToBoolObjectFunction<T> extends Function<T, Boolean>, Serializable {

View File

@@ -21,7 +21,7 @@ import java.util.function.Function;
/**
* Function&lt;T, Double&gt;
*
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
@FunctionalInterface
public interface ToDoubleObjectFunction<T> extends Function<T, Double>, Serializable {

View File

@@ -21,7 +21,7 @@ import java.util.function.Function;
/**
* Function&lt;T, Integer&gt;
*
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
@FunctionalInterface
public interface ToIntegerFunction<T> extends Function<T, Integer>, Serializable {

View File

@@ -21,7 +21,7 @@ import java.util.function.Function;
/**
* Function&lt;T, Long&gt;
*
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
@FunctionalInterface
public interface ToLongObjectFunction<T> extends Function<T, Long>, Serializable {

View File

@@ -21,7 +21,7 @@ import java.util.function.Function;
/**
* Function&lt;T, String&gt;
*
* @author ZhouXY
* @author ZhouXY108 <luquanlion@outlook.com>
*/
@FunctionalInterface
public interface ToStringFunction<T> extends Function<T, String>, Serializable {

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2025-present ZhouXY
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* 函数式接口包,提供带序列化能力的特殊 {@code Function} 子类型。
*
* <p>
* 本包中的接口均继承自 {@link java.util.function.Function},同时实现
* {@link java.io.Serializable},使 lambda 表达式或方法引用可以被序列化。
* 主要用作 {@link xyz.zhouxy.plusone.validator.BaseValidator} 中
* {@code ruleFor} 方法族的参数类型,以支持特定类型的属性取值。
*
* <ul>
* <li>{@link xyz.zhouxy.plusone.validator.function.ToStringFunction ToStringFunction} — Function&lt;T, String&gt;</li>
* <li>{@link xyz.zhouxy.plusone.validator.function.ToIntegerFunction ToIntegerFunction} — Function&lt;T, Integer&gt;</li>
* <li>{@link xyz.zhouxy.plusone.validator.function.ToLongObjectFunction ToLongObjectFunction} — Function&lt;T, Long&gt;</li>
* <li>{@link xyz.zhouxy.plusone.validator.function.ToDoubleObjectFunction ToDoubleObjectFunction} — Function&lt;T, Double&gt;</li>
* <li>{@link xyz.zhouxy.plusone.validator.function.ToBoolObjectFunction ToBoolObjectFunction} — Function&lt;T, Boolean&gt;</li>
* </ul>
*/
@NullMarked
package xyz.zhouxy.plusone.validator.function;
import org.jspecify.annotations.NullMarked;

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2025-present ZhouXY
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Plusone Validator 核心包,提供流式校验 API。
*
* <p>
* 使用 lambda 表达式(包括方法引用)和流式 API 构建校验规则,对对象进行校验。
* 支持校验普通对象POJO和 {@code Map} 对象。
*
* <h3>快速开始</h3>
* <pre>{@code
* public class PersonValidator extends BaseValidator<Person> {
* public PersonValidator() {
* ruleFor(Person::getName)
* .notNull()
* .notBlank();
* ruleForInt(Person::getAge)
* .gt(0);
* }
* }
* }</pre>
*
* <h3>核心概念</h3>
* <ul>
* <li>{@link xyz.zhouxy.plusone.validator.IValidator IValidator} — 校验器接口</li>
* <li>{@link xyz.zhouxy.plusone.validator.BaseValidator BaseValidator} — 校验器基类,通过继承并添加规则构建校验器</li>
* <li>{@link xyz.zhouxy.plusone.validator.BasePropertyValidator BasePropertyValidator} — 属性校验器基类</li>
* <li>{@link xyz.zhouxy.plusone.validator.MapValidator MapValidator} — Map 校验器</li>
* <li>{@link xyz.zhouxy.plusone.validator.ValidationException ValidationException} — 校验失败异常</li>
* </ul>
*
* @see xyz.zhouxy.plusone.validator.BaseValidator
* @see xyz.zhouxy.plusone.validator.MapValidator
*/
@NullMarked
package xyz.zhouxy.plusone.validator;
import org.jspecify.annotations.NullMarked;

109
pom.xml
View File

@@ -47,7 +47,7 @@
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<plusone-commons.version>1.1.0-RC2</plusone-commons.version>
<plusone-commons.version>1.1.0-SNAPSHOT</plusone-commons.version>
</properties>
<dependencyManagement>
@@ -68,68 +68,49 @@
</dependencyManagement>
<build>
<plugins>
<!-- Flatten 插件:生成包含继承信息的完整 POM 用于发布 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<!-- 核心配置oss 模式专为 Sonatype/Maven Central 设计 -->
<!-- 它会保留 url, licenses, developers, scm, properties 等必要元素 -->
<flattenMode>oss</flattenMode>
</configuration>
<executions>
<!-- 在 process-resources 阶段生成展平的 POM -->
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<!-- 在 clean 阶段删除展平的 POM -->
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<pluginManagement>
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>release</id>
<build>
<plugins>
<!-- GPG -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>