组件编码
hzero-starter-core
一、简介
1.1 概述
该组件主要针对技术开发中进行技术支持,提供一些通用的技术开发支持功能,减少重复造轮子。定义了基础实现类,异常封装,常用工具等。
1.2 组件坐标
<dependency>
<groupId>org.hzero.starter</groupId>
<artifactId>hzero-starter-core</artifactId>
<version>${hzero.starter.version}</version>
</dependency>
二、组件功能
2.1 base
base 包主要定义了一些基础常量、BaseController 等。
-
BaseController
:提供了校验单个对象和集合元素的方法
validObject
、validList
,只需在 Controller 接口方法里使用这些方法即可对@NotBlank
、@Size
、@NotNull
等常用hibernate-validator
包的校验注解进行校验。
提供了getMessage
、getExceptionResponse
、locale
等与返回消息相关的便捷方法使用。 -
BaseConstants
:定义了常用的字段常量:
Long DEFAULT_TENANT_ID = 0L; String PAGE = "0"; String SIZE = "10"; String FIELD_BODY = "body"; String FIELD_CONTENT = "content"; String FIELD_MSG = "message"; String FIELD_FAILED = "failed";
在
Pattern
接口定义了各种格式的日期时间格式常量:interface Pattern { // // 常规模式 // ---------------------------------------------------------------------------------------------------- String DATE = "yyyy-MM-dd"; String DATETIME = "yyyy-MM-dd HH:mm:ss"; String DATETIME_MM = "yyyy-MM-dd HH:mm"; String DATETIME_SSS = "yyyy-MM-dd HH:mm:ss.SSS"; String TIME = "HH:mm"; String TIME_SS = "HH:mm:ss"; // // 系统时间格式 // ---------------------------------------------------------------------------------------------------- String SYS_DATE = "yyyy/MM/dd"; String SYS_DATETIME = "yyyy/MM/dd HH:mm:ss"; String SYS_DATETIME_MM = "yyyy/MM/dd HH:mm"; String SYS_DATETIME_SSS = "yyyy/MM/dd HH:mm:ss.SSS"; // // 无连接符模式 // ---------------------------------------------------------------------------------------------------- String NONE_DATE = "yyyyMMdd"; String NONE_DATETIME = "yyyyMMddHHmmss"; String NONE_DATETIME_MM = "yyyyMMddHHmm"; String NONE_DATETIME_SSS = "yyyyMMddHHmmssSSS"; }
在
Symbol
接口定义了常用的特殊符号:interface Symbol { String SIGH = "!"; String AT = "@"; String WELL = "#"; String DOLLAR = "$"; String RMB = "¥"; String PERCENTAGE = "%"; String AND = "&"; String STAR = "*"; String MIDDLE_LINE = "-"; String LOWER_LINE = "_"; String EQUAL = "="; String PLUS = "+"; String COLON = ":"; String SEMICOLON = ";"; String COMMA = ","; String POINT = "."; String SLASH = "/"; String DOUBLE_SLASH = "//"; String BACKSLASH = "\\"; String QUESTION = "?"; }
-
AopProxy
:提供self()方法便于获取自身接口代理对象,常用在一个事务方法里调用当前类的其它事务方法,如果不使用代理对象调用方法,本质使用的是原始对象,因而可能导致事务或AOP拦截不生效。
2.2 exception
exception 包提供了基础的异常类,以及全局异常处理器:
BaseExceptionHandler
:异常处理器,拦截各类异常,获取多语言消息,返回 ResponseEntity。CheckedException
:受检异常类,继承自Exception
,程序中需要抛出一定需要捕捉的异常时,可使用该异常类。IllegalOperationException
:非法操作异常,对于某些不能进入的方法、操作,可直接抛出该异常。MessageException
:在程序中,某些消息在抛出时已经处理过,不需要异常处理器再去获取多语言消息,可使用该异常,异常处理器会直接将消息返回。NotLoginException
:未登录异常。OptimisticLockException
:乐观锁检查异常。
/**
* 通用异常类的使用
*
* 通用异常类主要有 {@link CommonException}、受检异常 {@link CheckedException}、非法操作异常 {@link IllegalOperationException} <br>
*
* 抛出异常时,通常需在 messages_zh_CN.properties/messages_en_US.properties 定义多语言消息,抛出异常时指定对应的编码 <br>
*
* 定义消息编码时,一般分三段,格式为 服务简码.类型(info/warn/error).消息编码,如:demo.warn.emailNotBlank <br>
*/
public void commonException() throws CheckedException {
String email = "";
// CommonException:RuntimeException 的子类,一般用于封装程序中的业务异常
if (StringUtils.isBlank(email)) {
throw new CommonException("demo.warn.emailNotBlank");
}
try {
// do ...
} catch (Exception e) {
throw new CommonException("demo.error.createUserError", e);
}
// CheckedException: Exception 的子类,一般用于封装高级别的错误,需上级调用者处理
try {
// do...
} catch (RuntimeException e) {
throw new CheckedException("demo.error.checkedException");
}
}
2.3 util
util 包提供了一些常用的扩展的工具类:
AssertUtils
:Assert 扩展CheckStrength
:检测密码强度工具FieldNameUtils
: 驼峰-下划线互转EncoderUtils
: 对文件名称进行编码,处理一些特殊字符。EncryptionUtils
:加解密工具类,提供了MD5
、AES
、RSA
、RSA2
等加密方式Reflections
: 反射工具类Regexs
: 正则表达式工具类,提供了常用的正则表达式常量及校验方法Results
:返回 ResponseEntity 对象UUIDUtils
:生成UUIDValidUtils
: 数据校验工具EncryptionUtils
:加密解密工具类,如:MD5 非对称加密、AES 对称加密、RSA 对称加密、RSA2 对称加密Sequence
:分布式高效有序ID生产工具StringPool
:String 相关常量SensitiveUtils
:敏感信息工具类ResponseUtils
:响应处理工具类PinyinUtils
:拼音处理工具类- ….
PinyinUtils
public void demo() {
String text1 = "菜单管理"; // 有多音字
String text2 = "用户管理"; // 无多音字
String pinyin1 = PinyinUtils.getPinyin(text1);
String capital1 = PinyinUtils.getPinyinCapital(text1);
// 获取中文的拼音,结果:|caidanguanli|caishanguanli|caichanguanli|
System.out.println("pinyin1 >> " + pinyin1);
// 获取拼音首字母,结果:|ccgl|csgl|cdgl|
System.out.println("capital1 >> " + capital1);
String pinyin2 = PinyinUtils.getPinyin(text2);
String capital2 = PinyinUtils.getPinyinCapital(text2);
// 结果:|yonghuguanli|
System.out.println("pinyin2 >> " + pinyin2);
// 结果:|yhgl|
System.out.println("capital2 >> " + capital2);
}
Regexs
public void demo() {
// 正则表达式
String integer = Regexs.INTEGER; // 整数
String email = Regexs.EMAIL; // 邮件
String url = Regexs.URL; // URL
String chinese = Regexs.CHINESE; // 中文
// .......
// 正则匹配方法
// 判断是否是手机号
boolean isPhone = Regexs.is("185666444", Regexs.MOBILE);
// 判断是否是中文
boolean isChinese = Regexs.is("是否中文", Regexs.CHINESE);
// 判断是否是数字
boolean isNumber = Regexs.isNumber("12345");
// 判断是否是邮箱
boolean isEmail = Regexs.isEmail("hzero@hand-china.com");
// 判断是否是IP地址
boolean isIp = Regexs.isIP("192.168.12.101");
// 判断是否是URL地址
boolean isUrl = Regexs.isUrl("www.baidu.com");
// 判断是否是移动手机号
boolean isMobile = Regexs.isMobile("18523226666", "+86");
}
ResponseUtils
- 响应处理类,通常用于处理 Feign 调用返回结果,由于 Feign 调用结果可能错误,返回对象并不一定是指定的泛型,所以通用的处理方式是指定返回类型为 String,然后使用 ResponseUtils 来处理返回结果。
public class ResponseUtilsDemo {
private UserFeignClient userFeignClient;
public void demo() {
UserDTO dto = new UserDTO();
dto.setEmail("demo@hand-china.com");
// Feign 调用创建用户
ResponseEntity<String> result = userFeignClient.createUser(dto);
// 处理方式一:判断结果处理
if (ResponseUtils.isFailed(result)) {
// 获取异常信息
ExceptionResponse response = ResponseUtils.getResponse(result, ExceptionResponse.class);
throw new CommonException(response.getMessage());
}
// 成功,获取返回用户信息
UserDTO user1 = ResponseUtils.getResponse(result, UserDTO.class);
// 处理方式二,基于 lambada 表达式处理
// 第三个参数:非2xx错误处理;第四个参数:200,但是failed=true处理
UserDTO user2 = ResponseUtils.getResponse(result, UserDTO.class, (httpStatus, response) -> {
throw new CommonException(response);
}, (exceptionResponse) -> {
throw new CommonException(exceptionResponse.getMessage());
});
}
@FeignClient(value = HZeroService.Iam.NAME)
public interface UserFeignClient {
@PostMapping("/v1/users")
ResponseEntity<String> createUser(UserDTO user);
}
public class UserDTO {
private Long id;
private String email;
// getter/setter
}
}
2.4 captcha
captcha 下提供了基础的验证码功能封装。
-
如果需要开启验证码功能,首先需要配置
hzero.captcha.enable=true
。 -
图片验证码和短信验证码详细配置可参考:
-
CaptchaImageHelper
:提供图片验证码功能,调用generateAndWriteCaptchaImage
生成图片验证码,会将验证码缓存到redis中,并将captchaKey
和captcha
写到 cookie 中。调用checkCaptcha
方法检查验证码正确与否,调用getCaptcha
方法从缓存中获取验证码。 -
CaptchaMessageHelper
:用于短信验证码或者邮箱验证码,只用于生成、校验验证码,并不会直接发送验证码,发送验证码可使用hzero-boot-message
客户端。该helper主要封装了生成验证码、校验验证码时的各种验证,并返回相应的多语言消息,验证码及各种key、消息都封装到CaptchaResult
返回,需要自行处理结果。
2.5 message
message 包提供国际化消息相关的操作。
-
MessageAccessor
:获取 classpath 下资源多语言的工具,封装 Spring 的MessageSourceAccessor
,提供多种重载的便捷方法,方法返回的Message
对象非空,可放心使用。 -
getMessage:先从缓存获取多语言消息,没有则从本地消息文件获取多语言消息。参数说明如下:
- code:消息编码
- args:消息参数,当消息中包含参数时,可传入参数,如:demo.warn.usernameLengthError=Username’s length between {0}-{1}
- defaultMessage:默认消息,当通过编码找不到消息时,则使用默认消息
- locale:消息语言,默认为当前登录用户的语言
-
getMessageLocal:从本地消息文件获取多语言消息
/**
* 添加资源文件位置
*
* @param names 如 <code>classpath:messages/messages_core</code>
*/
public static void addBasenames(String... names)
public static Message getMessage(String code, String defaultMessage)
public static Message getMessage(String code, String defaultMessage, Locale locale)
public static Message getMessage(String code, Object[] args, String defaultMessage)
public static Message getMessage(String code, Object[] args, String defaultMessage, Locale locale)
public static Message getMessage(String code)
public static Message getMessage(String code, Locale locale)
public static Message getMessage(String code, Object[] args)
public static Message getMessage(String code, Object[] args, Locale locale)
public static Message getMessageLocal(String code)
public static Message getMessageLocal(String code, Locale locale)
public static Message getMessageLocal(String code, Object[] args)
public static Message getMessageLocal(String code, Object[] args, Locale locale)
- Demo 示例
public void getMessage() {
// 获取消息:demo.warn.emailNotBlank=邮箱不能为空
String msg1 = MessageAccessor.getMessage("demo.warn.emailNotBlank").getDesc();
// 获取带参数的消息:demo.warn.usernameLengthError=用户名长度在{0}-{1}
String msg2 = MessageAccessor.getMessage("demo.warn.usernameLengthError", new Object[]{6, 30}).getDesc();
// 指定默认消息,没有则使用默认消息
String msg3 = MessageAccessor.getMessage("demo.warn.emailNotBlank", "Email is blank.").getDesc();
// 指定消息的语言
String msg4 = MessageAccessor.getMessage("demo.warn.emailNotBlank", LanguageHelper.locale()).getDesc();
}
2.6 algorithm
常用算法工具。
tree(递归构建树)
-
功能说明:将具有层级结构的数据使用递归关联起来,父对象中会有一个children列表对象来存放子子对象。
-
使用方式
- Bean对象继承
org.hzero.core.algorithm.tree.Child
类,泛型为子对象的类型,也就是当前Bean的类 - 调用
org.hzero.core.algorithm.tree.TreeBuilder.buildTree(...)
静态方法构建树List<T> objList
: 该参数为原数据列表Node<P, T> nodeOperation
: 该参数为接口,继承了Key和ParentKey接口,需要自行实现,包含两个方法,getKey()方法用户获取当前节点的Key,getParentKey()用于获取父节点的Key,当前节点的key等于子节点的parentKey()时建立关联关系P rootKey
: 该参数为根节点的key,这个参数非必须,但请尽可能提供这个参数,如果不提供,会多遍历一次列表取构建根节点Key<P, T> key
: 获取当前节点的keyParentKey<P, T> parentKey
: 获取父节点的Key
- Bean对象继承
-
使用示例
class Entity extentds org.hzero.core.algorithm.tree.Child<Entity> {
Integer id;
Integer parentId;
// other field ...
// getter and setter ...
}
// method
List<Entity> objList = // from anywhere...
List<Entity> result = org.hzero.core.algorithm.tree.TreeBuilder.buildTree(objList, null, Entity::getId, Entity::getParentId)
[ [
{ {
"id":1, "id":1,
"parentId":null, "parentId":null,
... "children":[{
},{ "id":2,
"id":2, "parentId":1,
"parentId":1, -> ...
... },{
},{ "id":3,
"id":3, "parentId":1,
"parentId":1, ...
... }]
} ...
... }
] ]
structure(自定义数据结构)
- 功能说明:提供一些自定义的数据结构
- 使用说明
org.hzero.core.algorithm.structure.LinkedQueue
: 有序队列,基于LinkedList实现,线程不安全,每次add/append元素之后的数据都是按照顺序排列的,每次插入时使用二分法查找插入的位置。
2.7 jackson
封装了一些jackson序列化和反序列化的操作,支持Java8时间格式的序列化,使用前需要在Spring Boot启动类上添加org.hzero.core.jackson.annotation.EnableObjectMapper
注解。
忽略时区转换
- 功能说明:默认情况下,所有
java.util.Date
在序列化和反序列化时会按照当前用户设置的时区对时间进行转换(固定格式yyyy-MM-dd HH:mm:ss
),如果时间不需要做转换,可以使用该功能忽略时区转换。 - 使用说明
- 序列化和反序列化时忽略时区转换:在字段上添加
@org.hzero.core.jackson.annotation.IgnoreTimeZone
注解 - 序列化时忽略时区转换:在字段上添加
@com.fasterxml.jackson.databind.annotation.JsonSerialize(using = org.hzero.core.jackson.serializer.IgnoreTimeZoneDateSerializer)
注解 - 反序列化时忽略时区转换:在字段上添加
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = org.hzero.core.jackson.serializer.IgnoreTimeZoneDateDeserializer)
注解
- 序列化和反序列化时忽略时区转换:在字段上添加
字符串两端空格过滤
- 功能说明:前端往后端传输数据时,有些字段需要过滤两端空格,例如编码等,添加注解可以在反序列化时过滤掉空格。
- 使用说明:在字段上添加注解
@org.hzero.core.jackson.annotation.Trim
注解,可以指定不同的过滤策略,默认两端过滤,也可以设置左或者右一端。
敏感信息加密
- 功能说明:后端往前端传输数据时,有些数据可能需要按照某种规则屏蔽掉敏感信息,例如电话号码中间四位显示“*”。
- 使用说明
- **在数据返回前端之前,调用
org.hzero.core.jackson.sensitive.SensitiveHelper.open()
**方法,开启敏感信息屏蔽,当前线程内有效,一次序列化之后自动关闭。 - 在返回Bean的字段上添加
@org.hzero.core.jackson.annotation.Sensitive
注解,注解中可以指定多种规则取屏蔽敏感信息。left
: 从左边开始前 n 位替换right
: 从右边开始后 n 位替换cipher
: 复杂密文规则,下标从1开始,屏蔽某一位字符直接指定下标即可;屏蔽某一个范围可用下标1-下标2
,或者下标1-
表示屏蔽某一位及之后所有;多个规则之间可以用,
分割,或者用数组形式传递。例如:4-7,9,11-
表示第4,5,6,7,9,11以及11之后的位使用加密字符替换clear
: 明文规则,使用方式痛cipher
,结果相反symbol
: 加密字符,默认*
reverse
: 规则反转,指定cipher/clear
时无效。
- **在数据返回前端之前,调用
2.8 convert
封装了一些自定义的参数转换,可以通过hzero.date.converter.enable
配置关闭或者开启这些转换器,默认开启。
Date
- 功能说明 : 按照用户设置的时区对时间做反序列化,固定格式
yyyy-MM-dd HH:mm:ss
- 使用说明 : 需要使用
java.util.Date
LocalDate
- 功能说明 : Java8日期类型,仅包含年月日,固定格式
yyyy-MM-dd
- 使用说明 : 需要使用
java.time.LocalDate
三、版本更新日志
0.8.0.RELEASE [2019-3-29]
- 删除无用代码文件:
EntityFactory
、ApplicationContextEntityFactory
、HeaderParamFilter
- 增加
MessageAccessor
获取资源文件消息存取器。 - 将
starter-common
包下的jackson
、convert
、SensitiveUtils
、TrimUtils
移到starter-core
下。 - 修复已知的bug