diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d7acf8 --- /dev/null +++ b/README.md @@ -0,0 +1,219 @@ +## 一、annotation - 注解 +### 1. StaticFactoryMethod +标识静态工厂方法。 *《Effective Java》* 的 **Item1** 建议考虑用静态工厂方法替换构造器, 因而考虑有一个注解可以标记一下静态工厂方法,以和其它方法进行区分。 + +### 2. ReaderMethod 和 WriterMethod +分别标识读方法(如 getter)或写方法(如 setter)。 + +最早是写了一个集合类,为了方便判断使用读写锁时,哪些情况下使用读锁,哪些情况下使用写锁。 + +### 3. UnsupportedOperation +标识该方法不被支持或没有实现,将抛出 `UnsupportedOperationException`。 为了方便在使用时,不需要点进源码,就能知道该方法没有实现。 + +### 4. Virtual +Java 非 final 的实例方法,对应 C++/C# 中的虚方法,允许被子类覆写。 Virtual 注解旨在设计父类时,强调该方法父类虽然有默认实现,但子类可以根据自己的需要覆写。 + +### 5. ValueObject +标记一个类,表示其作为值对象,区别与 Entity。 + +## 二、base - 基础组件 +### 1. Ref +`Ref` 包装了一个值,表示对该值的应用。 + +灵感来自于 C# 的 ref 参数修饰符。C# 允许通过以下方式,将值返回给调用端: +```C# +void Method(ref int refArgument) +{ + refArgument = refArgument + 44; +} + +int number = 1; +Method(ref number); +Console.WriteLine(number); // Output: 45 +``` +`Ref` 使 Java 可以达到类似的效果,如: +```java +void method(Ref refArgument) { + refArgument.transformValue(i -> i + 44); +} + +Ref number = Ref.of(1); +method(number); +System.out.println(number.getValue()); // Output: 45 +``` +当一个方法需要产生多个结果时,无法有多个返回值,可以使用 `Ref` 作为参数传入,方法内部修改 `Ref` 的值。 调用方在调用方法之后,使用 `getValue()` 获取结果。 +```java +String method(Ref intRefArgument, Ref strRefArgument) { + intRefArgument.transformValue(i -> i + 44); + strRefArgument.setValue("Hello " + strRefArgument.getValue()); + return "Return string"; +} + +Ref number = Ref.of(1); +Ref str = Ref.of("Java"); +String result = method(number, str); +System.out.println(number.getValue()); // Output: 45 +System.out.println(str.getValue()); // Output: Hello Java +System.out.println(result); // Output: Return string +``` +### 2. IWithCode +类似于枚举这样的类型,通常需要设置固定的码值表示对应的含义。 可实现 `IWithCode`、`IWithIntCode`、`IWithLongCode`,便于在需要的地方对这些接口的实现进行处理。 + +## 三、collection - 集合 +### 1. CollectionTools +集合工具类 + +## 四、constant - 常量 +### 1. 正则常量 +`RegexConsts` 包含常见正则表达式;`PatternConsts` 包含对应的 `Pattern` 对象 + +## 五、exception - 异常 +### 1. MultiTypesException - 多类型异常 +异常在不同场景下被抛出,可以用不同的枚举值,表示不同的场景类型。 + +异常实现 `MultiTypesException` 的 `MultiTypesException#getType` 方法,返回对应的场景类型。 + +表示场景类型的枚举实现 `MultiTypesException.ExceptionType`,其中的工厂方法用于创建类型对象。 +```java +public final class LoginException + extends RuntimeException + implements MultiTypesException { + private final Type type; + private LoginException(@Nonnull Type type, @Nonnull String message) { + super(message); + this.type = type; + } + + private LoginException(@Nonnull Type type, @Nonnull Throwable cause) { + super(cause); + this.type = type; + } + + private LoginException(@Nonnull Type type, + @Nonnull String message, + @Nonnull Throwable cause) { + super(message, cause); + this.type = type; + } + + @Override + public @Nonnull Type getType() { + return this.type; + } + + // ... + + public enum Type implements ExceptionType { + DEFAULT("00", "当前会话未登录"), + NOT_TOKEN("10", "未提供token"), + INVALID_TOKEN("20", "token无效"), + TOKEN_TIMEOUT("30", "token已过期"), + BE_REPLACED("40", "token已被顶下线"), + KICK_OUT("50", "token已被踢下线"), + ; + + @Nonnull + private final String code; + @Nonnull + private final String defaultMessage; + + Type(@Nonnull String code, @Nonnull String defaultMessage) { + this.code = code; + this.defaultMessage = defaultMessage; + } + + @Override + public @Nonnull String getCode() { + return code; + } + + @Override + public @Nonnull String getDefaultMessage() { + return defaultMessage; + } + + @Override + public @Nonnull LoginException create() { + return new LoginException(this, this.defaultMessage); + } + + @Override + public @Nonnull LoginException create(String message) { + return new LoginException(this, message); + } + + @Override + public @Nonnull LoginException create(Throwable cause) { + return new LoginException(this, cause); + } + + @Override + public @Nonnull LoginException create(String message, Throwable cause) { + return new LoginException(this, message, cause); + } + } +} +``` +使用时,可以使用这种方式创建并抛出异常: +```java +throw LoginException.Type.TOKEN_TIMEOUT.create(); +``` + +### 2. 业务异常 +预设常见的业务异常。可继承 `BizException` 自定义业务异常。 + +### 3. 系统异常 +预设常见的系统异常。可继承 `SysException` 自定义系统异常。 + +## 六、function - 函数式编程 +### 1. PredicateTools +`PredicateTools` 用于 `Predicate` 的相关操作。 + +### 2. Functional interfaces +补充可能用得上的函数式接口: + +| Group | FunctionalInterface | method | + | ------------- | -------------------- | -------------------------------- | + | UnaryOperator | BoolUnaryOperator | boolean applyAsBool (boolean) | + | UnaryOperator | CharUnaryOperator | char applyAsChar(char) | + | Throwing | Executable | void execute() throws E | + | Throwing | ThrowingConsumer | void accept(T) throws E | + | Throwing | ThrowingFunction | R apply(T) throws E | + | Throwing | ThrowingPredicate | boolean test(T) throws E | + | Throwing | ThrowingSupplier | T get() throws E | + | Optional | OptionalSupplier | Optional get() throws E | + | Optional | ToOptionalBiFunction | Optional apply(T,U) | + | Optional | ToOptionalFunction | Optional apply(T) | + +## 七、model - 业务建模组件 +包含业务建模可能用到的性别、身份证等元素,也包含数据传输对象,如分页查询参数、响应结果、分页结果等。 + +### 数据传输对象 +#### 1. 分页 +分页组件由 `PagingAndSortingQueryParams` 作为入参, 因为分页必须伴随着排序,不然可能出现同一个对象重复出现在不同页,有的对象不被查询到的情况, 所以分页查询的入参必须包含排序条件。 + +用户可继承 `PagingAndSortingQueryParams` 构建自己的分页查询入参,需在构造器中调用 `PagingAndSortingQueryParams` 的构造器,传入一个 Map 作为白名单, key 是供前端指定用于排序的**属性名**,value 是对应数据库中的**字段名**,只有在白名单中指定的属性名才允许作为排序条件。 + +`PagingAndSortingQueryParams` 包含三个主要的属性: +- **size** - 每页显示的记录数 +- **pageNum** - 当前页码 +- **orderBy** - 排序条件 +其中 `orderBy` 是一个 List,可以指定多个排序条件,每个排序条件是一个字符串, 格式为“**属性名-ASC**”或“**属性名-DESC**”,分别表示升序和降序。 + +比如前端传入的 orderBy 为 ["name-ASC","age-DESC"],意味着要按 name 进行升序,name 相同的情况下则按 age 进行降序。 + +使用时调用 `PagingAndSortingQueryParams#buildPagingParams()` 方法获取分页参数 `PagingParams`。 + +分页结果可以存放到 `PageResult` 中,作为出参。 + +#### 2. UnifiedResponse +UnifiedResponse 对返回给前端的数据进行封装,包含 `code`、`message`、`data。` + +可使用 `UnifiedResponses` 快速构建 `UnifiedResponse` 对象。 `UnifiedResponses` 默认的成功代码为 "2000000", 用户按测试类 `CustomUnifiedResponseFactoryTests` 中所示范的,继承 `UnifiedResponses` 实现自己的工厂类, 自定义 `SUCCESS_CODE` 和 `DEFAULT_SUCCESS_MSG` 和工厂方法。 见 [issue#22](http://zhouxy.xyz:3000/plusone/plusone-commons/issues/22)。 + +## 八、time - 时间 API +### 1. 季度 +模仿 JDK 的 `java.time.Month` 和 `java.time.YearMonth`, 实现 `Quarter`、`YearQuarter`,对季度进行建模。 + +## 九、util - 工具类 +包含树构建器(`TreeBuilder`)、断言工具(`AssertTools`)、ID 生成器(`IdGenerator`)及其它实用工具类。