This commit is contained in:
kk
2026-07-07 18:01:21 +08:00
commit b259f94d4b
1088 changed files with 121778 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>BladeX-Tool</artifactId>
<groupId>org.springblade</groupId>
<version>${revision}</version>
</parent>
<artifactId>blade-core-tool</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<properties>
<module.name>org.springblade.blade.core.tool</module.name>
</properties>
<dependencies>
<!-- Blade -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
</dependency>
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<!-- Guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!--Swagger-->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
</dependency>
<!-- protostuff -->
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</dependency>
<!-- validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- xml bind -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
</dependency>
<!-- sm2 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
</dependency>
<!-- Auto -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,51 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.api;
import java.io.Serializable;
/**
* 业务代码接口
*
* @author Chill
*/
public interface IResultCode extends Serializable {
/**
* 获取消息
*
* @return
*/
String getMessage();
/**
* 获取状态码
*
* @return
*/
int getCode();
}
@@ -0,0 +1,260 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.api;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.ObjectUtil;
import org.springframework.lang.Nullable;
import java.io.Serial;
import java.io.Serializable;
import java.util.Optional;
/**
* 统一API响应结果封装
*
* @author Chill
*/
@Getter
@Setter
@ToString
@Schema(description = "返回信息")
@NoArgsConstructor
public class R<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "状态码", requiredMode = Schema.RequiredMode.REQUIRED)
private int code;
@Schema(description = "是否成功", requiredMode = Schema.RequiredMode.REQUIRED)
private boolean success;
@Schema(description = "承载数据")
private T data;
@Schema(description = "返回消息", requiredMode = Schema.RequiredMode.REQUIRED)
private String msg;
private R(IResultCode resultCode) {
this(resultCode, null, resultCode.getMessage());
}
private R(IResultCode resultCode, String msg) {
this(resultCode, null, msg);
}
private R(IResultCode resultCode, T data) {
this(resultCode, data, resultCode.getMessage());
}
private R(IResultCode resultCode, T data, String msg) {
this(resultCode.getCode(), data, msg);
}
private R(int code, T data, String msg) {
this.code = code;
this.data = data;
this.msg = msg;
this.success = ResultCode.SUCCESS.code == code;
}
/**
* 判断返回是否为成功
*
* @param result Result
* @return 是否成功
*/
public static boolean isSuccess(@Nullable R<?> result) {
return Optional.ofNullable(result)
.map(x -> ObjectUtil.nullSafeEquals(ResultCode.SUCCESS.code, x.code))
.orElse(Boolean.FALSE);
}
/**
* 判断返回是否为成功
*
* @param result Result
* @return 是否成功
*/
public static boolean isNotSuccess(@Nullable R<?> result) {
return !R.isSuccess(result);
}
/**
* 返回R
*
* @param data 数据
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> data(T data) {
return data(data, BladeConstant.DEFAULT_SUCCESS_MESSAGE);
}
/**
* 返回R
*
* @param data 数据
* @param msg 消息
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> data(T data, String msg) {
return data(HttpServletResponse.SC_OK, data, msg);
}
/**
* 返回R
*
* @param code 状态码
* @param data 数据
* @param msg 消息
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> data(int code, T data, String msg) {
return new R<>(code, data, data == null ? BladeConstant.DEFAULT_NULL_MESSAGE : msg);
}
/**
* 返回成功
*
* @param <T> 泛型标记
* @return Result
*/
public static <T> R<T> success() {
return new R<>(ResultCode.SUCCESS);
}
/**
* 返回R
*
* @param msg 消息
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> success(String msg) {
return new R<>(ResultCode.SUCCESS, msg);
}
/**
* 返回R
*
* @param resultCode 业务代码
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> success(IResultCode resultCode) {
return new R<>(resultCode);
}
/**
* 返回R
*
* @param resultCode 业务代码
* @param msg 消息
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> success(IResultCode resultCode, String msg) {
return new R<>(resultCode, msg);
}
/**
* 返回R
*
* @param msg 消息
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> fail(String msg) {
return new R<>(ResultCode.FAILURE, msg);
}
/**
* 返回R
*
* @param code 状态码
* @param msg 消息
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> fail(int code, String msg) {
return new R<>(code, null, msg);
}
/**
* 返回R
*
* @param resultCode 业务代码
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> fail(IResultCode resultCode) {
return new R<>(resultCode);
}
/**
* 返回R
*
* @param resultCode 业务代码
* @param msg 消息
* @param <T> T 泛型标记
* @return R
*/
public static <T> R<T> fail(IResultCode resultCode, String msg) {
return new R<>(resultCode, msg);
}
/**
* 返回R
*
* @param flag 成功状态
* @return R
*/
public static <T> R<T> status(boolean flag) {
return flag ? success(BladeConstant.DEFAULT_SUCCESS_MESSAGE) : fail(BladeConstant.DEFAULT_FAILURE_MESSAGE);
}
/**
* 根据状态返回成功或者失败
*
* @param status 状态
* @param msg 异常msg
* @param <T> 泛型标记
* @return Result
*/
public static <T> R<T> status(boolean status, String msg) {
return status ? R.success(BladeConstant.DEFAULT_SUCCESS_MESSAGE) : R.fail(msg);
}
}
@@ -0,0 +1,122 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.api;
import lombok.AllArgsConstructor;
import lombok.Getter;
import jakarta.servlet.http.HttpServletResponse;
/**
* 业务代码枚举
*
* @author Chill
*/
@Getter
@AllArgsConstructor
public enum ResultCode implements IResultCode {
/**
* 操作成功
*/
SUCCESS(HttpServletResponse.SC_OK, "操作成功"),
/**
* 业务异常
*/
FAILURE(HttpServletResponse.SC_BAD_REQUEST, "业务异常"),
/**
* 请求未授权
*/
UN_AUTHORIZED(HttpServletResponse.SC_UNAUTHORIZED, "请求未授权"),
/**
* 客户端请求未授权
*/
CLIENT_UN_AUTHORIZED(HttpServletResponse.SC_UNAUTHORIZED, "客户端请求未授权"),
/**
* 404 没找到请求
*/
NOT_FOUND(HttpServletResponse.SC_NOT_FOUND, "404 没找到请求"),
/**
* 消息不能读取
*/
MSG_NOT_READABLE(HttpServletResponse.SC_BAD_REQUEST, "消息不能读取"),
/**
* 不支持当前请求方法
*/
METHOD_NOT_SUPPORTED(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "不支持当前请求方法"),
/**
* 不支持当前媒体类型
*/
MEDIA_TYPE_NOT_SUPPORTED(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "不支持当前媒体类型"),
/**
* 请求被拒绝
*/
REQ_REJECT(HttpServletResponse.SC_FORBIDDEN, "请求被拒绝"),
/**
* 服务器异常
*/
INTERNAL_SERVER_ERROR(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "请求未完成,请联系管理员"),
/**
* 缺少必要的请求参数
*/
PARAM_MISS(HttpServletResponse.SC_BAD_REQUEST, "缺少必要的请求参数"),
/**
* 请求参数类型错误
*/
PARAM_TYPE_ERROR(HttpServletResponse.SC_BAD_REQUEST, "请求参数类型错误"),
/**
* 请求参数绑定错误
*/
PARAM_BIND_ERROR(HttpServletResponse.SC_BAD_REQUEST, "请求参数绑定错误"),
/**
* 参数校验失败
*/
PARAM_VALID_ERROR(HttpServletResponse.SC_BAD_REQUEST, "参数校验失败"),
;
/**
* code编码
*/
final int code;
/**
* 中文信息描述
*/
final String message;
}
@@ -0,0 +1,16 @@
package org.springblade.core.tool.beans;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* Bean属性
*
* @author Chill
*/
@Getter
@AllArgsConstructor
public class BeanProperty {
private final String name;
private final Class<?> type;
}
@@ -0,0 +1,438 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.beans;
import lombok.Setter;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.ClassUtil;
import org.springblade.core.tool.utils.ReflectUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Label;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.cglib.core.*;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import java.beans.PropertyDescriptor;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* spring cglib 魔改
*
* <p>
* 1. 支持链式 bean,支持 map
* 2. ClassLoader 跟 target 保持一致
* </p>
*
* @author L.cm
*/
public abstract class BladeBeanCopier {
private static final String BEAN_NAME_PREFIX = BladeBeanCopier.class.getName();
private static final Type CONVERTER = TypeUtils.parseType("org.springframework.cglib.core.Converter");
private static final Type BEAN_COPIER = TypeUtils.parseType(BEAN_NAME_PREFIX);
private static final Type BEAN_MAP = TypeUtils.parseType(Map.class.getName());
private static final Signature COPY = new Signature("copy", Type.VOID_TYPE, new Type[]{Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER});
private static final Signature CONVERT = TypeUtils.parseSignature("Object convert(Object, Class, Object)");
private static final Signature BEAN_MAP_GET = TypeUtils.parseSignature("Object get(Object)");
private static final Type CLASS_UTILS = TypeUtils.parseType(ClassUtils.class.getName());
private static final Signature IS_ASSIGNABLE_VALUE = TypeUtils.parseSignature("boolean isAssignableValue(Class, Object)");
/**
* The map to store {@link BladeBeanCopier} of source type and class type for copy.
*/
private static final ConcurrentMap<BladeBeanCopierKey, BladeBeanCopier> BEAN_COPIER_MAP = new ConcurrentHashMap<>();
public static BladeBeanCopier create(Class source, Class target, boolean useConverter) {
return BladeBeanCopier.create(source, target, useConverter, false);
}
public static BladeBeanCopier create(Class source, Class target, boolean useConverter, boolean nonNull) {
BladeBeanCopierKey copierKey = new BladeBeanCopierKey(source, target, useConverter, nonNull);
// 利用 ConcurrentMap 缓存 提高性能,接近 直接 get set
return BEAN_COPIER_MAP.computeIfAbsent(copierKey, key -> {
Generator gen = new Generator(copierKey);
gen.setSource(key.getSource());
gen.setTarget(key.getTarget());
gen.setUseConverter(key.isUseConverter());
gen.setNonNull(key.isNonNull());
gen.setUseCache(true);
return gen.create(key);
});
}
/**
* Bean copy
*
* @param from from Bean
* @param to to Bean
* @param converter Converter
*/
abstract public void copy(Object from, Object to, @Nullable Converter converter);
public static class Generator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(BEAN_NAME_PREFIX);
private final Object key;
private Class source;
private Class target;
@Setter
private boolean useConverter;
@Setter
private boolean nonNull;
private String className;
Generator(Object key) {
super(SOURCE);
this.key = key;
}
public void setSource(Class source) {
if (!Modifier.isPublic(source.getModifiers())) {
setNamePrefix(source.getName());
}
this.source = source;
}
public void setTarget(Class target) {
if (!Modifier.isPublic(target.getModifiers())) {
setNamePrefix(target.getName());
}
this.target = target;
}
@Override
protected ClassLoader getDefaultClassLoader() {
// L.cm 保证 和 返回使用同一个 ClassLoader
return target.getClassLoader();
}
@Override
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(source);
}
@Override
public BladeBeanCopier create(Object key) {
return (BladeBeanCopier) super.create(key);
}
public BladeBeanCopier create() {
return (BladeBeanCopier) super.create(key);
}
@Override
public void generateClass(ClassVisitor v) {
Type sourceType = Type.getType(source);
Type targetType = Type.getType(target);
ClassEmitter ce = new ClassEmitter(v);
ce.begin_class(Constants.V1_2,
Constants.ACC_PUBLIC,
this.className,
BEAN_COPIER,
null,
Constants.SOURCE_FILE);
EmitUtils.null_constructor(ce);
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
// map 单独处理
if (Map.class.isAssignableFrom(source)) {
generateClassFormMap(ce, e, sourceType, targetType);
return;
}
// 2018.12.27 by L.cm 支持链式 bean
// 注意:此处需兼容链式bean 使用了 spring 的方法,比较耗时
PropertyDescriptor[] getters = ReflectUtil.getBeanGetters(source);
PropertyDescriptor[] setters = ReflectUtil.getBeanSetters(target);
Map<String, PropertyDescriptor> names = new HashMap<>(16);
for (PropertyDescriptor getter : getters) {
names.put(getter.getName(), getter);
}
Local targetLocal = e.make_local();
Local sourceLocal = e.make_local();
e.load_arg(1);
e.checkcast(targetType);
e.store_local(targetLocal);
e.load_arg(0);
e.checkcast(sourceType);
e.store_local(sourceLocal);
for (PropertyDescriptor setter : setters) {
String propName = setter.getName();
CopyProperty targetIgnoreCopy = ReflectUtil.getAnnotation(target, propName, CopyProperty.class);
// set 上有忽略的 注解
if (targetIgnoreCopy != null) {
if (targetIgnoreCopy.ignore()) {
continue;
}
// 注解上的别名,如果别名不为空,使用别名
String aliasTargetPropName = targetIgnoreCopy.value();
if (StringUtil.isNotBlank(aliasTargetPropName)) {
propName = aliasTargetPropName;
}
}
// 找到对应的 get
PropertyDescriptor getter = names.get(propName);
// 没有 get 跳出
if (getter == null) {
continue;
}
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
Method writeMethod = setter.getWriteMethod();
MethodInfo write = ReflectUtils.getMethodInfo(writeMethod);
Type returnType = read.getSignature().getReturnType();
Type setterType = write.getSignature().getArgumentTypes()[0];
Class<?> getterPropertyType = getter.getPropertyType();
Class<?> setterPropertyType = setter.getPropertyType();
// L.cm 2019.01.12 优化逻辑,先判断类型,类型一致直接 set,不同再判断 是否 类型转换
// nonNull Label
Label l0 = e.make_label();
// 判断类型是否一致,包括 包装类型
if (ClassUtil.isAssignable(setterPropertyType, getterPropertyType)) {
// 2018.12.27 by L.cm 支持链式 bean
e.load_local(targetLocal);
e.load_local(sourceLocal);
e.invoke(read);
boolean getterIsPrimitive = getterPropertyType.isPrimitive();
boolean setterIsPrimitive = setterPropertyType.isPrimitive();
if (nonNull) {
// 需要落栈,强制装箱
e.box(returnType);
Local var = e.make_local();
e.store_local(var);
e.load_local(var);
// nonNull Label
e.ifnull(l0);
e.load_local(targetLocal);
e.load_local(var);
// 需要落栈,强制拆箱
e.unbox_or_zero(setterType);
} else {
// 如果 get 为原始类型,需要装箱
if (getterIsPrimitive && !setterIsPrimitive) {
e.box(returnType);
}
// 如果 set 为原始类型,需要拆箱
if (!getterIsPrimitive && setterIsPrimitive) {
e.unbox_or_zero(setterType);
}
}
// 构造 set 方法
invokeWrite(e, write, writeMethod, nonNull, l0);
} else if (useConverter) {
e.load_local(targetLocal);
e.load_arg(2);
e.load_local(sourceLocal);
e.invoke(read);
e.box(returnType);
if (nonNull) {
Local var = e.make_local();
e.store_local(var);
e.load_local(var);
e.ifnull(l0);
e.load_local(targetLocal);
e.load_arg(2);
e.load_local(var);
}
EmitUtils.load_class(e, setterType);
// 更改成了属性名,之前是 set 方法名
e.push(propName);
e.invoke_interface(CONVERTER, CONVERT);
e.unbox_or_zero(setterType);
// 构造 set 方法
invokeWrite(e, write, writeMethod, nonNull, l0);
}
}
e.return_value();
e.end_method();
ce.end_class();
}
private static void invokeWrite(CodeEmitter e, MethodInfo write, Method writeMethod, boolean nonNull, Label l0) {
// 返回值,判断 链式 bean
Class<?> returnType = writeMethod.getReturnType();
e.invoke(write);
// 链式 bean,有返回值需要 pop
if (!returnType.equals(Void.TYPE)) {
e.pop();
}
if (nonNull) {
e.visitLabel(l0);
}
}
@Override
protected Object firstInstance(Class type) {
return BeanUtil.newInstance(type);
}
@Override
protected Object nextInstance(Object instance) {
return instance;
}
@Override
protected Class generate(ClassLoaderData data) {
// 生成类名
data.reserveName(generateClassName(data.getUniqueNamePredicate()));
try {
return MethodHandles.lookup()
.defineClass(DefaultGeneratorStrategy.INSTANCE.generate(this))
.asSubclass(BladeBeanCopier.class);
} catch (Exception ex) {
throw new CodeGenerationException(ex);
}
}
private String generateClassName(Predicate nameTestPredicate) {
this.className = DefaultNamingPolicy.INSTANCE.getClassName(BEAN_NAME_PREFIX, BEAN_NAME_PREFIX, key, nameTestPredicate);
return this.className;
}
/**
* 处理 map 的 copy
*
* @param ce ClassEmitter
* @param e CodeEmitter
* @param sourceType sourceType
* @param targetType targetType
*/
public void generateClassFormMap(ClassEmitter ce, CodeEmitter e, Type sourceType, Type targetType) {
// 2018.12.27 by L.cm 支持链式 bean
PropertyDescriptor[] setters = ReflectUtil.getBeanSetters(target);
// 入口变量
Local targetLocal = e.make_local();
Local sourceLocal = e.make_local();
e.load_arg(1);
e.checkcast(targetType);
e.store_local(targetLocal);
e.load_arg(0);
e.checkcast(sourceType);
e.store_local(sourceLocal);
Type mapBox = Type.getType(Object.class);
for (PropertyDescriptor setter : setters) {
String propName = setter.getName();
// set 上有忽略的 注解
CopyProperty targetIgnoreCopy = ReflectUtil.getAnnotation(target, propName, CopyProperty.class);
if (targetIgnoreCopy != null) {
if (targetIgnoreCopy.ignore()) {
continue;
}
// 注解上的别名
String aliasTargetPropName = targetIgnoreCopy.value();
if (StringUtil.isNotBlank(aliasTargetPropName)) {
propName = aliasTargetPropName;
}
}
Method writeMethod = setter.getWriteMethod();
MethodInfo write = ReflectUtils.getMethodInfo(writeMethod);
Type setterType = write.getSignature().getArgumentTypes()[0];
e.load_local(targetLocal);
e.load_local(sourceLocal);
e.push(propName);
// 执行 map get
e.invoke_interface(BEAN_MAP, BEAN_MAP_GET);
// box 装箱,避免 array[] 数组问题
e.box(mapBox);
// 生成变量
Local var = e.make_local();
e.store_local(var);
e.load_local(var);
// 先判断 不为null,然后做类型判断
Label l0 = e.make_label();
e.ifnull(l0);
EmitUtils.load_class(e, setterType);
e.load_local(var);
// ClassUtils.isAssignableValue(Integer.class, id)
e.invoke_static(CLASS_UTILS, IS_ASSIGNABLE_VALUE);
Label l1 = new Label();
// 返回值,判断 链式 bean
Class<?> returnType = writeMethod.getReturnType();
if (useConverter) {
e.if_jump(Opcodes.IFEQ, l1);
e.load_local(targetLocal);
e.load_local(var);
e.unbox_or_zero(setterType);
e.invoke(write);
if (!returnType.equals(Void.TYPE)) {
e.pop();
}
e.goTo(l0);
e.visitLabel(l1);
e.load_local(targetLocal);
e.load_arg(2);
e.load_local(var);
EmitUtils.load_class(e, setterType);
e.push(propName);
e.invoke_interface(CONVERTER, CONVERT);
e.unbox_or_zero(setterType);
e.invoke(write);
} else {
e.if_jump(Opcodes.IFEQ, l0);
e.load_local(targetLocal);
e.load_local(var);
e.unbox_or_zero(setterType);
e.invoke(write);
}
// 返回值,判断 链式 bean
if (!returnType.equals(Void.TYPE)) {
e.pop();
}
e.visitLabel(l0);
}
e.return_value();
e.end_method();
ce.end_class();
}
}
}
@@ -0,0 +1,20 @@
package org.springblade.core.tool.beans;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
/**
* copy key
*
* @author L.cm
*/
@Getter
@EqualsAndHashCode
@AllArgsConstructor
public class BladeBeanCopierKey {
private final Class<?> source;
private final Class<?> target;
private final boolean useConverter;
private final boolean nonNull;
}
@@ -0,0 +1,125 @@
package org.springblade.core.tool.beans;
import org.springframework.asm.ClassVisitor;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.cglib.core.AbstractClassGenerator;
import org.springframework.cglib.core.ReflectUtils;
import java.security.ProtectionDomain;
/**
* 重写 cglib BeanMap,支持链式bean
*
* @author L.cm
*/
public abstract class BladeBeanMap extends BeanMap {
protected BladeBeanMap() {
}
protected BladeBeanMap(Object bean) {
super(bean);
}
public static BladeBeanMap create(Object bean) {
BladeGenerator gen = new BladeGenerator();
gen.setBean(bean);
return gen.create();
}
/**
* newInstance
*
* @param o Object
* @return BladeBeanMap
*/
@Override
public abstract BladeBeanMap newInstance(Object o);
public static class BladeGenerator extends AbstractClassGenerator {
private static final Source SOURCE = new Source(BladeBeanMap.class.getName());
private Object bean;
private Class beanClass;
private int require;
public BladeGenerator() {
super(SOURCE);
}
/**
* Set the bean that the generated map should reflect. The bean may be swapped
* out for another bean of the same type using {@link #setBean}.
* Calling this method overrides any value previously set using {@link #setBeanClass}.
* You must call either this method or {@link #setBeanClass} before {@link #create}.
*
* @param bean the initial bean
*/
public void setBean(Object bean) {
this.bean = bean;
if (bean != null) {
beanClass = bean.getClass();
}
}
/**
* Set the class of the bean that the generated map should support.
* You must call either this method or {@link #setBeanClass} before {@link #create}.
*
* @param beanClass the class of the bean
*/
public void setBeanClass(Class beanClass) {
this.beanClass = beanClass;
}
/**
* Limit the properties reflected by the generated map.
*
* @param require any combination of {@link #REQUIRE_GETTER} and
* {@link #REQUIRE_SETTER}; default is zero (any property allowed)
*/
public void setRequire(int require) {
this.require = require;
}
@Override
protected ClassLoader getDefaultClassLoader() {
return beanClass.getClassLoader();
}
@Override
protected ProtectionDomain getProtectionDomain() {
return ReflectUtils.getProtectionDomain(beanClass);
}
/**
* Create a new instance of the <code>BeanMap</code>. An existing
* generated class will be reused if possible.
*
* @return {BladeBeanMap}
*/
public BladeBeanMap create() {
if (beanClass == null) {
throw new IllegalArgumentException("Class of bean unknown");
}
setNamePrefix(beanClass.getName());
BladeBeanMapKey key = new BladeBeanMapKey(beanClass, require);
return (BladeBeanMap) super.create(key);
}
@Override
public void generateClass(ClassVisitor v) throws Exception {
new BladeBeanMapEmitter(v, getClassName(), beanClass, require);
}
@Override
protected Object firstInstance(Class type) {
return ((BeanMap) ReflectUtils.newInstance(type)).newInstance(bean);
}
@Override
protected Object nextInstance(Object instance) {
return ((BeanMap) instance).newInstance(bean);
}
}
}
@@ -0,0 +1,192 @@
package org.springblade.core.tool.beans;
import org.springblade.core.tool.utils.ReflectUtil;
import org.springframework.asm.ClassVisitor;
import org.springframework.asm.Label;
import org.springframework.asm.Type;
import org.springframework.cglib.core.*;
import java.beans.PropertyDescriptor;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* 重写 cglib BeanMap 处理器
*
* @author L.cm
*/
class BladeBeanMapEmitter extends ClassEmitter {
private static final Type BEAN_MAP = TypeUtils.parseType(BladeBeanMap.class.getName());
private static final Type FIXED_KEY_SET = TypeUtils.parseType("org.springframework.cglib.beans.FixedKeySet");
private static final Signature CSTRUCT_OBJECT = TypeUtils.parseConstructor("Object");
private static final Signature CSTRUCT_STRING_ARRAY = TypeUtils.parseConstructor("String[]");
private static final Signature BEAN_MAP_GET = TypeUtils.parseSignature("Object get(Object, Object)");
private static final Signature BEAN_MAP_PUT = TypeUtils.parseSignature("Object put(Object, Object, Object)");
private static final Signature KEY_SET = TypeUtils.parseSignature("java.util.Set keySet()");
private static final Signature NEW_INSTANCE = new Signature("newInstance", BEAN_MAP, new Type[]{Constants.TYPE_OBJECT});
private static final Signature GET_PROPERTY_TYPE = TypeUtils.parseSignature("Class getPropertyType(String)");
public BladeBeanMapEmitter(ClassVisitor v, String className, Class type, int require) {
super(v);
begin_class(Constants.V1_2, Constants.ACC_PUBLIC, className, BEAN_MAP, null, Constants.SOURCE_FILE);
EmitUtils.null_constructor(this);
EmitUtils.factory_method(this, NEW_INSTANCE);
generateConstructor();
Map<String, PropertyDescriptor> getters = makePropertyMap(ReflectUtil.getBeanGetters(type));
Map<String, PropertyDescriptor> setters = makePropertyMap(ReflectUtil.getBeanSetters(type));
Map<String, PropertyDescriptor> allProps = new HashMap<>(32);
allProps.putAll(getters);
allProps.putAll(setters);
if (require != 0) {
for (Iterator it = allProps.keySet().iterator(); it.hasNext(); ) {
String name = (String) it.next();
if ((((require & BladeBeanMap.REQUIRE_GETTER) != 0) && !getters.containsKey(name)) ||
(((require & BladeBeanMap.REQUIRE_SETTER) != 0) && !setters.containsKey(name))) {
it.remove();
getters.remove(name);
setters.remove(name);
}
}
}
generateGet(type, getters);
generatePut(type, setters);
String[] allNames = getNames(allProps);
generateKeySet(allNames);
generateGetPropertyType(allProps, allNames);
end_class();
}
private Map<String, PropertyDescriptor> makePropertyMap(PropertyDescriptor[] props) {
Map<String, PropertyDescriptor> names = new HashMap<>(16);
for (PropertyDescriptor prop : props) {
String propName = prop.getName();
// 过滤 getClassSpring 的工具类会拿到该方法
if (!"class".equals(propName)) {
names.put(propName, prop);
}
}
return names;
}
private String[] getNames(Map<String, PropertyDescriptor> propertyMap) {
return propertyMap.keySet().toArray(new String[0]);
}
private void generateConstructor() {
CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
e.load_this();
e.load_arg(0);
e.super_invoke_constructor(CSTRUCT_OBJECT);
e.return_value();
e.end_method();
}
private void generateGet(Class type, final Map<String, PropertyDescriptor> getters) {
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null);
e.load_arg(0);
e.checkcast(Type.getType(type));
e.load_arg(1);
e.checkcast(Constants.TYPE_STRING);
EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
@Override
public void processCase(Object key, Label end) {
PropertyDescriptor pd = getters.get(key);
MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod());
e.invoke(method);
e.box(method.getSignature().getReturnType());
e.return_value();
}
@Override
public void processDefault() {
e.aconst_null();
e.return_value();
}
});
e.end_method();
}
private void generatePut(Class type, final Map<String, PropertyDescriptor> setters) {
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_PUT, null);
e.load_arg(0);
e.checkcast(Type.getType(type));
e.load_arg(1);
e.checkcast(Constants.TYPE_STRING);
EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
@Override
public void processCase(Object key, Label end) {
PropertyDescriptor pd = setters.get(key);
if (pd.getReadMethod() == null) {
e.aconst_null();
} else {
MethodInfo read = ReflectUtils.getMethodInfo(pd.getReadMethod());
e.dup();
e.invoke(read);
e.box(read.getSignature().getReturnType());
}
// move old value behind bean
e.swap();
// new value
e.load_arg(2);
MethodInfo write = ReflectUtils.getMethodInfo(pd.getWriteMethod());
e.unbox(write.getSignature().getArgumentTypes()[0]);
e.invoke(write);
e.return_value();
}
@Override
public void processDefault() {
// fall-through
}
});
e.aconst_null();
e.return_value();
e.end_method();
}
private void generateKeySet(String[] allNames) {
// static initializer
declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null);
CodeEmitter e = begin_static();
e.new_instance(FIXED_KEY_SET);
e.dup();
EmitUtils.push_array(e, allNames);
e.invoke_constructor(FIXED_KEY_SET, CSTRUCT_STRING_ARRAY);
e.putfield("keys");
e.return_value();
e.end_method();
// keySet
e = begin_method(Constants.ACC_PUBLIC, KEY_SET, null);
e.load_this();
e.getfield("keys");
e.return_value();
e.end_method();
}
private void generateGetPropertyType(final Map allProps, String[] allNames) {
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null);
e.load_arg(0);
EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
@Override
public void processCase(Object key, Label end) {
PropertyDescriptor pd = (PropertyDescriptor) allProps.get(key);
EmitUtils.load_class(e, Type.getType(pd.getPropertyType()));
e.return_value();
}
@Override
public void processDefault() {
e.aconst_null();
e.return_value();
}
});
e.end_method();
}
}
@@ -0,0 +1,16 @@
package org.springblade.core.tool.beans;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
/**
* bean map key,提高性能
*
* @author L.cm
*/
@EqualsAndHashCode
@AllArgsConstructor
public class BladeBeanMapKey {
private final Class type;
private final int require;
}
@@ -0,0 +1,26 @@
package org.springblade.core.tool.beans;
import java.lang.annotation.*;
/**
* copy 字段 配置
*
* @author L.cm
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CopyProperty {
/**
* 属性名,用于指定别名,默认使用:field name
* @return 属性名
*/
String value() default "";
/**
* 忽略:默认为 false
* @return 是否忽略
*/
boolean ignore() default false;
}
@@ -0,0 +1,23 @@
package org.springblade.core.tool.config;
import org.springblade.core.tool.convert.EnumToStringConverter;
import org.springblade.core.tool.convert.StringToEnumConverter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* blade enum 《-》 String 转换配置
*
* @author L.cm
*/
@AutoConfiguration
public class BladeConverterConfiguration implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new EnumToStringConverter());
registry.addConverter(new StringToEnumConverter());
}
}
@@ -0,0 +1,67 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.config;
import org.springblade.core.tool.jackson.*;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
/**
* BladeView 视图序列化自动装配
* <p>
* 框架内置常驻加载,无需手动开启。
* 当 Controller 方法未标注 {@link BladeView} 时不产生任何额外开销。
* 所有 Bean 均支持 {@link ConditionalOnMissingBean},可由用户自定义覆盖。
* </p>
*
* @author Chill
*/
@AutoConfiguration(after = JacksonConfiguration.class)
public class BladeViewAutoConfiguration {
/**
* 视图解析器
*/
@Bean
@ConditionalOnMissingBean
public BladeViewResolver bladeViewResolver(BladeJacksonProperties properties, ObjectProvider<BladeViewCustomizer> viewCustomizer) {
BladeViewResolver resolver = new BladeViewResolver(properties.getView());
viewCustomizer.orderedStream().forEach(customizer -> customizer.customize(resolver));
return resolver;
}
/**
* 响应拦截器
*/
@Bean
@ConditionalOnMissingBean
public BladeViewResponseAdvice bladeViewResponseAdvice(BladeViewResolver viewResolver, ObjectProvider<BladeRoleSupplier> roleNameSupplier) {
return new BladeViewResponseAdvice(viewResolver, roleNameSupplier.getIfAvailable());
}
}
@@ -0,0 +1,61 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.config;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springblade.core.tool.jackson.BladeJacksonProperties;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
/**
* Jackson配置类
*
* @author Chill
*/
@AutoConfiguration(before = JacksonAutoConfiguration.class)
@ConditionalOnClass(ObjectMapper.class)
@EnableConfigurationProperties(BladeJacksonProperties.class)
public class JacksonConfiguration {
@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper() {
//创建默认的ObjectMapper
ObjectMapper objectMapper = JsonUtil.getInstance();
//允许空字符串序列化为null对象
objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
//自定义拓展配置
objectMapper.findAndRegisterModules();
return objectMapper;
}
}
@@ -0,0 +1,82 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import org.springblade.core.tool.jackson.BladeJacksonProperties;
import org.springblade.core.tool.jackson.MappingApiJackson2HttpMessageConverter;
import org.springblade.core.tool.utils.DateUtil;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.converter.*;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* 消息配置类
*
* @author Chill
*/
@AutoConfiguration
@AllArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MessageConfiguration implements WebMvcConfigurer {
private final ObjectMapper objectMapper;
private final BladeJacksonProperties properties;
/**
* 使用 JACKSON 作为JSON MessageConverter
* 消息转换,内置断点续传,下载和字符串
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.removeIf(x -> x instanceof StringHttpMessageConverter || x instanceof AbstractJackson2HttpMessageConverter);
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new ResourceHttpMessageConverter());
converters.add(new ResourceRegionHttpMessageConverter());
converters.add(new MappingApiJackson2HttpMessageConverter(objectMapper, properties));
}
/**
* 日期格式化
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new DateFormatter(DateUtil.PATTERN_DATE));
registry.addFormatter(new DateFormatter(DateUtil.PATTERN_DATETIME));
}
}
@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.config;
import org.springblade.core.tool.support.BinderSupplier;
import org.springblade.core.tool.utils.SpringUtil;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import java.util.function.Supplier;
/**
* 工具配置类
*
* @author Chill
*/
@AutoConfiguration
public class ToolConfiguration {
/**
* Spring上下文缓存
*/
@Bean
public SpringUtil springUtil() {
return new SpringUtil();
}
/**
* Binder支持类
*/
@Bean
@ConditionalOnMissingBean
public Supplier<Object> binderSupplier() {
return new BinderSupplier();
}
}
@@ -0,0 +1,174 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.constant;
/**
* 系统常量
*
* @author Chill
*/
public interface BladeConstant {
/**
* 编码
*/
String UTF_8 = "UTF-8";
/**
* contentType
*/
String CONTENT_TYPE_NAME = "Content-type";
/**
* JSON 资源
*/
String CONTENT_TYPE = "application/json;charset=utf-8";
/**
* 上下文键值
*/
String CONTEXT_KEY = "bladeContext";
/**
* 用于 聚合层 向调用层传递请求信息 的请求头
*/
String CONTEXT_REQUEST_ID = "Blade-RequestId";
/**
* 用于 聚合层 向调用层传递用户信息 的请求头
*/
String CONTEXT_ACCOUNT_ID = "Blade-AccountId";
/**
* 用于 聚合层 向调用层传递租户信息 的请求头
*/
String CONTEXT_TENANT_ID = "Blade-TenantId";
/**
* mdc request id key
*/
String MDC_REQUEST_ID_KEY = "requestId";
/**
* mdc account id key
*/
String MDC_ACCOUNT_ID_KEY = "accountId";
/**
* mdc tenant id key
*/
String MDC_TENANT_ID_KEY = "tenantId";
/**
* 角色前缀
*/
String SECURITY_ROLE_PREFIX = "ROLE_";
/**
* 主键字段名
*/
String DB_PRIMARY_KEY = "id";
/**
* 主键字段get方法
*/
String DB_PRIMARY_KEY_METHOD = "getId";
/**
* 租户字段名
*/
String DB_TENANT_KEY = "tenantId";
/**
* 租户字段get方法
*/
String DB_TENANT_KEY_GET_METHOD = "getTenantId";
/**
* 租户字段set方法
*/
String DB_TENANT_KEY_SET_METHOD = "setTenantId";
/**
* 业务状态[正常]
*/
int DB_STATUS_NORMAL = 1;
/**
* 业务状态[0、1、2]
*/
int DB_STATUS_0 = 0;
int DB_STATUS_1 = 1;
int DB_STATUS_2 = 2;
/**
* 删除状态[0:正常,1:删除]
*/
int DB_NOT_DELETED = 0;
int DB_IS_DELETED = 1;
/**
* 用户锁定状态
*/
int DB_ADMIN_NON_LOCKED = 0;
int DB_ADMIN_LOCKED = 1;
/**
* 顶级父节点id
*/
Long TOP_PARENT_ID = 0L;
/**
* 顶级父节点名称
*/
String TOP_PARENT_NAME = "顶级";
/**
* 管理员对应的租户ID
*/
String ADMIN_TENANT_ID = "000000";
/**
* 日志默认状态
*/
String LOG_NORMAL_TYPE = "1";
/**
* 默认为空消息
*/
String DEFAULT_NULL_MESSAGE = "暂无承载数据";
/**
* 默认成功消息
*/
String DEFAULT_SUCCESS_MESSAGE = "操作成功";
/**
* 默认失败消息
*/
String DEFAULT_FAILURE_MESSAGE = "操作失败";
/**
* 默认未授权消息
*/
String DEFAULT_UNAUTHORIZED_MESSAGE = "签名认证失败";
}
@@ -0,0 +1,51 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.constant;
/**
* 系统默认角色
*
* @author Chill
*/
public class RoleConstant {
public static final String ADMINISTRATOR = "administrator";
public static final String HAS_ROLE_ADMINISTRATOR = "hasRole('" + ADMINISTRATOR + "')";
public static final String ADMIN = "admin";
public static final String HAS_ROLE_ADMIN = "hasAnyRole('" + ADMINISTRATOR + "', '" + ADMIN + "')";
public static final String USER = "user";
public static final String HAS_ROLE_USER = "hasRole('" + USER + "')";
public static final String TEST = "test";
public static final String HAS_ROLE_TEST = "hasRole('" + TEST + "')";
}
@@ -0,0 +1,50 @@
package org.springblade.core.tool.convert;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.lang.Nullable;
import org.springframework.util.StringValueResolver;
/**
* 类型 转换 服务,添加了 IEnum 转换
*
* @author L.cm
*/
public class BladeConversionService extends ApplicationConversionService {
@Nullable
private static volatile BladeConversionService SHARED_INSTANCE;
public BladeConversionService() {
this(null);
}
public BladeConversionService(@Nullable StringValueResolver embeddedValueResolver) {
super(embeddedValueResolver);
super.addConverter(new EnumToStringConverter());
super.addConverter(new StringToEnumConverter());
}
/**
* Return a shared default application {@code ConversionService} instance, lazily
* building it once needed.
* <p>
* Note: This method actually returns an {@link BladeConversionService}
* instance. However, the {@code ConversionService} signature has been preserved for
* binary compatibility.
* @return the shared {@code BladeConversionService} instance (never{@code null})
*/
public static GenericConversionService getInstance() {
BladeConversionService sharedInstance = BladeConversionService.SHARED_INSTANCE;
if (sharedInstance == null) {
synchronized (BladeConversionService.class) {
sharedInstance = BladeConversionService.SHARED_INSTANCE;
if (sharedInstance == null) {
sharedInstance = new BladeConversionService();
BladeConversionService.SHARED_INSTANCE = sharedInstance;
}
}
}
return sharedInstance;
}
}
@@ -0,0 +1,77 @@
package org.springblade.core.tool.convert;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.function.CheckedFunction;
import org.springblade.core.tool.utils.ClassUtil;
import org.springblade.core.tool.utils.ConvertUtil;
import org.springblade.core.tool.utils.ReflectUtil;
import org.springblade.core.tool.utils.Unchecked;
import org.springframework.cglib.core.Converter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 组合 spring cglib Converter 和 spring ConversionService
*
* @author L.cm
*/
@Slf4j
@AllArgsConstructor
public class BladeConverter implements Converter {
private static final ConcurrentMap<String, TypeDescriptor> TYPE_CACHE = new ConcurrentHashMap<>();
private final Class<?> sourceClazz;
private final Class<?> targetClazz;
/**
* cglib convert
*
* @param value 源对象属性
* @param target 目标对象属性类
* @param fieldName 目标的field名,原为 set 方法名,BladeBeanCopier 里做了更改
* @return {Object}
*/
@Override
@Nullable
public Object convert(Object value, Class target, final Object fieldName) {
if (value == null) {
return null;
}
// 类型一样,不需要转换
if (ClassUtil.isAssignableValue(target, value)) {
return value;
}
try {
TypeDescriptor targetDescriptor = BladeConverter.getTypeDescriptor(targetClazz, (String) fieldName);
// 1. 判断 sourceClazz 为 Map
if (Map.class.isAssignableFrom(sourceClazz)) {
return ConvertUtil.convert(value, targetDescriptor);
} else {
TypeDescriptor sourceDescriptor = BladeConverter.getTypeDescriptor(sourceClazz, (String) fieldName);
return ConvertUtil.convert(value, sourceDescriptor, targetDescriptor);
}
} catch (Throwable e) {
log.warn("BladeConverter error", e);
return null;
}
}
private static TypeDescriptor getTypeDescriptor(final Class<?> clazz, final String fieldName) {
String srcCacheKey = clazz.getName() + fieldName;
// 忽略抛出异常的函数,定义完整泛型,避免编译问题
CheckedFunction<String, TypeDescriptor> uncheckedFunction = (key) -> {
// 这里 property 理论上不会为 null
Field field = ReflectUtil.getField(clazz, fieldName);
if (field == null) {
throw new NoSuchFieldException(fieldName);
}
return new TypeDescriptor(field);
};
return TYPE_CACHE.computeIfAbsent(srcCacheKey, Unchecked.function(uncheckedFunction));
}
}
@@ -0,0 +1,135 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.convert;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.utils.ConvertUtil;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.lang.Nullable;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 接收参数 同 jackson Enum -》 String 转换
*
* @author L.cm
*/
@Slf4j
public class EnumToStringConverter implements ConditionalGenericConverter {
/**
* 缓存 Enum 类信息,提供性能
*/
private static final ConcurrentMap<Class<?>, AccessibleObject> ENUM_CACHE_MAP = new ConcurrentHashMap<>(8);
@Nullable
private static AccessibleObject getAnnotation(Class<?> clazz) {
Set<AccessibleObject> accessibleObjects = new HashSet<>();
// JsonValue METHOD, FIELD
Field[] fields = clazz.getDeclaredFields();
Collections.addAll(accessibleObjects, fields);
// methods
Method[] methods = clazz.getDeclaredMethods();
Collections.addAll(accessibleObjects, methods);
for (AccessibleObject accessibleObject : accessibleObjects) {
// 复用 jackson 的 JsonValue 注解
JsonValue jsonValue = accessibleObject.getAnnotation(JsonValue.class);
if (jsonValue != null && jsonValue.value()) {
accessibleObject.setAccessible(true);
return accessibleObject;
}
}
return null;
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return true;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
Set<ConvertiblePair> pairSet = new HashSet<>(3);
pairSet.add(new ConvertiblePair(Enum.class, String.class));
pairSet.add(new ConvertiblePair(Enum.class, Integer.class));
pairSet.add(new ConvertiblePair(Enum.class, Long.class));
return Collections.unmodifiableSet(pairSet);
}
@Override
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Class<?> sourceClazz = sourceType.getType();
AccessibleObject accessibleObject = ENUM_CACHE_MAP.computeIfAbsent(sourceClazz, EnumToStringConverter::getAnnotation);
Class<?> targetClazz = targetType.getType();
// 如果为null,走默认的转换
if (accessibleObject == null) {
if (String.class == targetClazz) {
return ((Enum) source).name();
}
int ordinal = ((Enum) source).ordinal();
return ConvertUtil.convert(ordinal, targetClazz);
}
try {
return EnumToStringConverter.invoke(sourceClazz, accessibleObject, source, targetClazz);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
@Nullable
private static Object invoke(Class<?> clazz, AccessibleObject accessibleObject, Object source, Class<?> targetClazz)
throws IllegalAccessException, InvocationTargetException {
Object value = null;
if (accessibleObject instanceof Field) {
Field field = (Field) accessibleObject;
value = field.get(source);
} else if (accessibleObject instanceof Method) {
Method method = (Method) accessibleObject;
Class<?> paramType = method.getParameterTypes()[0];
// 类型转换
Object object = ConvertUtil.convert(source, paramType);
value = method.invoke(clazz, object);
}
if (value == null) {
return null;
}
return ConvertUtil.convert(value, targetClazz);
}
}
@@ -0,0 +1,135 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.convert;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.utils.ConvertUtil;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.lang.Nullable;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* 接收参数 同 jackson String -》 Enum 转换
*
* @author L.cm
*/
@Slf4j
public class StringToEnumConverter implements ConditionalGenericConverter {
/**
* 缓存 Enum 类信息,提供性能
*/
private static final ConcurrentMap<Class<?>, AccessibleObject> ENUM_CACHE_MAP = new ConcurrentHashMap<>(8);
@Nullable
private static AccessibleObject getAnnotation(Class<?> clazz) {
Set<AccessibleObject> accessibleObjects = new HashSet<>();
// JsonCreator METHOD, CONSTRUCTOR
Constructor<?>[] constructors = clazz.getConstructors();
Collections.addAll(accessibleObjects, constructors);
// methods
Method[] methods = clazz.getDeclaredMethods();
Collections.addAll(accessibleObjects, methods);
for (AccessibleObject accessibleObject : accessibleObjects) {
// 复用 jackson 的 JsonCreator注解
JsonCreator jsonCreator = accessibleObject.getAnnotation(JsonCreator.class);
if (jsonCreator != null && JsonCreator.Mode.DISABLED != jsonCreator.mode()) {
accessibleObject.setAccessible(true);
return accessibleObject;
}
}
return null;
}
@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return true;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, Enum.class));
}
@Nullable
@Override
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (StringUtil.isBlank((String) source)) {
return null;
}
Class<?> clazz = targetType.getType();
AccessibleObject accessibleObject = ENUM_CACHE_MAP.computeIfAbsent(clazz, StringToEnumConverter::getAnnotation);
String value = ((String) source).trim();
// 如果为null,走默认的转换
if (accessibleObject == null) {
return valueOf(clazz, value);
}
try {
return StringToEnumConverter.invoke(clazz, accessibleObject, value);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
@SuppressWarnings("unchecked")
private static <T extends Enum<T>> T valueOf(Class<?> clazz, String value){
return Enum.valueOf((Class<T>) clazz, value);
}
@Nullable
private static Object invoke(Class<?> clazz, AccessibleObject accessibleObject, String value)
throws IllegalAccessException, InvocationTargetException, InstantiationException {
if (accessibleObject instanceof Constructor) {
Constructor constructor = (Constructor) accessibleObject;
Class<?> paramType = constructor.getParameterTypes()[0];
// 类型转换
Object object = ConvertUtil.convert(value, paramType);
return constructor.newInstance(object);
}
if (accessibleObject instanceof Method) {
Method method = (Method) accessibleObject;
Class<?> paramType = method.getParameterTypes()[0];
// 类型转换
Object object = ConvertUtil.convert(value, paramType);
return method.invoke(clazz, object);
}
return null;
}
}
@@ -0,0 +1,47 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.function;
import org.springframework.lang.Nullable;
/**
* 受检的 Callable
*
* @author L.cm
*/
@FunctionalInterface
public interface CheckedCallable<T> {
/**
* Run this callable.
*
* @return result
* @throws Throwable CheckedException
*/
@Nullable
T call() throws Throwable;
}
@@ -0,0 +1,47 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.function;
/**
* 受检的 Comparator
*
* @author L.cm
*/
@FunctionalInterface
public interface CheckedComparator<T> {
/**
* Compares its two arguments for order.
*
* @param o1 o1
* @param o2 o2
* @return int
* @throws Throwable CheckedException
*/
int compare(T o1, T o2) throws Throwable;
}
@@ -0,0 +1,48 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.function;
import org.springframework.lang.Nullable;
/**
* 受检的 Consumer
*
* @author L.cm
*/
@FunctionalInterface
public interface CheckedConsumer<T> {
/**
* Run the Consumer
*
* @param t T
* @throws Throwable UncheckedException
*/
@Nullable
void accept(@Nullable T t) throws Throwable;
}
@@ -0,0 +1,49 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.function;
import org.springframework.lang.Nullable;
/**
* 受检的 function
*
* @author L.cm
*/
@FunctionalInterface
public interface CheckedFunction<T, R> {
/**
* Run the Function
*
* @param t T
* @return R R
* @throws Throwable CheckedException
*/
@Nullable
R apply(@Nullable T t) throws Throwable;
}
@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.function;
/**
* 受检的 runnable
*
* @author L.cm
*/
@FunctionalInterface
public interface CheckedRunnable {
/**
* Run this runnable.
*
* @throws Throwable CheckedException
*/
void run() throws Throwable;
}
@@ -0,0 +1,48 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.function;
import org.springframework.lang.Nullable;
/**
* 受检的 Supplier
*
* @author L.cm
*/
@FunctionalInterface
public interface CheckedSupplier<T> {
/**
* Run the Supplier
*
* @return T
* @throws Throwable CheckedException
*/
@Nullable
T get() throws Throwable;
}
@@ -0,0 +1,43 @@
package org.springblade.core.tool.geo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
/**
* 坐标点
*
* @author JourWon、hutool、L.cm
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GeoPoint implements Serializable {
@Serial
private static final long serialVersionUID = 3584864663880053897L;
/**
* 经度
*/
private double lon;
/**
* 纬度
*/
private double lat;
/**
* 当前坐标偏移指定坐标
*
* @param offset 偏移量
* @return this
*/
public GeoPoint offset(GeoPoint offset) {
this.lon += offset.lon;
this.lat += offset.lat;
return this;
}
}
@@ -0,0 +1,135 @@
package org.springblade.core.tool.geo;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 各坐标系之间的转换工具类
* <p>
* 参考:<a href="https://github.com/JourWon/coordinate-transform">coordinate-transform</a>
* <p>
* WGS84坐标系:即地球坐标系,国际上通用的坐标系。设备一般包含GPS芯片或者北斗芯片获取的经纬度为WGS84地理坐标系。
* 谷歌地图采用的是WGS84地理坐标系(中国范围除外,谷歌中国地图采用的是GCJ02地理坐标系。)
* <p>
* GCJ02坐标系:即火星坐标系,WGS84坐标系经加密后的坐标系。
* 出于国家安全考虑,国内所有导航电子地图必须使用国家测绘局制定的加密坐标系统,即将一个真实的经纬度坐标加密成一个不正确的经纬度坐标。
* <p>
* BD09坐标系:即百度坐标系,GCJ02坐标系经加密后的坐标系。搜狗坐标系、图吧坐标系等,估计也是在GCJ02基础上加密而成的。
* <p>
* 高德MapABC地图API 火星坐标
* 腾讯搜搜地图API 火星坐标
* 阿里云地图API 火星坐标
* 灵图51ditu地图API 火星坐标
* <p>
* 百度地图API 百度坐标
* 搜狐搜狗地图API 搜狗坐标
* 图吧MapBar地图API 图吧坐标
*
* @author JourWon、hutool、L.cm
*/
@Getter
@RequiredArgsConstructor
public enum GeoType {
/**
* WGS84
*/
WGS84("WGS84", "地球坐标系,国际通用坐标系") {
@Override
public GeoPoint toWGS84(double lon, double lat) {
return new GeoPoint(lon, lat);
}
@Override
public GeoPoint toGCJ02(double lon, double lat) {
return GeoUtil.wgs84ToGcj02(lon, lat);
}
@Override
public GeoPoint toBD09(double lon, double lat) {
return GeoUtil.wgs84ToBd09(lon, lat);
}
},
GCJ02("GCJ02", "火星坐标系,高德、腾讯、阿里等使用") {
@Override
public GeoPoint toWGS84(double lon, double lat) {
return GeoUtil.gcj02ToWgs84(lon, lat);
}
@Override
public GeoPoint toGCJ02(double lon, double lat) {
return new GeoPoint(lon, lat);
}
@Override
public GeoPoint toBD09(double lon, double lat) {
return GeoUtil.gcj02ToBd09(lon, lat);
}
},
BD09("BD09", "百度坐标系,百度、搜狗等使用") {
@Override
public GeoPoint toWGS84(double lon, double lat) {
return GeoUtil.bd09toWgs84(lon, lat);
}
@Override
public GeoPoint toGCJ02(double lon, double lat) {
return GeoUtil.bd09ToGcj02(lon, lat);
}
@Override
public GeoPoint toBD09(double lon, double lat) {
return new GeoPoint(lon, lat);
}
};
@JsonValue
private final String type;
private final String desc;
/**
* 转换成 地球坐标系
*
* @param lon lon
* @param lat lat
* @return GeoPoint
*/
public abstract GeoPoint toWGS84(double lon, double lat);
/**
* 转换成 火星坐标系
*
* @param lon lon
* @param lat lat
* @return GeoPoint
*/
public abstract GeoPoint toGCJ02(double lon, double lat);
/**
* 转换成 百度坐标系
*
* @param lon lon
* @param lat lat
* @return GeoPoint
*/
public abstract GeoPoint toBD09(double lon, double lat);
/**
* 获取坐标系
*
* @param type type 坐标系类型
* @return GeoType
*/
@JsonCreator
public static GeoType getGeoType(String type) {
for (GeoType geoType : values()) {
if (geoType.type.equalsIgnoreCase(type)) {
return geoType;
}
}
throw new IllegalArgumentException("未知的坐标系类型" + type);
}
}
@@ -0,0 +1,261 @@
package org.springblade.core.tool.geo;
import java.text.DecimalFormat;
/**
* 位置工具类
*
* @author JourWon、hutool、L.cm
*/
public class GeoUtil {
/**
* 地球的半径 (m)
*/
public static final double EARTH_RADIUS = 6378137;
/**
* 圆周率π
*/
private static final double PI = 3.1415926535897932384626D;
/**
* 火星坐标系与百度坐标系转换的中间量
*/
private static final double X_PI = 3.14159265358979324 * 3000.0 / 180.0D;
/**
* 地球半径(Krasovsky 1940
*/
public static final double RADIUS = 6378245.0D;
/**
* 修正参数(偏率ee
*/
public static final double CORRECTION_PARAM = 0.00669342162296594323D;
/**
* 经纬度格式化为字符串
*/
public static final DecimalFormat FORMAT = new DecimalFormat("#.########");
/**
* 根据经纬度计算两点之间的距离 (m)
*
* @param lon1 位置 1 的经度
* @param lat1 位置 1 的纬度
* @param lon2 位置 2 的经度
* @param lat2 位置 2 的纬度
* @return 返回距离
*/
public static double getDistance(double lon1, double lat1, double lon2, double lat2) {
double radLat1 = radian(lat1);
double radLat2 = radian(lat2);
double a = radLat1 - radLat2;
double b = radian(lon1) - radian(lon2);
return (2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
+ Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))))
* EARTH_RADIUS;
}
/**
* 将整数形式的经纬度转换为十进制度格式,因为部分设备采集上来的为十进制形式的坐标
*
* @param coordinate 整数形式的经度或纬度
* @return 十进制度格式的经纬度
*/
public static double getGpsValue(int coordinate) {
int degrees = coordinate / (3600 * 100);
int remainder = coordinate % (3600 * 100);
int minutes = remainder / (60 * 100);
remainder = remainder % (60 * 100);
double seconds = remainder / 100.0;
// 将分和秒转换为度的小数部分
return degrees + (minutes / 60.0) + (seconds / 3600.0);
}
/**
* 格式化经纬度
*
* @param value value
* @return 格式化经纬度
*/
public static String formatGeo(double value) {
return FORMAT.format(value);
}
/**
* 判断坐标是否在国外<br>
* 火星坐标系 (GCJ-02)只对国内有效,国外无需转换
*
* @param lng 经度
* @param lat 纬度
* @return 坐标是否在国外
*/
public static boolean isOutOfChina(double lng, double lat) {
return (lng < 72.004 || lng > 137.8347) || (lat < 0.8293 || lat > 55.8271);
}
/**
* WGS84 转换为 火星坐标系 (GCJ-02)
*
* @param lon 经度值
* @param lat 纬度值
* @return 火星坐标 (GCJ-02)
*/
public static GeoPoint wgs84ToGcj02(double lon, double lat) {
return new GeoPoint(lon, lat).offset(offset(lon, lat, true));
}
/**
* WGS84 坐标转为 百度坐标系 (BD-09) 坐标
*
* @param lon 经度值
* @param lat 纬度值
* @return bd09 坐标
*/
public static GeoPoint wgs84ToBd09(double lon, double lat) {
final GeoPoint gcj02 = wgs84ToGcj02(lon, lat);
return gcj02ToBd09(gcj02.getLon(), gcj02.getLat());
}
/**
* 火星坐标系 (GCJ-02) 转换为 WGS84
*
* @param lon 经度坐标
* @param lat 纬度坐标
* @return WGS84 坐标
*/
public static GeoPoint gcj02ToWgs84(double lon, double lat) {
return new GeoPoint(lon, lat).offset(offset(lon, lat, false));
}
/**
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
*
* @param lon 经度值
* @param lat 纬度值
* @return BD-09 坐标
*/
public static GeoPoint gcj02ToBd09(double lon, double lat) {
double z = Math.sqrt(lon * lon + lat * lat) + 0.00002 * Math.sin(lat * X_PI);
double theta = Math.atan2(lat, lon) + 0.000003 * Math.cos(lon * X_PI);
double bd_lng = z * Math.cos(theta) + 0.0065;
double bd_lat = z * Math.sin(theta) + 0.006;
return new GeoPoint(bd_lng, bd_lat);
}
/**
* 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
* 即 百度 转 谷歌、高德
*
* @param lon 经度值
* @param lat 纬度值
* @return GCJ-02 坐标
*/
public static GeoPoint bd09ToGcj02(double lon, double lat) {
double x = lon - 0.0065;
double y = lat - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
double gg_lon = z * Math.cos(theta);
double gg_lat = z * Math.sin(theta);
return new GeoPoint(gg_lon, gg_lat);
}
/**
* 百度坐标系 (BD-09) 与 WGS84 的转换
*
* @param lon 经度值
* @param lat 纬度值
* @return WGS84坐标
*/
public static GeoPoint bd09toWgs84(double lon, double lat) {
final GeoPoint gcj02 = bd09ToGcj02(lon, lat);
return gcj02ToWgs84(gcj02.getLon(), gcj02.getLat());
}
/**
* WGS84 坐标转为 墨卡托投影
*
* @param lon 经度值
* @param lat 纬度值
* @return 墨卡托投影
*/
public static GeoPoint wgs84ToMercator(double lon, double lat) {
double x = lon * 20037508.342789244 / 180;
double y = Math.log(Math.tan((90 + lat) * PI / 360)) / (PI / 180);
y = y * 20037508.342789244 / 180;
return new GeoPoint(x, y);
}
/**
* 墨卡托投影 转为 WGS84 坐标
*
* @param mercatorX 墨卡托X坐标
* @param mercatorY 墨卡托Y坐标
* @return WGS84 坐标
*/
public static GeoPoint mercatorToWgs84(double mercatorX, double mercatorY) {
double x = mercatorX / 20037508.342789244 * 180;
double y = mercatorY / 20037508.342789244 * 180;
y = 180 / PI * (2 * Math.atan(Math.exp(y * PI / 180)) - PI / 2);
return new GeoPoint(x, y);
}
/**
* WGS84 与 火星坐标系 (GCJ-02)转换的偏移算法(非精确)
*
* @param lon 经度值
* @param lat 纬度值
* @param isPlus 是否正向偏移:WGS84转GCJ-02使用正向,否则使用反向
* @return 偏移坐标
*/
private static GeoPoint offset(double lon, double lat, boolean isPlus) {
double dlon = transLon(lon - 105.0, lat - 35.0);
double dlat = transLat(lon - 105.0, lat - 35.0);
double magic = Math.sin(lat / 180.0 * PI);
magic = 1 - CORRECTION_PARAM * magic * magic;
final double sqrtMagic = Math.sqrt(magic);
dlon = (dlon * 180.0) / (RADIUS / sqrtMagic * Math.cos(lat / 180.0 * PI) * PI);
dlat = (dlat * 180.0) / ((RADIUS * (1 - CORRECTION_PARAM)) / (magic * sqrtMagic) * PI);
if (isPlus) {
return new GeoPoint(dlon, dlat);
} else {
return new GeoPoint(-dlon, -dlat);
}
}
/**
* 计算经度坐标
*
* @param lon 经度坐标
* @param lat 纬度坐标
* @return ret 计算完成后的
*/
private static double transLon(double lon, double lat) {
double ret = 300.0 + lon + 2.0 * lat + 0.1 * lon * lon + 0.1 * lon * lat + 0.1 * Math.sqrt(Math.abs(lon));
ret += (20.0 * Math.sin(6.0 * lon * PI) + 20.0 * Math.sin(2.0 * lon * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lon * PI) + 40.0 * Math.sin(lon / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(lon / 12.0 * PI) + 300.0 * Math.sin(lon / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
/**
* 计算纬度坐标
*
* @param lon 经度
* @param lat 纬度
* @return ret 计算完成后的
*/
private static double transLat(double lon, double lat) {
double ret = -100.0 + 2.0 * lon + 3.0 * lat + 0.2 * lat * lat + 0.1 * lon * lat
+ 0.2 * Math.sqrt(Math.abs(lon));
ret += (20.0 * Math.sin(6.0 * lon * PI) + 20.0 * Math.sin(2.0 * lon * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
private static double radian(double d) {
return d * PI / 180.0;
}
}
@@ -0,0 +1,152 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.PrettyPrinter;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import org.springblade.core.tool.utils.Charsets;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.TypeUtils;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
* 分读写的 json 消息 处理器
*
* @author L.cm
*/
public abstract class AbstractReadWriteJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
private static final java.nio.charset.Charset DEFAULT_CHARSET = Charsets.UTF_8;
private final ObjectMapper writeObjectMapper;
@Nullable
private PrettyPrinter ssePrettyPrinter;
public AbstractReadWriteJackson2HttpMessageConverter(ObjectMapper readObjectMapper, ObjectMapper writeObjectMapper) {
super(readObjectMapper);
this.writeObjectMapper = writeObjectMapper;
initSsePrettyPrinter();
}
public AbstractReadWriteJackson2HttpMessageConverter(ObjectMapper readObjectMapper, ObjectMapper writeObjectMapper, MediaType supportedMediaType) {
this(readObjectMapper, writeObjectMapper);
setSupportedMediaTypes(Collections.singletonList(supportedMediaType));
initSsePrettyPrinter();
}
public AbstractReadWriteJackson2HttpMessageConverter(ObjectMapper readObjectMapper, ObjectMapper writeObjectMapper, List<MediaType> supportedMediaTypes) {
this(readObjectMapper, writeObjectMapper);
setSupportedMediaTypes(supportedMediaTypes);
}
private void initSsePrettyPrinter() {
setDefaultCharset(DEFAULT_CHARSET);
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
this.ssePrettyPrinter = prettyPrinter;
}
@Override
public boolean canWrite(@NonNull Class<?> clazz, @Nullable MediaType mediaType) {
if (!canWrite(mediaType)) {
return false;
}
AtomicReference<Throwable> causeRef = new AtomicReference<>();
if (this.defaultObjectMapper.canSerialize(clazz, causeRef)) {
return true;
}
logWarningIfNecessary(clazz, causeRef.get());
return false;
}
@Override
protected void writeInternal(@NonNull Object object, @Nullable Type type, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
MediaType contentType = outputMessage.getHeaders().getContentType();
JsonEncoding encoding = getJsonEncoding(contentType);
JsonGenerator generator = this.writeObjectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
try {
writePrefix(generator, object);
Object value = object;
Class<?> serializationView = null;
FilterProvider filters = null;
JavaType javaType = null;
if (object instanceof MappingJacksonValue) {
MappingJacksonValue container = (MappingJacksonValue) object;
value = container.getValue();
serializationView = container.getSerializationView();
filters = container.getFilters();
}
if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
javaType = getJavaType(type, null);
}
ObjectWriter objectWriter = (serializationView != null ?
this.writeObjectMapper.writerWithView(serializationView) : this.writeObjectMapper.writer());
if (filters != null) {
objectWriter = objectWriter.with(filters);
}
if (javaType != null && javaType.isContainerType()) {
objectWriter = objectWriter.forType(javaType);
}
SerializationConfig config = objectWriter.getConfig();
if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
objectWriter = objectWriter.with(this.ssePrettyPrinter);
}
objectWriter.writeValue(generator, value);
writeSuffix(generator, object);
generator.flush();
} catch (InvalidDefinitionException ex) {
throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
} catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
}
}
}
@@ -0,0 +1,69 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.NumberSerializer;
import java.io.IOException;
/**
* 大数值序列化,避免超过js的精度,造成精度丢失
*
* @author L.cm
*/
@JacksonStdImpl
public class BigNumberSerializer extends NumberSerializer {
/**
* js 最大值为 Math.pow(2, 53),十进制为:9007199254740992
*/
private static final long JS_NUM_MAX = 0x20000000000000L;
/**
* js 最小值为 -Math.pow(2, 53),十进制为:-9007199254740992
*/
private static final long JS_NUM_MIN = -0x20000000000000L;
/**
* Static instance that is only to be used for {@link java.lang.Number}.
*/
public final static BigNumberSerializer instance = new BigNumberSerializer(Number.class);
public BigNumberSerializer(Class<? extends Number> rawType) {
super(rawType);
}
@Override
public void serialize(Number value, JsonGenerator gen, SerializerProvider provider) throws IOException {
long longValue = value.longValue();
if (longValue < JS_NUM_MIN || longValue > JS_NUM_MAX) {
gen.writeString(value.toString());
} else {
super.serialize(value, gen, provider);
}
}
}
@@ -0,0 +1,138 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import java.io.IOException;
import java.time.OffsetDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import static org.springblade.core.tool.constant.BladeConstant.DB_PRIMARY_KEY;
/**
* jackson 默认值为 null 时的处理
* <p>
* 主要是为了避免 app 端出现null导致闪退
* <p>
* 规则:
* number -1
* string ""
* date ""
* boolean false
* array []
* Object {}
*
* @author L.cm
*/
public class BladeBeanSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(
SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
// 循环所有的beanPropertyWriter
beanProperties.forEach(writer -> {
// 如果已经有 null 序列化处理如注解:@JsonSerialize(nullsUsing = xxx) 跳过
if (writer.hasNullSerializer()) {
return;
}
JavaType type = writer.getType();
Class<?> clazz = type.getRawClass();
if (type.isTypeOrSubTypeOf(Number.class)) {
// 如果字段名为 id,则跳过 null 序列化处理
if (StringUtil.equalsIgnoreCase(DB_PRIMARY_KEY, writer.getName())) {
return;
}
writer.assignNullSerializer(NullJsonSerializers.NUMBER_JSON_SERIALIZER);
} else if (type.isTypeOrSubTypeOf(Boolean.class)) {
writer.assignNullSerializer(NullJsonSerializers.BOOLEAN_JSON_SERIALIZER);
} else if (type.isTypeOrSubTypeOf(Character.class)) {
writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
} else if (type.isTypeOrSubTypeOf(String.class)) {
writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
} else if (type.isArrayType() || clazz.isArray() || type.isTypeOrSubTypeOf(Collection.class)) {
writer.assignNullSerializer(NullJsonSerializers.ARRAY_JSON_SERIALIZER);
} else if (type.isTypeOrSubTypeOf(OffsetDateTime.class)) {
writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
} else if (type.isTypeOrSubTypeOf(Date.class) || type.isTypeOrSubTypeOf(TemporalAccessor.class)) {
writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
} else {
writer.assignNullSerializer(NullJsonSerializers.OBJECT_JSON_SERIALIZER);
}
});
return super.changeProperties(config, beanDesc, beanProperties);
}
public interface NullJsonSerializers {
JsonSerializer<Object> STRING_JSON_SERIALIZER = new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(StringPool.EMPTY);
}
};
JsonSerializer<Object> NUMBER_JSON_SERIALIZER = new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeNumber(StringUtil.INDEX_NOT_FOUND);
}
};
JsonSerializer<Object> BOOLEAN_JSON_SERIALIZER = new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeObject(Boolean.FALSE);
}
};
JsonSerializer<Object> ARRAY_JSON_SERIALIZER = new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartArray();
gen.writeEndArray();
}
};
JsonSerializer<Object> OBJECT_JSON_SERIALIZER = new JsonSerializer<Object>() {
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeEndObject();
}
};
}
}
@@ -0,0 +1,82 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* jackson 配置
*
* @author L.cm
*/
@Getter
@Setter
@ConfigurationProperties("blade.jackson")
public class BladeJacksonProperties {
/**
* null 转为 空,字符串转成"",数组转为[],对象转为{},数字转为-1
*/
private Boolean nullToEmpty = Boolean.TRUE;
/**
* 响应到前端,大数值自动写出为 String,避免精度丢失
*/
private Boolean bigNumToString = Boolean.TRUE;
/**
* 支持 MediaType text/plain,用于和 blade-api-crypto 一起使用
*/
private Boolean supportTextPlain = Boolean.FALSE;
/**
* 视图配置
*/
private View view = new View();
/**
* Jackson Views 视图配置
*
* @author Chill
*/
@Getter
@Setter
public static class View {
/**
* 动态模式的默认视图(角色获取不到时降级)
* <p>可选: summary / detail / admin / administrator</p>
*/
private String defaultView = "summary";
/**
* 角色 → 视图映射
*/
private Map<String, String> roleMapping = new LinkedHashMap<>();
}
}
@@ -0,0 +1,60 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.PackageVersion;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springblade.core.tool.utils.DateTimeUtil;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
/**
* java 8 时间默认序列化
*
* @author L.cm
*/
public class BladeJavaTimeModule extends SimpleModule {
public static final BladeJavaTimeModule INSTANCE = new BladeJavaTimeModule();
public BladeJavaTimeModule() {
super(PackageVersion.VERSION);
this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeUtil.DATETIME_FORMAT));
this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeUtil.DATE_FORMAT));
this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeUtil.TIME_FORMAT));
this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeUtil.DATETIME_FORMAT));
this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeUtil.DATE_FORMAT));
this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeUtil.TIME_FORMAT));
}
}
@@ -0,0 +1,57 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* 大整数序列化为 String 字符串,避免浏览器丢失精度
*
* <p>
* 前端建议采用:
* bignumber 库: https://github.com/MikeMcl/bignumber.js
* decimal.js 库: https://github.com/MikeMcl/decimal.js
* </p>
*
* @author L.cm
*/
public class BladeNumberModule extends SimpleModule {
public static final BladeNumberModule INSTANCE = new BladeNumberModule();
public BladeNumberModule() {
super(BladeNumberModule.class.getName());
// Long 和 BigInteger 采用定制的逻辑序列化,避免超过js的精度
this.addSerializer(Long.class, BigNumberSerializer.instance);
this.addSerializer(Long.TYPE, BigNumberSerializer.instance);
this.addSerializer(BigInteger.class, BigNumberSerializer.instance);
// BigDecimal 采用 toString 避免精度丢失,前端采用 decimal.js 来计算。
this.addSerializer(BigDecimal.class, ToStringSerializer.instance);
}
}
@@ -0,0 +1,50 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.jackson;
import java.util.function.Supplier;
/**
* BladeView 角色名提供者
* <p>
* 用于 {@link BladeViewResponseAdvice} 动态模式下按角色解析视图。
* 自定义时请实现本接口而非通用的 {@code Supplier<String>}
* 以避免与业务工程中其它 {@code Supplier<String>} Bean 产生歧义。
* </p>
*
* <h3>示例</h3>
* <pre>
* &#64;Bean
* public BladeRoleSupplier myRoleSupplier() {
* return () -&gt; currentUserHolder.getRole();
* }
* </pre>
*
* @author Chill
*/
@FunctionalInterface
public interface BladeRoleSupplier extends Supplier<String> {
}
@@ -0,0 +1,43 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
* java 8 时间默认序列化
*
* @author L.cm
*/
public class BladeSensitiveModule extends SimpleModule {
public static final BladeSensitiveModule INSTANCE = new BladeSensitiveModule();
public BladeSensitiveModule() {
super(BladeSensitiveModule.class.getName());
this.addSerializer(String.class, SensitiveSerializer.instance);
}
}
@@ -0,0 +1,80 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.jackson;
import java.lang.annotation.*;
/**
* BladeX 视图控制注解(全场景统一)
* <p>
* 用在字段上:标记该字段的可见视图层级(替代 {@code @JsonView}
* 用在方法/类上:控制接口的视图过滤策略
* </p>
*
* <h3>字段标注</h3>
* <pre>
* &#64;BladeView(Views.Detail.class)
* private String tenantName;
* </pre>
*
* <h3>Controller 标注</h3>
* <pre>
* // 静态:明确指定视图
* &#64;BladeView(Views.Summary.class)
* public R&lt;List&lt;UserVO&gt;&gt; list() { ... }
*
* // 动态:根据用户角色自动解析
* &#64;BladeView
* public R&lt;UserVO&gt; detail() { ... }
*
* // 类级别默认(方法级可覆盖)
* &#64;BladeView(Views.Admin.class)
* public class AdminController { ... }
* </pre>
*
* @author Chill
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BladeView {
/**
* 视图类型
* <p>
* 字段上:指定 Views.Summary / Detail / Admin / Administrator
* 方法上:指定具体视图 = 静态模式;默认 Auto.class = 动态模式
* </p>
*/
Class<?> value() default Auto.class;
/**
* 动态解析标记(仅用于 Controller 方法/类)
*/
interface Auto {
}
}
@@ -0,0 +1,63 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import java.io.Serial;
/**
* 自定义 Jackson 注解内省器
* <p>
* 让 Jackson 序列化引擎识别字段上的 {@link BladeView} 注解,
* 将其等同于标准的 {@code @JsonView} 处理。
* </p>
* <p>
* 继承 {@link JacksonAnnotationIntrospector},所有标准 Jackson 注解
* {@code @JsonIgnore}、{@code @JsonSerialize}、{@code @JsonFormat} 等)
* 通过 super 正常工作。
* </p>
*
* @author Chill
*/
public class BladeViewAnnotationIntrospector extends JacksonAnnotationIntrospector {
@Serial
private static final long serialVersionUID = 1L;
@Override
public Class<?>[] findViews(Annotated a) {
// 优先识别 @BladeView(仅处理非 Auto 的静态视图标注)
BladeView bladeView = _findAnnotation(a, BladeView.class);
if (bladeView != null && bladeView.value() != BladeView.Auto.class) {
return new Class<?>[]{bladeView.value()};
}
// 兼容:仍然识别标准 @JsonView
return super.findViews(a);
}
}
@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.jackson;
/**
* 视图自定义扩展接口
* <p>
* 用户可通过实现此接口来注册自定义视图层级,
* 注册为 Spring Bean 即可生效。
* </p>
*
* <h3>示例:新增超管以上的超级视图</h3>
* <pre>
* // 1. 定义自定义视图接口
* public interface SuperView extends Views.Administrator {}
*
* // 2. 注册到解析器
* &#64;Bean
* public BladeViewCustomizer myViewCustomizer() {
* return resolver -> resolver.registerView("super", SuperView.class, 4);
* }
*
* // 3. 在 YAML 中映射角色
* // blade.jackson.view.role-mapping.superadmin: super
* </pre>
*
* @author Chill
*/
@FunctionalInterface
public interface BladeViewCustomizer {
/**
* 自定义视图注册
*
* @param resolver 视图解析器
*/
void customize(BladeViewResolver resolver);
}
@@ -0,0 +1,124 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.jackson;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 视图解析器 — 视图名/角色名 → 视图 Class
* <p>
* 内置四级视图:summary(0) → detail(1) → admin(2) → administrator(3)
* 可通过 {@link BladeViewCustomizer} 注册自定义视图层级
* </p>
*
* @author Chill
*/
public class BladeViewResolver {
private final BladeJacksonProperties.View viewProperties;
/**
* 视图名称 → 视图 Class 映射(线程安全)
*/
private final Map<String, Class<?>> viewMapping = new ConcurrentHashMap<>();
/**
* 视图名称 → 优先级映射(数字越大优先级越高,线程安全)
*/
private final Map<String, Integer> priorityMapping = new ConcurrentHashMap<>();
public BladeViewResolver(BladeJacksonProperties.View viewProperties) {
this.viewProperties = viewProperties;
// 注册内置四级视图
registerView("summary", Views.Summary.class, 0);
registerView("list", Views.Summary.class, 0);
registerView("detail", Views.Detail.class, 1);
registerView("admin", Views.Admin.class, 2);
registerView("administrator", Views.Administrator.class, 3);
}
/**
* 注册自定义视图层级
*
* @param name 视图名称(不区分大小写)
* @param viewClass 视图 Class(建议继承 {@link Views} 中的接口)
* @param priority 优先级(数字越大权限越高,内置:summary=0, detail=1, admin=2, administrator=3
*/
public void registerView(String name, Class<?> viewClass, int priority) {
viewMapping.put(name.toLowerCase(), viewClass);
priorityMapping.put(name.toLowerCase(), priority);
}
/**
* 视图名称 → Class(未知名称降级到 Summary — 最小权限原则)
*
* @param viewName 视图名称
* @return 视图 Class
*/
public Class<?> resolve(String viewName) {
return viewMapping.getOrDefault(viewName.toLowerCase(), Views.Summary.class);
}
/**
* 根据角色名解析视图(支持逗号分隔多角色,取最高权限)
*
* @param roleName 角色名(可逗号分隔)
* @return 视图 Class
*/
public Class<?> resolveByRole(String roleName) {
if (roleName == null || roleName.isEmpty()) {
return resolve(viewProperties.getDefaultView());
}
Map<String, String> mapping = viewProperties.getRoleMapping();
String bestView = null;
int bestPriority = -1;
for (String role : roleName.split(",")) {
String viewName = mapping.get(role.trim().toLowerCase());
if (viewName != null) {
int priority = priorityMapping.getOrDefault(viewName.toLowerCase(), -1);
if (priority > bestPriority) {
bestPriority = priority;
bestView = viewName;
}
}
}
return resolve(bestView != null ? bestView : viewProperties.getDefaultView());
}
/**
* 获取默认视图
*
* @return 默认视图 Class
*/
public Class<?> getDefaultView() {
return resolve(viewProperties.getDefaultView());
}
}
@@ -0,0 +1,109 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.jackson;
import lombok.RequiredArgsConstructor;
import org.springblade.core.auto.annotation.AutoIgnore;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* {@link BladeView} 响应拦截器
* <p>
* 拦截 Controller 方法/类上的 {@link BladeView} 注解,
* 解析视图 Class 后包装为 {@link MappingJacksonValue} 交给 Jackson 处理。
* </p>
*
* @author Chill
*/
@AutoIgnore
@ControllerAdvice
@RequiredArgsConstructor
public class BladeViewResponseAdvice implements ResponseBodyAdvice<Object> {
private final BladeViewResolver viewResolver;
@Nullable
private final BladeRoleSupplier roleNameSupplier;
@Override
public boolean supports(@NonNull MethodParameter returnType,
@NonNull Class<? extends HttpMessageConverter<?>> converterType) {
return returnType.hasMethodAnnotation(BladeView.class)
|| returnType.getDeclaringClass().isAnnotationPresent(BladeView.class);
}
@Override
public Object beforeBodyWrite(@Nullable Object body,
@NonNull MethodParameter returnType,
@NonNull MediaType selectedContentType,
@NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
@NonNull ServerHttpRequest request,
@NonNull ServerHttpResponse response) {
if (body == null) {
return null;
}
// 方法级优先于类级
BladeView annotation = returnType.getMethodAnnotation(BladeView.class);
if (annotation == null) {
annotation = returnType.getDeclaringClass().getAnnotation(BladeView.class);
}
if (annotation == null) {
return body;
}
Class<?> viewClass = resolveViewClass(annotation);
MappingJacksonValue container;
if (body instanceof MappingJacksonValue) {
container = (MappingJacksonValue) body;
} else {
container = new MappingJacksonValue(body);
}
container.setSerializationView(viewClass);
return container;
}
private Class<?> resolveViewClass(BladeView annotation) {
if (annotation.value() != BladeView.Auto.class) {
return annotation.value();
}
if (roleNameSupplier != null) {
return viewResolver.resolveByRole(roleNameSupplier.get());
}
return viewResolver.getDefaultView();
}
}
@@ -0,0 +1,997 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.type.CollectionLikeType;
import com.fasterxml.jackson.databind.type.MapType;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.utils.*;
import org.springframework.lang.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.Serial;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.*;
/**
* Jackson工具类
*
* @author Chill
*/
@Slf4j
public class JsonUtil {
/**
* 将对象序列化成json字符串
*
* @param value javaBean
* @return jsonString json字符串
*/
public static <T> String toJson(T value) {
try {
return getInstance().writeValueAsString(value);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 将对象序列化成 json byte 数组
*
* @param object javaBean
* @return jsonString json字符串
*/
public static byte[] toJsonAsBytes(Object object) {
try {
return getInstance().writeValueAsBytes(object);
} catch (JsonProcessingException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param content content
* @param valueType class
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(String content, Class<T> valueType) {
try {
return getInstance().readValue(content, valueType);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 将json反序列化成对象
*
* @param content content
* @param typeReference 泛型类型
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(String content, TypeReference<T> typeReference) {
try {
return getInstance().readValue(content, typeReference);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json byte 数组反序列化成对象
*
* @param bytes json bytes
* @param valueType class
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(byte[] bytes, Class<T> valueType) {
try {
return getInstance().readValue(bytes, valueType);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param bytes bytes
* @param typeReference 泛型类型
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(byte[] bytes, TypeReference<T> typeReference) {
try {
return getInstance().readValue(bytes, typeReference);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param in InputStream
* @param valueType class
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(InputStream in, Class<T> valueType) {
try {
return getInstance().readValue(in, valueType);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param in InputStream
* @param typeReference 泛型类型
* @param <T> T 泛型标记
* @return Bean
*/
public static <T> T parse(InputStream in, TypeReference<T> typeReference) {
try {
return getInstance().readValue(in, typeReference);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成List对象
*
* @param content content
* @param valueTypeRef class
* @param <T> T 泛型标记
* @return List<T>
*/
public static <T> List<T> parseArray(String content, Class<T> valueTypeRef) {
try {
if (!StringUtil.startsWithIgnoreCase(content, StringPool.LEFT_SQ_BRACKET)) {
content = StringPool.LEFT_SQ_BRACKET + content + StringPool.RIGHT_SQ_BRACKET;
}
List<Map<String, Object>> list = getInstance().readValue(content, new TypeReference<List<Map<String, Object>>>() {
});
List<T> result = new ArrayList<>();
for (Map<String, Object> map : list) {
result.add(toPojo(map, valueTypeRef));
}
return result;
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 将json字符串转成 JsonNode
*
* @param jsonString jsonString
* @return jsonString json字符串
*/
public static JsonNode readTree(String jsonString) {
Objects.requireNonNull(jsonString, "jsonString is null");
try {
return getInstance().readTree(jsonString);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json字符串转成 JsonNode
*
* @param in InputStream
* @return jsonString json字符串
*/
public static JsonNode readTree(InputStream in) {
Objects.requireNonNull(in, "InputStream in is null");
try {
return getInstance().readTree(in);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json字符串转成 JsonNode
*
* @param content content
* @return jsonString json字符串
*/
public static JsonNode readTree(byte[] content) {
Objects.requireNonNull(content, "byte[] content is null");
try {
return getInstance().readTree(content);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json字符串转成 JsonNode
*
* @param jsonParser JsonParser
* @return jsonString json字符串
*/
public static JsonNode readTree(JsonParser jsonParser) {
Objects.requireNonNull(jsonParser, "jsonParser is null");
try {
return getInstance().readTree(jsonParser);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json byte 数组反序列化成对象
*
* @param content json bytes
* @param valueType class
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable byte[] content, Class<T> valueType) {
if (ObjectUtil.isEmpty(content)) {
return null;
}
try {
return getInstance().readValue(content, valueType);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param jsonString jsonString
* @param valueType class
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable String jsonString, Class<T> valueType) {
if (StringUtil.isBlank(jsonString)) {
return null;
}
try {
return getInstance().readValue(jsonString, valueType);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param in InputStream
* @param valueType class
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable InputStream in, Class<T> valueType) {
if (in == null) {
return null;
}
try {
return getInstance().readValue(in, valueType);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param content bytes
* @param typeReference 泛型类型
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable byte[] content, TypeReference<T> typeReference) {
if (ObjectUtil.isEmpty(content)) {
return null;
}
try {
return getInstance().readValue(content, typeReference);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param jsonString jsonString
* @param typeReference 泛型类型
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable String jsonString, TypeReference<T> typeReference) {
if (StringUtil.isBlank(jsonString)) {
return null;
}
try {
return getInstance().readValue(jsonString, typeReference);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param in InputStream
* @param typeReference 泛型类型
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable InputStream in, TypeReference<T> typeReference) {
if (in == null) {
return null;
}
try {
return getInstance().readValue(in, typeReference);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param content bytes
* @param javaType JavaType
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable byte[] content, JavaType javaType) {
if (content == null || content.length == 0) {
return null;
}
try {
return getInstance().readValue(content, javaType);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param jsonString jsonString
* @param javaType JavaType
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable String jsonString, JavaType javaType) {
if (StringUtil.isBlank(jsonString)) {
return null;
}
try {
return getInstance().readValue(jsonString, javaType);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将json反序列化成对象
*
* @param in InputStream
* @param javaType JavaType
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable InputStream in, JavaType javaType) {
if (in == null) {
return null;
}
try {
return getInstance().readValue(in, javaType);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将java.io.Reader反序列化成对象
*
* @param reader java.io.Reader
* @param javaType JavaType
* @param <T> T 泛型标记
* @return Bean
*/
@Nullable
public static <T> T readValue(@Nullable Reader reader, JavaType javaType) {
if (reader == null) {
return null;
}
try {
return getInstance().readValue(reader, javaType);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* clazz 获取 JavaType
*
* @param clazz Class
* @return MapType
*/
public static JavaType getType(Class<?> clazz) {
return getInstance().getTypeFactory().constructType(clazz);
}
/**
* 封装 map typekeyClass String
*
* @param valueClass value 类型
* @return MapType
*/
public static MapType getMapType(Class<?> valueClass) {
return getMapType(String.class, valueClass);
}
/**
* 封装 map type
*
* @param keyClass key 类型
* @param valueClass value 类型
* @return MapType
*/
public static MapType getMapType(Class<?> keyClass, Class<?> valueClass) {
return getInstance().getTypeFactory().constructMapType(Map.class, keyClass, valueClass);
}
/**
* 封装 map type
*
* @param elementClass 集合值类型
* @return CollectionLikeType
*/
public static CollectionLikeType getListType(Class<?> elementClass) {
return getInstance().getTypeFactory().constructCollectionLikeType(List.class, elementClass);
}
/**
* 读取集合
*
* @param content bytes
* @param elementClass elementClass
* @param <T> 泛型
* @return 集合
*/
public static <T> List<T> readList(@Nullable byte[] content, Class<T> elementClass) {
if (ObjectUtil.isEmpty(content)) {
return Collections.emptyList();
}
try {
return getInstance().readValue(content, getListType(elementClass));
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 读取集合
*
* @param content InputStream
* @param elementClass elementClass
* @param <T> 泛型
* @return 集合
*/
public static <T> List<T> readList(@Nullable InputStream content, Class<T> elementClass) {
if (content == null) {
return Collections.emptyList();
}
try {
return getInstance().readValue(content, getListType(elementClass));
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 读取集合
*
* @param content bytes
* @param elementClass elementClass
* @param <T> 泛型
* @return 集合
*/
public static <T> List<T> readList(@Nullable String content, Class<T> elementClass) {
if (ObjectUtil.isEmpty(content)) {
return Collections.emptyList();
}
try {
return getInstance().readValue(content, getListType(elementClass));
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 读取集合
*
* @param content InputStream
* @param valueClass 值类型
* @param <V> 泛型
* @return 集合
*/
public static <V> Map<String, V> readMap(@Nullable InputStream content, Class<?> valueClass) {
return readMap(content, String.class, valueClass);
}
/**
* 读取集合
*
* @param reader java.io.Reader
* @param valueClass 值类型
* @param <V> 泛型
* @return 集合
*/
public static <V> Map<String, V> readMap(@Nullable Reader reader, Class<?> valueClass) {
return readMap(reader, String.class, valueClass);
}
/**
* 读取集合
*
* @param content bytes
* @param valueClass 值类型
* @param <V> 泛型
* @return 集合
*/
public static <V> Map<String, V> readMap(@Nullable String content, Class<?> valueClass) {
return readMap(content, String.class, valueClass);
}
/**
* 读取集合
*
* @param content bytes
* @param keyClass key类型
* @param valueClass 值类型
* @param <K> 泛型
* @param <V> 泛型
* @return 集合
*/
public static <K, V> Map<K, V> readMap(@Nullable byte[] content, Class<?> keyClass, Class<?> valueClass) {
if (ObjectUtil.isEmpty(content)) {
return Collections.emptyMap();
}
try {
return getInstance().readValue(content, getMapType(keyClass, valueClass));
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 读取集合
*
* @param content InputStream
* @param keyClass key类型
* @param valueClass 值类型
* @param <K> 泛型
* @param <V> 泛型
* @return 集合
*/
public static <K, V> Map<K, V> readMap(@Nullable InputStream content, Class<?> keyClass, Class<?> valueClass) {
if (ObjectUtil.isEmpty(content)) {
return Collections.emptyMap();
}
try {
return getInstance().readValue(content, getMapType(keyClass, valueClass));
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 读取集合
*
* @param reader java.io.Reader
* @param keyClass key类型
* @param valueClass 值类型
* @param <K> 泛型
* @param <V> 泛型
* @return 集合
*/
public static <K, V> Map<K, V> readMap(@Nullable Reader reader, Class<?> keyClass, Class<?> valueClass) {
if (reader == null) {
return Collections.emptyMap();
}
try {
return getInstance().readValue(reader, getMapType(keyClass, valueClass));
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 读取集合
*
* @param content bytes
* @param keyClass key类型
* @param valueClass 值类型
* @param <K> 泛型
* @param <V> 泛型
* @return 集合
*/
public static <K, V> Map<K, V> readMap(@Nullable String content, Class<?> keyClass, Class<?> valueClass) {
if (ObjectUtil.isEmpty(content)) {
return Collections.emptyMap();
}
try {
return getInstance().readValue(content, getMapType(keyClass, valueClass));
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 读取集合
*
* @param content bytes
* @return 集合
*/
public static Map<String, Object> readMap(@Nullable byte[] content) {
return readMap(content, Object.class);
}
/**
* 读取集合
*
* @param content bytes
* @return 集合
*/
public static Map<String, Object> readMap(@Nullable String content) {
return readMap(content, String.class, Object.class);
}
/**
* 读取集合
*
* @param content InputStream
* @return 集合
*/
public static Map<String, Object> readMap(@Nullable InputStream content) {
return readMap(content, Object.class);
}
/**
* 读取集合
*
* @param reader java.io.Reader
* @return 集合
*/
public static Map<String, Object> readMap(@Nullable Reader reader) {
return readMap(reader, Object.class);
}
/**
* 读取集合
*
* @param content bytes
* @param valueClass 值类型
* @param <V> 泛型
* @return 集合
*/
public static <V> Map<String, V> readMap(@Nullable byte[] content, Class<?> valueClass) {
return readMap(content, String.class, valueClass);
}
/**
* 读取集合
*
* @param content bytes
* @return 集合
*/
public static List<Map<String, Object>> readListMap(@Nullable String content) {
if (ObjectUtil.isEmpty(content)) {
return Collections.emptyList();
}
try {
return getInstance().readValue(content, getListType(Map.class));
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 封装参数化类型
*
* <p>
* 例如: Map.class, String.class, String.class 对应 Map[String, String]
* </p>
*
* @param parametrized 泛型参数化
* @param parameterClasses 泛型参数类型
* @return JavaType
*/
public static JavaType getParametricType(Class<?> parametrized, Class<?>... parameterClasses) {
return getInstance().getTypeFactory().constructParametricType(parametrized, parameterClasses);
}
/**
* 封装参数化类型,用来构造复杂的泛型
*
* <p>
* 例如: Map.class, String.class, String.class 对应 Map[String, String]
* </p>
*
* @param parametrized 泛型参数化
* @param parameterTypes 泛型参数类型
* @return JavaType
*/
public static JavaType getParametricType(Class<?> parametrized, JavaType... parameterTypes) {
return getInstance().getTypeFactory().constructParametricType(parametrized, parameterTypes);
}
/**
* jackson 的类型转换
*
* @param fromValue 来源对象
* @param toValueType 转换的类型
* @param <T> 泛型标记
* @return 转换结果
*/
public static <T> T convertValue(Object fromValue, Class<T> toValueType) {
return getInstance().convertValue(fromValue, toValueType);
}
/**
* jackson 的类型转换
*
* @param fromValue 来源对象
* @param toValueType 转换的类型
* @param <T> 泛型标记
* @return 转换结果
*/
public static <T> T convertValue(Object fromValue, JavaType toValueType) {
return getInstance().convertValue(fromValue, toValueType);
}
/**
* jackson 的类型转换
*
* @param fromValue 来源对象
* @param toValueTypeRef 泛型类型
* @param <T> 泛型标记
* @return 转换结果
*/
public static <T> T convertValue(Object fromValue, TypeReference<T> toValueTypeRef) {
return getInstance().convertValue(fromValue, toValueTypeRef);
}
/**
* tree 转对象
*
* @param treeNode TreeNode
* @param valueType valueType
* @param <T> 泛型标记
* @return 转换结果
*/
public static <T> T treeToValue(TreeNode treeNode, Class<T> valueType) {
try {
return getInstance().treeToValue(treeNode, valueType);
} catch (JsonProcessingException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 对象转为 json node
*
* @param value 对象
* @return JsonNode
*/
public static JsonNode valueToTree(@Nullable Object value) {
return getInstance().valueToTree(value);
}
/**
* 判断是否可以序列化
*
* @param value 对象
* @return 是否可以序列化
*/
public static boolean canSerialize(@Nullable Object value) {
if (value == null) {
return true;
}
return getInstance().canSerialize(value.getClass());
}
/**
* 将对象转成map形式
*
* @param content json字符串
* @return {Map}
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> toMap(String content) {
try {
return getInstance().readValue(content, Map.class);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return null;
}
/**
* 将对象转成map形式
*
* @param bean 源对象
* @return {Map}
*/
public static Map<String, Object> toMap(Object bean) {
return JsonUtil.convertValue(bean, JsonUtil.getMapType(Object.class));
}
/**
* 将对象转成map形式
*
* @param content json字符串
* @param valueTypeRef 泛型类型
* @param <T> 泛型
* @return {Map}
*/
public static <T> Map<String, T> toMap(String content, Class<T> valueTypeRef) {
try {
Map<String, Map<String, Object>> map = getInstance().readValue(content, new TypeReference<Map<String, Map<String, Object>>>() {
});
Map<String, T> result = new HashMap<>(16);
for (Map.Entry<String, Map<String, Object>> entry : map.entrySet()) {
result.put(entry.getKey(), toPojo(entry.getValue(), valueTypeRef));
}
return result;
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return null;
}
public static <T> T toPojo(Map fromValue, Class<T> toValueType) {
return getInstance().convertValue(fromValue, toValueType);
}
// --------------------------------- objectMapper method start -----------------------------------------
public static ObjectMapper getInstance() {
return JacksonHolder.INSTANCE;
}
public static ObjectMapper getInstance(BladeJacksonProperties properties) {
return JacksonPropertyHolder.INSTANCE(properties);
}
private static class JacksonHolder {
private static final ObjectMapper INSTANCE = new JacksonObjectMapper();
}
private static class JacksonPropertyHolder {
private static ObjectMapper INSTANCE(BladeJacksonProperties properties) {
ObjectMapper objectMapper = new JacksonObjectMapper();
if (properties != null) {
//大数字 转 字符串
if (Boolean.TRUE.equals(properties.getBigNumToString())) {
objectMapper.registerModules(BladeNumberModule.INSTANCE);
}
//null 处理
if (Boolean.TRUE.equals(properties.getNullToEmpty())) {
objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new BladeBeanSerializerModifier()));
objectMapper.getSerializerProvider().setNullValueSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
}
// 敏感词处理
objectMapper.registerModule(BladeSensitiveModule.INSTANCE);
}
objectMapper.findAndRegisterModules();
return objectMapper;
}
}
private static class JacksonObjectMapper extends ObjectMapper {
@Serial
private static final long serialVersionUID = 4288193147502386170L;
private static final Locale CHINA = Locale.CHINA;
public JacksonObjectMapper(ObjectMapper src) {
super(src);
}
public JacksonObjectMapper() {
// 通过 Builder 设置 Feature
super(JsonMapper.builder()
.enable(MapperFeature.DEFAULT_VIEW_INCLUSION) //默认视图包含
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS) //允许未转义的控制字符
.enable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER) //允许反斜杠转义任意字符
.enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) //允许单引号
.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT) //允许空字符串反序列化为null对象
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) //日期不序列化为时间戳
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) //序列化空对象不报错
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) //反序列化遇到未知属性不报错
.build());
//设置地点为中国
super.setLocale(CHINA);
//设置为中国上海时区
super.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
//序列化时,日期的统一格式
super.setDateFormat(new SimpleDateFormat(DateUtil.PATTERN_DATETIME, Locale.CHINA));
//日期格式化
super.registerModule(BladeJavaTimeModule.INSTANCE);
//视图注解内省器,让 Jackson 识别 @BladeView
super.setAnnotationIntrospector(new BladeViewAnnotationIntrospector());
//注册模块
super.findAndRegisterModules();
}
@Override
public ObjectMapper copy() {
return new JacksonObjectMapper(this);
}
}
// --------------------------------- objectMapper method end -----------------------------------------
}
@@ -0,0 +1,135 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springblade.core.tool.utils.Charsets;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* 针对 api 服务对 pc、android、ios 处理的 分读写的 jackson 处理
*
* <p>
* 1. app 端上报数据是 使用 readObjectMapper
* 2. 返回给 app 端的数据使用 writeObjectMapper
* 3. 如果是返回字符串,直接响应,不做 json 处理
* </p>
*
* @author L.cm
*/
public class MappingApiJackson2HttpMessageConverter extends AbstractReadWriteJackson2HttpMessageConverter {
@Nullable
private String jsonPrefix;
public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper, BladeJacksonProperties properties) {
super(objectMapper, initWriteObjectMapper(objectMapper, properties), initMediaType(properties));
}
private static List<MediaType> initMediaType(BladeJacksonProperties properties) {
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(new MediaType("application", "*+json"));
// 支持 text 文本,用于报文签名
if (Boolean.TRUE.equals(properties.getSupportTextPlain())) {
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
}
return supportedMediaTypes;
}
private static ObjectMapper initWriteObjectMapper(ObjectMapper readObjectMapper, BladeJacksonProperties properties) {
// 拷贝 readObjectMapper
ObjectMapper writeObjectMapper = readObjectMapper.copy();
// 敏感词处理
writeObjectMapper.registerModule(BladeSensitiveModule.INSTANCE);
// 大数字 转 字符串
if (Boolean.TRUE.equals(properties.getBigNumToString())) {
writeObjectMapper.registerModules(BladeNumberModule.INSTANCE);
}
// null 处理
if (Boolean.TRUE.equals(properties.getNullToEmpty())) {
writeObjectMapper.setSerializerFactory(writeObjectMapper.getSerializerFactory().withSerializerModifier(new BladeBeanSerializerModifier()));
writeObjectMapper.getSerializerProvider().setNullValueSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
}
return writeObjectMapper;
}
@Override
protected void writeInternal(@NonNull Object object, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// 如果是字符串,直接写出
if (object instanceof String) {
Charset defaultCharset = this.getDefaultCharset();
Charset charset = defaultCharset == null ? Charsets.UTF_8 : defaultCharset;
StreamUtils.copy((String) object, charset, outputMessage.getBody());
} else {
super.writeInternal(object, type, outputMessage);
}
}
/**
* Specify a custom prefix to use for this view's JSON output.
* Default is none.
*
* @param jsonPrefix jsonPrefix
* @see #setPrefixJson
*/
public void setJsonPrefix(@Nullable String jsonPrefix) {
this.jsonPrefix = jsonPrefix;
}
/**
* Indicate whether the JSON output by this view should be prefixed with ")]}', ". Default is false.
* <p>Prefixing the JSON string in this manner is used to help prevent JSON Hijacking.
* The prefix renders the string syntactically invalid as a script so that it cannot be hijacked.
* This prefix should be stripped before parsing the string as JSON.
*
* @param prefixJson prefixJson
* @see #setJsonPrefix
*/
public void setPrefixJson(boolean prefixJson) {
this.jsonPrefix = (prefixJson ? ")]}', " : null);
}
@Override
protected void writePrefix(@NonNull JsonGenerator generator, @NonNull Object object) throws IOException {
if (this.jsonPrefix != null) {
generator.writeRaw(this.jsonPrefix);
}
}
}
@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.annotation.JacksonAnnotation;
import org.springblade.core.tool.sensitive.SensitiveType;
import org.springblade.core.tool.sensitive.SensitiveUtil;
import org.springblade.core.tool.sensitive.SensitiveWord;
import org.springblade.core.tool.utils.StringPool;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 敏感信息处理注解类
*
* @author BladeX
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface Sensitive {
// 敏感词类型处理
SensitiveType type() default SensitiveType.NONE;
// 敏感词匹配处理
SensitiveWord word() default SensitiveWord.NONE;
// 自定义敏感词组
String[] words() default {};
// 自定义正则匹配
String regex() default StringPool.EMPTY;
// 自定义替换符
String replacement() default SensitiveUtil.DEFAULT_REPLACEMENT;
}
@@ -0,0 +1,99 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.jackson;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import org.springblade.core.tool.sensitive.SensitiveType;
import org.springblade.core.tool.sensitive.SensitiveUtil;
import org.springblade.core.tool.sensitive.SensitiveWord;
import org.springblade.core.tool.utils.StringUtil;
import java.io.IOException;
import java.util.Arrays;
/**
* 敏感信息序列化
*
* @author BladeX
*/
@JacksonStdImpl
public class SensitiveSerializer extends JsonSerializer<String> implements ContextualSerializer {
public final static SensitiveSerializer instance = new SensitiveSerializer();
private Sensitive sensitive;
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
if (StringUtil.isBlank(value)) {
gen.writeString(value);
return;
}
if (sensitive == null) {
gen.writeString(value);
return;
}
// 根据注解配置处理脱敏
if (sensitive.type() != SensitiveType.NONE) {
// 类型脱敏
gen.writeString(SensitiveUtil.process(value, sensitive.type()));
} else if (sensitive.word() != SensitiveWord.NONE) {
// 敏感词脱敏
gen.writeString(SensitiveUtil.processWithWords(value, sensitive.word().getWords(), sensitive.replacement(), true));
} else if (sensitive.words().length > 0) {
// 自定义敏感词脱敏
gen.writeString(SensitiveUtil.processWithWords(value, Arrays.asList(sensitive.words()), sensitive.replacement(), true));
} else if (StringUtil.isNotBlank(sensitive.regex())) {
// 正则脱敏
gen.writeString(SensitiveUtil.processWithRegex(value, sensitive.regex(), sensitive.replacement()));
} else {
// 默认值
gen.writeString(value);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
JsonSerializer<?> valueSerializer = prov.findValueSerializer(String.class);
if (property == null) {
return valueSerializer;
}
Sensitive sensitive = property.getAnnotation(Sensitive.class);
if (sensitive != null) {
SensitiveSerializer serializer = new SensitiveSerializer();
serializer.sensitive = sensitive;
return serializer;
}
return valueSerializer;
}
}
@@ -0,0 +1,67 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.jackson;
/**
* BladeX 全局视图层级定义
* <p>
* 四级视图:Summary → Detail → Admin → Administrator
* 继承即包含:高级视图自动包含所有低级视图的字段
* 未标注 {@link BladeView} 的字段在任何视图下始终输出
* </p>
*
* @author Chill
*/
public final class Views {
private Views() {
}
/**
* 摘要视图 — 列表、下拉、搜索
*/
public interface Summary {
}
/**
* 详情视图 — 详情页、个人中心
*/
public interface Detail extends Summary {
}
/**
* 管理视图 — 普通管理员(admin)
*/
public interface Admin extends Detail {
}
/**
* 超管视图 — 超级管理员(administrator)
*/
public interface Administrator extends Admin {
}
}
@@ -0,0 +1,86 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.node;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import java.io.Serial;
import java.util.ArrayList;
import java.util.List;
/**
* 节点基类
*
* @author smallchill
*/
@Data
public class BaseNode<T> implements INode<T> {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@JsonSerialize(using = ToStringSerializer.class)
protected Long id;
/**
* 父节点ID
*/
@JsonSerialize(using = ToStringSerializer.class)
protected Long parentId;
/**
* 子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
protected List<T> children = new ArrayList<T>();
/**
* 是否有子孙节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Boolean hasChildren;
/**
* 是否有子孙节点
*
* @return Boolean
*/
@Override
public Boolean getHasChildren() {
if (children.size() > 0) {
return true;
} else {
return this.hasChildren;
}
}
}
@@ -0,0 +1,57 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.node;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
/**
* 森林节点类
*
* @author smallchill
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ForestNode extends BaseNode<ForestNode> {
@Serial
private static final long serialVersionUID = 1L;
/**
* 节点内容
*/
private Object content;
public ForestNode(Long id, Long parentId, Object content) {
this.id = id;
this.parentId = parentId;
this.content = content;
}
}
@@ -0,0 +1,94 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.node;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.springblade.core.tool.utils.StringPool;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 森林管理类
*
* @author smallchill
*/
public class ForestNodeManager<T extends INode<T>> {
/**
* 森林的所有节点
*/
private final ImmutableMap<Long, T> nodeMap;
/**
* 森林的父节点ID
*/
private final Map<Long, Object> parentIdMap = Maps.newHashMap();
public ForestNodeManager(List<T> nodes) {
nodeMap = Maps.uniqueIndex(nodes, INode::getId);
}
/**
* 根据节点ID获取一个节点
*
* @param id 节点ID
* @return 对应的节点对象
*/
public INode<T> getTreeNodeAt(Long id) {
if (nodeMap.containsKey(id)) {
return nodeMap.get(id);
}
return null;
}
/**
* 增加父节点ID
*
* @param parentId 父节点ID
*/
public void addParentId(Long parentId) {
parentIdMap.put(parentId, StringPool.EMPTY);
}
/**
* 获取树的根节点(一个森林对应多颗树)
*
* @return 树的根节点集合
*/
public List<T> getRoot() {
List<T> roots = new ArrayList<>();
nodeMap.forEach((key, node) -> {
if (node.getParentId() == 0 || parentIdMap.containsKey(node.getId())) {
roots.add(node);
}
});
return roots;
}
}
@@ -0,0 +1,59 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.node;
import java.util.List;
/**
* 森林节点归并类
*
* @author smallchill
*/
public class ForestNodeMerger {
/**
* 将节点数组归并为一个森林多棵树填充节点的children域
* 时间复杂度为O(n^2)
*
* @param items 节点域
* @return 多棵树的根节点集合
*/
public static <T extends INode<T>> List<T> merge(List<T> items) {
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
items.forEach(forestNode -> {
if (forestNode.getParentId() != 0) {
INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());
if (node != null) {
node.getChildren().add(forestNode);
} else {
forestNodeManager.addParentId(forestNode.getId());
}
}
});
return forestNodeManager.getRoot();
}
}
@@ -0,0 +1,68 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.node;
import java.io.Serializable;
import java.util.List;
/**
* Created by Blade.
*
* @author smallchill
*/
public interface INode<T> extends Serializable {
/**
* 主键
*
* @return Long
*/
Long getId();
/**
* 父主键
*
* @return Long
*/
Long getParentId();
/**
* 子孙节点
*
* @return List<T>
*/
List<T> getChildren();
/**
* 是否有子孙节点
*
* @return Boolean
*/
default Boolean getHasChildren() {
return false;
}
}
@@ -0,0 +1,33 @@
package org.springblade.core.tool.node;
import org.springblade.core.tool.jackson.JsonUtil;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Blade.
*
* @author smallchill
*/
public class NodeTest {
public static void main(String[] args) {
List<ForestNode> list = new ArrayList<>();
list.add(new ForestNode(1L, 0L, "1"));
list.add(new ForestNode(2L, 0L, "2"));
list.add(new ForestNode(3L, 1L, "3"));
list.add(new ForestNode(4L, 2L, "4"));
list.add(new ForestNode(5L, 3L, "5"));
list.add(new ForestNode(6L, 4L, "6"));
list.add(new ForestNode(7L, 3L, "7"));
list.add(new ForestNode(8L, 5L, "8"));
list.add(new ForestNode(9L, 6L, "9"));
list.add(new ForestNode(10L, 9L, "10"));
List<ForestNode> tns = ForestNodeMerger.merge(list);
tns.forEach(node ->
System.out.println(JsonUtil.toJson(node))
);
}
}
@@ -0,0 +1,72 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.node;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import org.springblade.core.tool.utils.Func;
import java.io.Serial;
import java.util.Objects;
/**
* 树型节点类
*
* @author smallchill
*/
@Data
public class TreeNode extends BaseNode<TreeNode> {
@Serial
private static final long serialVersionUID = 1L;
private String title;
@JsonSerialize(using = ToStringSerializer.class)
private Long key;
@JsonSerialize(using = ToStringSerializer.class)
private Long value;
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
TreeNode other = (TreeNode) obj;
return Func.equals(this.getId(), other.getId());
}
@Override
public int hashCode() {
return Objects.hash(id, parentId);
}
}
@@ -0,0 +1,59 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.sensitive;
import lombok.Builder;
import lombok.Data;
import java.util.*;
import java.util.regex.Pattern;
/**
* 敏感信息处理配置类
*
* @author BladeX
*/
@Builder
@Data
public class SensitiveConfig {
// 启用的内置正则脱敏类型
private Set<SensitiveType> sensitiveTypes;
// 启用的内置敏感词分组
private Set<SensitiveWord> sensitiveWords;
// 自定义敏感词列表
private List<String> customSensitiveWords;
// 自定义正则表达式脱敏规则
private Map<String, Pattern> customPatterns;
// 自定义替换文本可选有默认值
private String replacement;
// 是否按行处理
private boolean processLineByLine;
}
@@ -0,0 +1,81 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.sensitive;
import java.util.regex.Pattern;
/**
* 脱敏类型枚举.
*
* @author BladeX
*/
public enum SensitiveType {
// 空敏感类型
NONE("", "", ""),
// 通用信息类
GLOBAL("全局", "(.{2}).*(.{2})", "$1****$2"),
KEYS("密钥", "(.{3}).*(.{3})", "$1************$2"),
// 身份信息类
MOBILE("手机号", "(\\d{3})\\d{4}(\\d{4})", "$1****$2"),
EMAIL("电子邮箱", "(\\w{2})\\w+(@\\w+\\.\\w+)", "$1****$2"),
ID_CARD("身份证号", "(\\d{4})\\d{10}(\\w{4})", "$1**********$2"),
PASSPORT("护照号", "([A-Z]{1})\\d{7}", "$1*******"),
// 金融信息类
BANK_CARD("银行卡号", "(\\d{4})\\d+(\\d{4})", "$1****$2"),
CREDIT_CARD("信用卡号", "(\\d{4})\\d+(\\d{4})", "$1****$2"),
// 账户信息类
USERNAME("用户名", "(\\w{1})\\w+(\\w{1})", "$1****$2"),
IP_ADDRESS("IP地址", "(\\d{1,3}\\.\\d{1,3})\\.\\d{1,3}\\.\\d{1,3}", "$1.***.***"),
MAC_ADDRESS("MAC地址", "([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}):[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}", "$1:****"),
// 地址信息类
ADDRESS("详细地址", "(.{3}).*(.{3})", "$1****$2"),
GPS("GPS坐标", "(\\d+\\.\\d{2})\\d+,(\\d+\\.\\d{2})\\d+", "$1***,$2***"),
;
private final String replacement;
private final Pattern pattern;
SensitiveType(String ignore, String regex, String replacement) {
this.replacement = replacement;
this.pattern = Pattern.compile(regex);
}
/**
* 替换文本
*
* @param content content
* @return 替换后的内容
*/
public String replaceAll(String content) {
return this.pattern.matcher(content).replaceAll(this.replacement);
}
}
@@ -0,0 +1,437 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.sensitive;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.StringUtil;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* 敏感信息处理工具类
* <p>
* 支持以下功能
* 1. 内置敏感类型处理手机号邮箱身份证等
* 2. 自定义正则表达式处理
* 3. 敏感词处理
* 4. 支持按行处理或整体处理
* 5. 支持自定义替换符
* 6. 支持泛型返回值
* </p>
*
* @author BladeX
*/
public class SensitiveUtil {
// 默认替换符
public static final String DEFAULT_REPLACEMENT = "******";
// 换行符
private static final String LINE_SEPARATOR = System.lineSeparator();
// 预编译的默认配置
private static final SensitiveConfig DEFAULT_CONFIG = SensitiveConfig.builder()
.sensitiveTypes(EnumSet.of(
SensitiveType.MOBILE,
SensitiveType.ID_CARD,
SensitiveType.EMAIL,
SensitiveType.BANK_CARD,
SensitiveType.CREDIT_CARD,
SensitiveType.IP_ADDRESS,
SensitiveType.MAC_ADDRESS
))
.sensitiveWords(EnumSet.of(
SensitiveWord.SECURE,
SensitiveWord.PAYMENT,
SensitiveWord.AUTHENTICATION,
SensitiveWord.SESSION
))
.processLineByLine(true)
.replacement(DEFAULT_REPLACEMENT)
.build();
/**
* 使用默认配置处理敏感信息
*
* @param content 待处理内容
* @return 处理后的结果
*/
public static String process(String content) {
return process(content, Function.identity());
}
/**
* 使用默认配置处理敏感信息
*
* @param content 待处理内容
* @param supplier 结果转换函数
* @param <T> 返回值类型
* @return 处理后的结果
*/
public static <T> T process(String content, Function<String, T> supplier) {
return process(content, DEFAULT_CONFIG, supplier);
}
/**
* 使用自定义配置处理敏感信息
*
* @param content 待处理内容
* @param config 自定义配置
* @return 处理后的结果
*/
public static String process(String content, SensitiveConfig config) {
return process(content, config, Function.identity());
}
/**
* 使用自定义配置处理敏感信息
*
* @param content 待处理内容
* @param config 自定义配置
* @param supplier 结果转换函数
* @param <T> 返回值类型
* @return 处理后的结果
*/
public static <T> T process(String content, SensitiveConfig config, Function<String, T> supplier) {
if (StringUtil.isBlank(content)) {
return supplier.apply(content);
}
String processedContent = content;
String replacement = StringUtil.isBlank(config.getReplacement()) ?
DEFAULT_REPLACEMENT : config.getReplacement();
// 1. 处理内置敏感类型
if (!CollectionUtil.isEmpty(config.getSensitiveTypes())) {
processedContent = processRegexPatterns(processedContent, config.getSensitiveTypes());
}
// 2. 处理自定义正则
if (!CollectionUtil.isEmpty(config.getCustomPatterns())) {
processedContent = processCustomPatterns(processedContent,
config.getCustomPatterns(),
replacement
);
}
// 3. 处理敏感词
if (!CollectionUtil.isEmpty(config.getSensitiveWords())) {
List<String> words = getSensitiveWords(config.getSensitiveWords());
processedContent = processSensitiveWords(processedContent,
words,
replacement,
config.isProcessLineByLine()
);
}
return supplier.apply(processedContent);
}
/**
* 处理单个敏感类型
*
* @param content 待处理内容
* @param type 敏感类型
* @return 处理后的结果
*/
public static String process(String content, SensitiveType type) {
return process(content, type, Function.identity());
}
/**
* 处理单个敏感类型
*
* @param content 待处理内容
* @param type 敏感类型
* @param supplier 结果转换函数
* @param <T> 返回值类型
* @return 处理后的结果
*/
public static <T> T process(String content, SensitiveType type, Function<String, T> supplier) {
if (StringUtil.isBlank(content) || type == null) {
return supplier.apply(content);
}
String processed = processRegexPatterns(content, Collections.singleton(type));
return supplier.apply(processed);
}
/**
* 处理多个敏感类型
*
* @param content 待处理内容
* @param types 敏感类型集合
* @return 处理后的结果
*/
public static String process(String content, Set<SensitiveType> types) {
return process(content, types, Function.identity());
}
/**
* 处理多个敏感类型
*
* @param content 待处理内容
* @param types 敏感类型集合
* @param supplier 结果转换函数
* @param <T> 返回值类型
* @return 处理后的结果
*/
public static <T> T process(String content, Set<SensitiveType> types, Function<String, T> supplier) {
if (StringUtil.isBlank(content) || CollectionUtil.isEmpty(types)) {
return supplier.apply(content);
}
String processed = processRegexPatterns(content, types);
return supplier.apply(processed);
}
/**
* 使用自定义正则处理使用默认替换符
*
* @param content 待处理内容
* @param regex 正则表达式
* @return 处理后的结果
*/
public static String processWithRegex(String content, String regex) {
return processWithRegex(content, regex, Function.identity());
}
/**
* 使用自定义正则处理使用默认替换符
*
* @param content 待处理内容
* @param regex 正则表达式
* @param supplier 结果转换函数
* @param <T> 返回值类型
* @return 处理后的结果
*/
public static <T> T processWithRegex(String content, String regex, Function<String, T> supplier) {
return processWithRegex(content, regex, DEFAULT_REPLACEMENT, supplier);
}
/**
* 使用自定义正则处理使用自定义替换符
*
* @param content 待处理内容
* @param regex 正则表达式
* @param replacement 替换内容
* @return 处理后的结果
*/
public static String processWithRegex(String content, String regex, String replacement) {
return processWithRegex(content, regex, replacement, Function.identity());
}
/**
* 使用自定义正则处理使用自定义替换符
*
* @param content 待处理内容
* @param regex 正则表达式
* @param replacement 替换内容
* @param supplier 结果转换函数
* @param <T> 返回值类型
* @return 处理后的结果
*/
public static <T> T processWithRegex(String content, String regex, String replacement, Function<String, T> supplier) {
if (StringUtil.isBlank(content) || StringUtil.isBlank(regex)) {
return supplier.apply(content);
}
Pattern pattern = Pattern.compile(regex);
String processed = pattern.matcher(content).replaceAll(replacement);
return supplier.apply(processed);
}
/**
* 处理敏感词使用默认配置
*
* @param content 待处理内容
* @param words 敏感词列表
* @return 处理后的结果
*/
public static String processWithWords(String content, List<String> words) {
return processWithWords(content, words, Function.identity());
}
/**
* 处理敏感词使用默认配置
*
* @param content 待处理内容
* @param words 敏感词列表
* @param supplier 结果转换函数
* @param <T> 返回值类型
* @return 处理后的结果
*/
public static <T> T processWithWords(String content, List<String> words, Function<String, T> supplier) {
return processWithWords(content, words, DEFAULT_REPLACEMENT, true, supplier);
}
/**
* 处理敏感词使用完整参数
*
* @param content 待处理内容
* @param words 敏感词列表
* @param replacement 替换符
* @param processLineByLine 是否按行处理
* @return 处理后的结果
*/
public static String processWithWords(String content,
List<String> words,
String replacement,
boolean processLineByLine) {
return processWithWords(content, words, replacement, processLineByLine, Function.identity());
}
/**
* 处理敏感词使用完整参数
*
* @param content 待处理内容
* @param words 敏感词列表
* @param replacement 替换符
* @param processLineByLine 是否按行处理
* @param supplier 结果转换函数
* @param <T> 返回值类型
* @return 处理后的结果
*/
public static <T> T processWithWords(String content,
List<String> words,
String replacement,
boolean processLineByLine,
Function<String, T> supplier) {
if (StringUtil.isBlank(content) || CollectionUtil.isEmpty(words)) {
return supplier.apply(content);
}
String processed = processSensitiveWords(content, words, replacement, processLineByLine);
return supplier.apply(processed);
}
/**
* 处理正则表达式
*
* @param content 待处理内容
* @param types 敏感类型集合
* @return 处理后的结果
*/
private static String processRegexPatterns(String content, Set<SensitiveType> types) {
String result = content;
for (SensitiveType type : types) {
result = type.replaceAll(result);
}
return result;
}
/**
* 处理自定义正则表达式
*
* @param content 待处理内容
* @param patterns 自定义正则表达式
* @param replacement 替换符
* @return 处理后的结果
*/
private static String processCustomPatterns(String content,
Map<String, Pattern> patterns,
String replacement) {
String result = content;
for (Pattern pattern : patterns.values()) {
result = pattern.matcher(result).replaceAll(replacement);
}
return result;
}
/**
* 获取敏感词列表
*
* @param groups 敏感词分组
* @return 敏感词列表
*/
private static List<String> getSensitiveWords(Set<SensitiveWord> groups) {
List<String> words = new ArrayList<>();
for (SensitiveWord group : groups) {
words.addAll(group.getWords());
}
return words;
}
/**
* 处理敏感词
*
* @param content 待处理内容
* @param words 敏感词列表
* @param replacement 替换符
* @param processLineByLine 是否按行处理
* @return 处理后的结果
*/
private static String processSensitiveWords(String content,
List<String> words,
String replacement,
boolean processLineByLine) {
return processLineByLine ?
maskSensitiveLines(content, words, replacement) :
maskSensitiveContent(content, words, replacement);
}
/**
* 按行处理敏感词
*
* @param content 待处理内容
* @param words 敏感词列表
* @param replacement 替换符
* @return 处理后的结果
*/
private static String maskSensitiveLines(String content,
List<String> words,
String replacement) {
String[] lines = content.split(LINE_SEPARATOR);
StringBuilder result = new StringBuilder();
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
boolean containsSensitive = words.stream()
.anyMatch(word -> line.toLowerCase().contains(word.toLowerCase()));
result.append(containsSensitive ? replacement : line);
if (i < lines.length - 1) {
result.append(LINE_SEPARATOR);
}
}
return result.toString();
}
/**
* 处理整体内容中的敏感词
*
* @param content 待处理内容
* @param words 敏感词列表
* @param replacement 替换符
* @return 处理后的结果
*/
private static String maskSensitiveContent(String content,
List<String> words,
String replacement) {
boolean containsSensitive = words.stream()
.anyMatch(word -> content.toLowerCase().contains(word.toLowerCase()));
return containsSensitive ? replacement : content;
}
}
@@ -0,0 +1,79 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.sensitive;
import lombok.Getter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 敏感词分组枚举.
*
* @author BladeX
*/
@Getter
public enum SensitiveWord {
// 空敏感词组
NONE(List.of()),
// 安全敏感词组
SECURE(Arrays.asList(
// 认证信息类
"password", "pwd", "token", "secret", "bearer", "key",
// API相关
"api_key", "access_token", "refresh_token", "auth_token",
// 加密相关
"private_key", "public_key", "salt", "hash",
// 安全相关
"security", "certificate", "credentials",
// 数据库相关
"connection_string", "jdbc", "sql", "database_url"
)),
// 支付相关敏感词
PAYMENT(Arrays.asList(
"cvv", "card_number", "expiry", "pin", "payment_token"
)),
// 身份验证相关敏感词
AUTHENTICATION(Arrays.asList(
"otp", "verification_code", "auth_code", "mfa_token"
)),
// 会话相关敏感词
SESSION(Arrays.asList(
"session_id", "cookie", "jwt_token", "bearer_token"
));
private final List<String> words;
SensitiveWord(List<String> words) {
this.words = Collections.unmodifiableList(words);
}
}
@@ -0,0 +1,120 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.spel;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 缓存 spEl 提高性能
*
* @author L.cm
*/
public class BladeExpressionEvaluator extends CachedExpressionEvaluator {
private final Map<ExpressionKey, Expression> expressionCache = new ConcurrentHashMap<>(64);
private final Map<AnnotatedElementKey, Method> methodCache = new ConcurrentHashMap<>(64);
/**
* Create an {@link EvaluationContext}.
*
* @param method the method
* @param args the method arguments
* @param target the target object
* @param targetClass the target class
* @return the evaluation context
*/
public EvaluationContext createContext(Method method, Object[] args, Object target, Class<?> targetClass, @Nullable BeanFactory beanFactory) {
Method targetMethod = getTargetMethod(targetClass, method);
BladeExpressionRootObject rootObject = new BladeExpressionRootObject(method, args, target, targetClass, targetMethod);
MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(rootObject, targetMethod, args, getParameterNameDiscoverer());
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
}
/**
* Create an {@link EvaluationContext}.
*
* @param method the method
* @param args the method arguments
* @param rootObject rootObject
* @param targetClass the target class
* @return the evaluation context
*/
public EvaluationContext createContext(Method method, Object[] args, Class<?> targetClass, Object rootObject, @Nullable BeanFactory beanFactory) {
Method targetMethod = getTargetMethod(targetClass, method);
MethodBasedEvaluationContext evaluationContext = new MethodBasedEvaluationContext(rootObject, targetMethod, args, getParameterNameDiscoverer());
if (beanFactory != null) {
evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
}
return evaluationContext;
}
@Nullable
public Object eval(String expression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return eval(expression, methodKey, evalContext, null);
}
@Nullable
public <T> T eval(String expression, AnnotatedElementKey methodKey, EvaluationContext evalContext, @Nullable Class<T> valueType) {
return getExpression(this.expressionCache, methodKey, expression).getValue(evalContext, valueType);
}
@Nullable
public String evalAsText(String expression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return eval(expression, methodKey, evalContext, String.class);
}
public boolean evalAsBool(String expression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
return Boolean.TRUE.equals(eval(expression, methodKey, evalContext, Boolean.class));
}
private Method getTargetMethod(Class<?> targetClass, Method method) {
AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
return methodCache.computeIfAbsent(methodKey, (key) -> AopUtils.getMostSpecificMethod(method, targetClass));
}
/**
* Clear all caches.
*/
public void clear() {
this.expressionCache.clear();
this.methodCache.clear();
}
}
@@ -0,0 +1,51 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.spel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.lang.reflect.Method;
/**
* ExpressionRootObject
*
* @author L.cm
*/
@Getter
@AllArgsConstructor
public class BladeExpressionRootObject {
private final Method method;
private final Object[] args;
private final Object target;
private final Class<?> targetClass;
private final Method targetMethod;
}
@@ -0,0 +1,55 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.ssl;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
* 不进行证书校验
*
* @author L.cm
*/
public class DisableValidationTrustManager implements X509TrustManager {
public static final X509TrustManager INSTANCE = new DisableValidationTrustManager();
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
@@ -0,0 +1,44 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.ssl;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
/**
* 信任所有 host name
*
* @author L.cm
*/
public class TrustAllHostNames implements HostnameVerifier {
public static final TrustAllHostNames INSTANCE = new TrustAllHostNames();
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
}
@@ -0,0 +1,31 @@
package org.springblade.core.tool.support;
import lombok.Getter;
import lombok.ToString;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* 跟踪类变动比较
*
* @author L.cm
*/
@Getter
@ToString
public class BeanDiff {
/**
* 变更字段
*/
private final Set<String> fields = new HashSet<>();
/**
* 旧值
*/
private final Map<String, Object> oldValues = new HashMap<>();
/**
* 新值
*/
private final Map<String, Object> newValues = new HashMap<>();
}
@@ -0,0 +1,41 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.support;
import java.util.function.Supplier;
/**
* 解决 no binder available 问题
*
* @author Chill
*/
public class BinderSupplier implements Supplier<Object> {
@Override
public Object get() {
return null;
}
}
@@ -0,0 +1,39 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.support;
/**
* Created by Blade.
*
* @author Chill
*/
public class CoreMain {
public static void main(String[] args) {
System.out.println("init core module");
}
}
@@ -0,0 +1,264 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.support;
import org.springblade.core.tool.utils.StringPool;
import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;
/**
* FastStringWriter更改于 jdk CharArrayWriter
*
* <p>
* 1. 去掉了锁
* 2. 初始容量由 32 改为 64
* </p>
*
* @author L.cm
*/
public class FastStringWriter extends Writer {
/**
* The buffer where data is stored.
*/
private char[] buf;
/**
* The number of chars in the buffer.
*/
private int count;
/**
* Creates a new CharArrayWriter.
*/
public FastStringWriter() {
this(64);
}
/**
* Creates a new CharArrayWriter with the specified initial size.
*
* @param initialSize an int specifying the initial buffer size.
* @throws IllegalArgumentException if initialSize is negative
*/
public FastStringWriter(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException("Negative initial size: " + initialSize);
}
buf = new char[initialSize];
}
/**
* Writes a character to the buffer.
*/
@Override
public void write(int c) {
int newCount = count + 1;
if (newCount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newCount));
}
buf[count] = (char) c;
count = newCount;
}
/**
* Writes characters to the buffer.
*
* @param c the data to be written
* @param off the start offset in the data
* @param len the number of chars that are written
*/
@Override
public void write(char[] c, int off, int len) {
if ((off < 0) || (off > c.length) || (len < 0) ||
((off + len) > c.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
int newCount = count + len;
if (newCount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newCount));
}
System.arraycopy(c, off, buf, count, len);
count = newCount;
}
/**
* Write a portion of a string to the buffer.
*
* @param str String to be written from
* @param off Offset from which to start reading characters
* @param len Number of characters to be written
*/
@Override
public void write(String str, int off, int len) {
int newCount = count + len;
if (newCount > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newCount));
}
str.getChars(off, off + len, buf, count);
count = newCount;
}
/**
* Writes the contents of the buffer to another character stream.
*
* @param out the output stream to write to
* @throws IOException If an I/O error occurs.
*/
public void writeTo(Writer out) throws IOException {
out.write(buf, 0, count);
}
/**
* Appends the specified character sequence to this writer.
*
* <p> An invocation of this method of the form <tt>out.append(csq)</tt>
* behaves in exactly the same way as the invocation
*
* <pre>
* out.write(csq.toString()) </pre>
*
* <p> Depending on the specification of <tt>toString</tt> for the
* character sequence <tt>csq</tt>, the entire sequence may not be
* appended. For instance, invoking the <tt>toString</tt> method of a
* character buffer will return a subsequence whose content depends upon
* the buffer's position and limit.
*
* @param csq The character sequence to append. If <tt>csq</tt> is
* <tt>null</tt>, then the four characters <tt>"null"</tt> are
* appended to this writer.
* @return This writer
*/
@Override
public FastStringWriter append(CharSequence csq) {
String s = (csq == null ? StringPool.NULL : csq.toString());
write(s, 0, s.length());
return this;
}
/**
* Appends a subsequence of the specified character sequence to this writer.
*
* <p> An invocation of this method of the form <tt>out.append(csq, start,
* end)</tt> when <tt>csq</tt> is not <tt>null</tt>, behaves in
* exactly the same way as the invocation
*
* <pre>
* out.write(csq.subSequence(start, end).toString()) </pre>
*
* @param csq The character sequence from which a subsequence will be
* appended. If <tt>csq</tt> is <tt>null</tt>, then characters
* will be appended as if <tt>csq</tt> contained the four
* characters <tt>"null"</tt>.
* @param start The index of the first character in the subsequence
* @param end The index of the character following the last character in the
* subsequence
* @return This writer
* @throws IndexOutOfBoundsException If <tt>start</tt> or <tt>end</tt> are negative, <tt>start</tt>
* is greater than <tt>end</tt>, or <tt>end</tt> is greater than
* <tt>csq.length()</tt>
*/
@Override
public FastStringWriter append(CharSequence csq, int start, int end) {
String s = (csq == null ? StringPool.NULL : csq).subSequence(start, end).toString();
write(s, 0, s.length());
return this;
}
/**
* Appends the specified character to this writer.
*
* <p> An invocation of this method of the form <tt>out.append(c)</tt>
* behaves in exactly the same way as the invocation
*
* <pre>
* out.write(c) </pre>
*
* @param c The 16-bit character to append
* @return This writer
*/
@Override
public FastStringWriter append(char c) {
write(c);
return this;
}
/**
* Resets the buffer so that you can use it again without
* throwing away the already allocated buffer.
*/
public void reset() {
count = 0;
}
/**
* Returns a copy of the input data.
*
* @return an array of chars copied from the input data.
*/
public char[] toCharArray() {
return Arrays.copyOf(buf, count);
}
/**
* Returns the current size of the buffer.
*
* @return an int representing the current size of the buffer.
*/
public int size() {
return count;
}
/**
* Converts input data to a string.
*
* @return the string.
*/
@Override
public String toString() {
return new String(buf, 0, count);
}
/**
* Flush the stream.
*/
@Override
public void flush() {
}
/**
* Close the stream. This method does not release the buffer, since its
* contents might still be required. Note: Invoking this method in this class
* will have no effect.
*/
@Override
public void close() {
}
}
@@ -0,0 +1,45 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.support;
import java.io.OutputStream;
/**
* A factory for creating MultiOutputStream objects.
*
* @author Chill
*/
public interface IMultiOutputStream {
/**
* Builds the output stream.
*
* @param params the params
* @return the output stream
*/
OutputStream buildOutputStream(Integer... params);
}
@@ -0,0 +1,195 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.support;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.concurrent.ThreadLocalRandom;
/**
* 轻量级雪花ID生成器
* <p>
* 64位结构: 1位符号(0) | 41位时间差值 | 10位机器标识 | 12位毫秒内序列
* <p>
* 设计要点
* <ul>
* <li>时间纪元 2018-01-01 UTC可使用至 2087 </li>
* <li>机器标识基于网卡MAC地址与进程PID混合哈希容器/集群环境下碰撞率极低</li>
* <li>内置时钟回拨容忍机制2秒内自旋等待超出则抛出异常拒绝生成</li>
* <li>适用于 JdbcTemplate 直接入库场景无需引入外部 IdWorker 依赖</li>
* </ul>
*
* @author Chill
*/
public final class IdGenerator {
// ======================== 位分配常量 ========================
/** 机器标识占位 */
private static final int MACHINE_BITS = 10;
/** 毫秒内序列占位 */
private static final int SEQUENCE_BITS = 12;
/** 机器标识左移位数 */
private static final int MACHINE_SHIFT = SEQUENCE_BITS;
/** 时间戳左移位数 */
private static final int TIMESTAMP_SHIFT = MACHINE_BITS + SEQUENCE_BITS;
/** 机器标识上限掩码(1023) */
private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_BITS);
/** 序列号上限掩码(4095) */
private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
// ======================== 时钟常量 ========================
/** 起始纪元:2018-01-01 00:00:00 UTC */
private static final long EPOCH = 1514764800000L;
/** 时钟回拨容忍上限(毫秒) */
private static final long MAX_BACKWARD_MS = 2000L;
// ======================== 运行时状态 ========================
/** 当前机器标识(启动时初始化,不可变) */
private static final long MACHINE_ID = resolveMachineId();
/** 上一次生成ID的时间戳 */
private static long lastTimestamp = -1L;
/** 当前毫秒内序列号 */
private static long sequence = 0L;
private IdGenerator() {
}
/**
* 生成全局唯一的雪花ID
*
* @return 64位 long ID
* @throws IllegalStateException 时钟回拨超出容忍阈值时抛出
*/
public static synchronized long getId() {
long now = System.currentTimeMillis();
// 时钟回拨保护
if (now < lastTimestamp) {
long offset = lastTimestamp - now;
if (offset <= MAX_BACKWARD_MS) {
// 回拨幅度在容忍范围内自旋等待追上
now = awaitNextMillis(lastTimestamp);
} else {
throw new IllegalStateException(
"时钟回拨超出容忍阈值: 回拨=" + offset + "ms, 上限=" + MAX_BACKWARD_MS + "ms"
);
}
}
// 同一毫秒内递增序列号
if (now == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0) {
// 当前毫秒序列号耗尽等待下一毫秒
now = awaitNextMillis(lastTimestamp);
}
} else {
// 新毫秒随机起始序列避免低位连续为0造成分库分表不均匀
sequence = ThreadLocalRandom.current().nextLong(4);
}
lastTimestamp = now;
return ((now - EPOCH) << TIMESTAMP_SHIFT)
| (MACHINE_ID << MACHINE_SHIFT)
| sequence;
}
/**
* 自旋等待直到时间戳超过给定值
*
* @param target 目标时间戳
* @return 超过目标的当前时间戳
*/
private static long awaitNextMillis(long target) {
long now;
do {
Thread.onSpinWait();
now = System.currentTimeMillis();
} while (now <= target);
return now;
}
/**
* 解析机器标识
* <p>
* 优先使用网卡MAC地址与进程PID混合哈希确保在容器/集群环境下具有良好的分散性
* 若MAC地址不可用降级为主机名与PID的混合哈希若仍失败使用安全随机数兜底
*
* @return 10位机器标识0~1023
*/
private static long resolveMachineId() {
try {
long pid = ProcessHandle.current().pid();
byte[] mac = findFirstMacAddress();
if (mac != null) {
// MAC(6字节) + PID 混合哈希
long hash = 0L;
for (byte macByte : mac) {
hash = hash * 31 + (macByte & 0xFF);
}
return (hash ^ pid) & MAX_MACHINE_ID;
}
// MAC不可用降级为主机名 + PID
String hostName = InetAddress.getLocalHost().getHostName();
return (hostName.hashCode() ^ pid) & MAX_MACHINE_ID;
} catch (Exception ignored) {
return ThreadLocalRandom.current().nextLong(MAX_MACHINE_ID + 1);
}
}
/**
* 查找首个可用的物理网卡MAC地址
*
* @return MAC地址字节数组不可用时返回 null
*/
private static byte[] findFirstMacAddress() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
if (ni.isLoopback() || ni.isVirtual() || !ni.isUp()) {
continue;
}
byte[] mac = ni.getHardwareAddress();
if (mac != null && mac.length == 6) {
return mac;
}
}
} catch (Exception ignored) {
}
return null;
}
}
@@ -0,0 +1,159 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.support;
/**
* 图片操作类
*
* @author Chill
*/
public class ImagePosition {
/**
* 图片顶部.
*/
public static final int TOP = 32;
/**
* 图片中部.
*/
public static final int MIDDLE = 16;
/**
* 图片底部.
*/
public static final int BOTTOM = 8;
/**
* 图片左侧.
*/
public static final int LEFT = 4;
/**
* 图片居中.
*/
public static final int CENTER = 2;
/**
* 图片右侧.
*/
public static final int RIGHT = 1;
/**
* 横向边距靠左或靠右时和边界的距离.
*/
private static final int PADDING_HORI = 6;
/**
* 纵向边距靠上或靠底时和边界的距离.
*/
private static final int PADDING_VERT = 6;
/**
* 图片中盒[左上角]的x坐标.
*/
private int boxPosX;
/**
* 图片中盒[左上角]的y坐标.
*/
private int boxPosY;
/**
* Instantiates a new image position.
*
* @param width the width
* @param height the height
* @param boxWidth the box width
* @param boxHeight the box height
* @param style the style
*/
public ImagePosition(int width, int height, int boxWidth, int boxHeight, int style) {
switch (style & 7) {
case LEFT:
boxPosX = PADDING_HORI;
break;
case RIGHT:
boxPosX = width - boxWidth - PADDING_HORI;
break;
case CENTER:
default:
boxPosX = (width - boxWidth) / 2;
}
switch (style >> 3 << 3) {
case TOP:
boxPosY = PADDING_VERT;
break;
case MIDDLE:
boxPosY = (height - boxHeight) / 2;
break;
case BOTTOM:
default:
boxPosY = height - boxHeight - PADDING_VERT;
}
}
/**
* Gets the x.
*
* @return the x
*/
public int getX() {
return getX(0);
}
/**
* Gets the x.
*
* @param x 横向偏移
* @return the x
*/
public int getX(int x) {
return this.boxPosX + x;
}
/**
* Gets the y.
*
* @return the y
*/
public int getY() {
return getY(0);
}
/**
* Gets the y.
*
* @param y 纵向偏移
* @return the y
*/
public int getY(int y) {
return this.boxPosY + y;
}
}
@@ -0,0 +1,283 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.support;
import org.springblade.core.tool.utils.Func;
import org.springframework.util.LinkedCaseInsensitiveMap;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 链式map
*
* @author Chill
*/
public class Kv extends LinkedCaseInsensitiveMap<Object> {
private Kv() {
super();
}
/**
* 创建Kv
*
* @return Kv
*/
public static Kv init() {
return new Kv();
}
/**
* 创建Kv
*
* @return Kv
*/
public static Kv create() {
return new Kv();
}
/**
* 创建Kv
*
* @param value 初始化数据
* @return Kv
*/
public static Kv create(Kv value) {
Kv kv = new Kv();
kv.putAll(value);
return kv;
}
/**
* 从Map创建Kv
*
* @param map 初始化数据
* @return Kv
*/
public static Kv create(Map<?, ?> map) {
Kv kv = new Kv();
if (map != null) {
map.forEach((key, value) -> kv.set(String.valueOf(key), value));
}
return kv;
}
/**
* 从对象创建Kv
*
* @param value 源对象
* @return Kv
*/
public static Kv create(Object value) {
Kv kv = new Kv();
if (value != null) {
if (value instanceof Map) {
return create((Map<?, ?>) value);
} else {
kv.set("object", value);
}
}
return kv;
}
public static <K, V> HashMap<K, V> newMap() {
return new HashMap<>(16);
}
/**
* 设置列
*
* @param attr 属性
* @param value
* @return 本身
*/
public Kv set(String attr, Object value) {
this.put(attr, value);
return this;
}
/**
* 设置全部
*
* @param map 属性
* @return 本身
*/
public Kv setAll(Map<? extends String, ?> map) {
if (map != null) {
this.putAll(map);
}
return this;
}
/**
* 设置列当键或值为null时忽略
*
* @param attr 属性
* @param value
* @return 本身
*/
public Kv setIgnoreNull(String attr, Object value) {
if (attr != null && value != null) {
set(attr, value);
}
return this;
}
public Object getObj(String key) {
return super.get(key);
}
/**
* 获得特定类型值
*
* @param <T> 值类型
* @param attr 字段名
* @param defaultValue 默认值
* @return 字段值
*/
@SuppressWarnings("unchecked")
public <T> T get(String attr, T defaultValue) {
final Object result = get(attr);
return (T) (result != null ? result : defaultValue);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public String getStr(String attr) {
return Func.toStr(get(attr), null);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public Integer getInt(String attr) {
return Func.toInt(get(attr), -1);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public Long getLong(String attr) {
return Func.toLong(get(attr), -1L);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public Float getFloat(String attr) {
return Func.toFloat(get(attr), null);
}
public Double getDouble(String attr) {
return Func.toDouble(get(attr), null);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public Boolean getBool(String attr) {
return Func.toBoolean(get(attr), null);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public byte[] getBytes(String attr) {
return get(attr, null);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public Date getDate(String attr) {
return get(attr, null);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public Time getTime(String attr) {
return get(attr, null);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public Timestamp getTimestamp(String attr) {
return get(attr, null);
}
/**
* 获得特定类型值
*
* @param attr 字段名
* @return 字段值
*/
public Number getNumber(String attr) {
return get(attr, null);
}
@Override
public Kv clone() {
Kv clone = new Kv();
clone.putAll(this);
return clone;
}
}
@@ -0,0 +1,501 @@
package org.springblade.core.tool.support;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.tool.utils.StringPool;
import org.springblade.core.tool.utils.StringUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 字符串切分器
*
* @author Looly
*/
public class StrSpliter {
//---------------------------------------------------------------------------------------------- Split by char
/**
* 切分字符串路径仅支持Unix分界符/
*
* @param str 被切分的字符串
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> splitPath(String str) {
return splitPath(str, 0);
}
/**
* 切分字符串路径仅支持Unix分界符/
*
* @param str 被切分的字符串
* @return 切分后的集合
* @since 3.0.8
*/
public static String[] splitPathToArray(String str) {
return toArray(splitPath(str));
}
/**
* 切分字符串路径仅支持Unix分界符/
*
* @param str 被切分的字符串
* @param limit 限制分片数
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> splitPath(String str, int limit) {
return split(str, StringPool.SLASH, limit, true, true);
}
/**
* 切分字符串路径仅支持Unix分界符/
*
* @param str 被切分的字符串
* @param limit 限制分片数
* @return 切分后的集合
* @since 3.0.8
*/
public static String[] splitPathToArray(String str, int limit) {
return toArray(splitPath(str, limit));
}
/**
* 切分字符串
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.2.1
*/
public static List<String> splitTrim(String str, char separator, boolean ignoreEmpty) {
return split(str, separator, 0, true, ignoreEmpty);
}
/**
* 切分字符串
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> split(String str, char separator, boolean isTrim, boolean ignoreEmpty) {
return split(str, separator, 0, isTrim, ignoreEmpty);
}
/**
* 切分字符串大小写敏感去除每个元素两边空白符
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数-1不限制
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> splitTrim(String str, char separator, int limit, boolean ignoreEmpty) {
return split(str, separator, limit, true, ignoreEmpty, false);
}
/**
* 切分字符串大小写敏感
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数-1不限制
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> split(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) {
return split(str, separator, limit, isTrim, ignoreEmpty, false);
}
/**
* 切分字符串忽略大小写
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数-1不限制
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.2.1
*/
public static List<String> splitIgnoreCase(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) {
return split(str, separator, limit, isTrim, ignoreEmpty, true);
}
/**
* 切分字符串
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数-1不限制
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @param ignoreCase 是否忽略大小写
* @return 切分后的集合
* @since 3.2.1
*/
public static List<String> split(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase) {
if (StringUtil.isEmpty(str)) {
return new ArrayList<String>(0);
}
if (limit == 1) {
return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty);
}
final ArrayList<String> list = new ArrayList<>(limit > 0 ? limit : 16);
int len = str.length();
int start = 0;
for (int i = 0; i < len; i++) {
if (Func.equals(separator, str.charAt(i))) {
addToList(list, str.substring(start, i), isTrim, ignoreEmpty);
start = i + 1;
//检查是否超出范围最大允许limit-1个剩下一个留给末尾字符串
if (limit > 0 && list.size() > limit - 2) {
break;
}
}
}
return addToList(list, str.substring(start, len), isTrim, ignoreEmpty);
}
/**
* 切分字符串为字符串数组
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static String[] splitToArray(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) {
return toArray(split(str, separator, limit, isTrim, ignoreEmpty));
}
//---------------------------------------------------------------------------------------------- Split by String
/**
* 切分字符串不忽略大小写
*
* @param str 被切分的字符串
* @param separator 分隔符字符串
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> split(String str, String separator, boolean isTrim, boolean ignoreEmpty) {
return split(str, separator, -1, isTrim, ignoreEmpty, false);
}
/**
* 切分字符串去除每个元素两边空格忽略大小写
*
* @param str 被切分的字符串
* @param separator 分隔符字符串
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.2.1
*/
public static List<String> splitTrim(String str, String separator, boolean ignoreEmpty) {
return split(str, separator, true, ignoreEmpty);
}
/**
* 切分字符串不忽略大小写
*
* @param str 被切分的字符串
* @param separator 分隔符字符串
* @param limit 限制分片数
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> split(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) {
return split(str, separator, limit, isTrim, ignoreEmpty, false);
}
/**
* 切分字符串去除每个元素两边空格忽略大小写
*
* @param str 被切分的字符串
* @param separator 分隔符字符串
* @param limit 限制分片数
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.2.1
*/
public static List<String> splitTrim(String str, String separator, int limit, boolean ignoreEmpty) {
return split(str, separator, limit, true, ignoreEmpty);
}
/**
* 切分字符串忽略大小写
*
* @param str 被切分的字符串
* @param separator 分隔符字符串
* @param limit 限制分片数
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.2.1
*/
public static List<String> splitIgnoreCase(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) {
return split(str, separator, limit, isTrim, ignoreEmpty, true);
}
/**
* 切分字符串去除每个元素两边空格忽略大小写
*
* @param str 被切分的字符串
* @param separator 分隔符字符串
* @param limit 限制分片数
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.2.1
*/
public static List<String> splitTrimIgnoreCase(String str, String separator, int limit, boolean ignoreEmpty) {
return split(str, separator, limit, true, ignoreEmpty, true);
}
/**
* 切分字符串
*
* @param str 被切分的字符串
* @param separator 分隔符字符串
* @param limit 限制分片数
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @param ignoreCase 是否忽略大小写
* @return 切分后的集合
* @since 3.2.1
*/
public static List<String> split(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase) {
if (StringUtil.isEmpty(str)) {
return new ArrayList<String>(0);
}
if (limit == 1) {
return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty);
}
if (StringUtil.isEmpty(separator)) {
return split(str, limit);
} else if (separator.length() == 1) {
return split(str, separator.charAt(0), limit, isTrim, ignoreEmpty, ignoreCase);
}
final ArrayList<String> list = new ArrayList<>();
int len = str.length();
int separatorLen = separator.length();
int start = 0;
int i = 0;
while (i < len) {
i = StringUtil.indexOf(str, separator, start, ignoreCase);
if (i > -1) {
addToList(list, str.substring(start, i), isTrim, ignoreEmpty);
start = i + separatorLen;
//检查是否超出范围最大允许limit-1个剩下一个留给末尾字符串
if (limit > 0 && list.size() > limit - 2) {
break;
}
} else {
break;
}
}
return addToList(list, str.substring(start, len), isTrim, ignoreEmpty);
}
/**
* 切分字符串为字符串数组
*
* @param str 被切分的字符串
* @param separator 分隔符字符
* @param limit 限制分片数
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static String[] splitToArray(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) {
return toArray(split(str, separator, limit, isTrim, ignoreEmpty));
}
//---------------------------------------------------------------------------------------------- Split by Whitespace
/**
* 使用空白符切分字符串<br>
* 切分后的字符串两边不包含空白符空串或空白符串并不做为元素之一
*
* @param str 被切分的字符串
* @param limit 限制分片数
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> split(String str, int limit) {
if (StringUtil.isEmpty(str)) {
return new ArrayList<String>(0);
}
if (limit == 1) {
return addToList(new ArrayList<String>(1), str, true, true);
}
final ArrayList<String> list = new ArrayList<>();
int len = str.length();
int start = 0;
for (int i = 0; i < len; i++) {
if (Func.isEmpty(str.charAt(i))) {
addToList(list, str.substring(start, i), true, true);
start = i + 1;
if (limit > 0 && list.size() > limit - 2) {
break;
}
}
}
return addToList(list, str.substring(start, len), true, true);
}
/**
* 切分字符串为字符串数组
*
* @param str 被切分的字符串
* @param limit 限制分片数
* @return 切分后的集合
* @since 3.0.8
*/
public static String[] splitToArray(String str, int limit) {
return toArray(split(str, limit));
}
//---------------------------------------------------------------------------------------------- Split by regex
/**
* 通过正则切分字符串
*
* @param str 字符串
* @param separatorPattern 分隔符正则{@link Pattern}
* @param limit 限制分片数
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static List<String> split(String str, Pattern separatorPattern, int limit, boolean isTrim, boolean ignoreEmpty) {
if (StringUtil.isEmpty(str)) {
return new ArrayList<String>(0);
}
if (limit == 1) {
return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty);
}
if (null == separatorPattern) {
return split(str, limit);
}
final Matcher matcher = separatorPattern.matcher(str);
final ArrayList<String> list = new ArrayList<>();
int len = str.length();
int start = 0;
while (matcher.find()) {
addToList(list, str.substring(start, matcher.start()), isTrim, ignoreEmpty);
start = matcher.end();
if (limit > 0 && list.size() > limit - 2) {
break;
}
}
return addToList(list, str.substring(start, len), isTrim, ignoreEmpty);
}
/**
* 通过正则切分字符串为字符串数组
*
* @param str 被切分的字符串
* @param separatorPattern 分隔符正则{@link Pattern}
* @param limit 限制分片数
* @param isTrim 是否去除切分字符串后每个元素两边的空格
* @param ignoreEmpty 是否忽略空串
* @return 切分后的集合
* @since 3.0.8
*/
public static String[] splitToArray(String str, Pattern separatorPattern, int limit, boolean isTrim, boolean ignoreEmpty) {
return toArray(split(str, separatorPattern, limit, isTrim, ignoreEmpty));
}
//---------------------------------------------------------------------------------------------- Split by length
/**
* 根据给定长度将给定字符串截取为多个部分
*
* @param str 字符串
* @param len 每一个小节的长度
* @return 截取后的字符串数组
*/
public static String[] splitByLength(String str, int len) {
int partCount = str.length() / len;
int lastPartCount = str.length() % len;
int fixPart = 0;
if (lastPartCount != 0) {
fixPart = 1;
}
final String[] strs = new String[partCount + fixPart];
for (int i = 0; i < partCount + fixPart; i++) {
if (i == partCount + fixPart - 1 && lastPartCount != 0) {
strs[i] = str.substring(i * len, i * len + lastPartCount);
} else {
strs[i] = str.substring(i * len, i * len + len);
}
}
return strs;
}
//---------------------------------------------------------------------------------------------------------- Private method start
/**
* 将字符串加入List中
*
* @param list 列表
* @param part 被加入的部分
* @param isTrim 是否去除两端空白符
* @param ignoreEmpty 是否略过空字符串空字符串不做为一个元素
* @return 列表
*/
private static List<String> addToList(List<String> list, String part, boolean isTrim, boolean ignoreEmpty) {
part = part.toString();
if (isTrim) {
part = part.trim();
}
if (false == ignoreEmpty || false == part.isEmpty()) {
list.add(part);
}
return list;
}
/**
* List转Array
*
* @param list List
* @return Array
*/
private static String[] toArray(List<String> list) {
return list.toArray(new String[list.size()]);
}
//---------------------------------------------------------------------------------------------------------- Private method end
}
@@ -0,0 +1,88 @@
package org.springblade.core.tool.support;
import org.springblade.core.tool.utils.Exceptions;
import org.springframework.lang.Nullable;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Lambda 受检异常处理
* https://segmentfault.com/a/1190000007832130
*
* @author Chill
*/
public class Try {
public static <T, R> Function<T, R> of(UncheckedFunction<T, R> mapper) {
Objects.requireNonNull(mapper);
return t -> {
try {
return mapper.apply(t);
} catch (Exception e) {
throw Exceptions.unchecked(e);
}
};
}
public static <T> Consumer<T> of(UncheckedConsumer<T> mapper) {
Objects.requireNonNull(mapper);
return t -> {
try {
mapper.accept(t);
} catch (Exception e) {
throw Exceptions.unchecked(e);
}
};
}
public static <T> Supplier<T> of(UncheckedSupplier<T> mapper) {
Objects.requireNonNull(mapper);
return () -> {
try {
return mapper.get();
} catch (Exception e) {
throw Exceptions.unchecked(e);
}
};
}
@FunctionalInterface
public interface UncheckedFunction<T, R> {
/**
* apply
*
* @param t
* @return
* @throws Exception
*/
@Nullable
R apply(@Nullable T t) throws Exception;
}
@FunctionalInterface
public interface UncheckedConsumer<T> {
/**
* accept
*
* @param t
* @throws Exception
*/
@Nullable
void accept(@Nullable T t) throws Exception;
}
@FunctionalInterface
public interface UncheckedSupplier<T> {
/**
* get
*
* @return
* @throws Exception
*/
@Nullable
T get() throws Exception;
}
}
@@ -0,0 +1,71 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.tuple;
import lombok.RequiredArgsConstructor;
import org.springblade.core.tool.utils.RsaUtil;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* rsa key pair 封装
*
* @author L.cm
*/
@RequiredArgsConstructor
public class KeyPair {
private final java.security.KeyPair keyPair;
public PublicKey getPublic() {
return keyPair.getPublic();
}
public PrivateKey getPrivate() {
return keyPair.getPrivate();
}
public byte[] getPublicBytes() {
return this.getPublic().getEncoded();
}
public byte[] getPrivateBytes() {
return this.getPrivate().getEncoded();
}
public String getPublicBase64() {
return RsaUtil.getKeyString(this.getPublic());
}
public String getPrivateBase64() {
return RsaUtil.getKeyString(this.getPrivate());
}
@Override
public String toString() {
return "PublicKey=" + this.getPublicBase64() + '\n' + "PrivateKey=" + this.getPrivateBase64();
}
}
@@ -0,0 +1,93 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.tuple;
import lombok.*;
/**
* tuple Pair
*
* @author L.cm
**/
@Getter
@ToString
@EqualsAndHashCode
public class Pair<L, R> {
private static final Pair<Object, Object> EMPTY = new Pair<>(null, null);
private final L left;
private final R right;
/**
* Returns an empty pair.
*/
@SuppressWarnings("unchecked")
public static <L, R> Pair<L, R> empty() {
return (Pair<L, R>) EMPTY;
}
/**
* Constructs a pair with its left value being {@code left}, or returns an empty pair if
* {@code left} is null.
*
* @return the constructed pair or an empty pair if {@code left} is null.
*/
public static <L, R> Pair<L, R> createLeft(L left) {
if (left == null) {
return empty();
} else {
return new Pair<>(left, null);
}
}
/**
* Constructs a pair with its right value being {@code right}, or returns an empty pair if
* {@code right} is null.
*
* @return the constructed pair or an empty pair if {@code right} is null.
*/
public static <L, R> Pair<L, R> createRight(R right) {
if (right == null) {
return empty();
} else {
return new Pair<>(null, right);
}
}
public static <L, R> Pair<L, R> create(L left, R right) {
if (right == null && left == null) {
return empty();
} else {
return new Pair<>(left, right);
}
}
private Pair(L left, R right) {
this.left = left;
this.right = right;
}
}
@@ -0,0 +1,259 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Function;
/**
* 完全兼容微信所使用的AES加密工具类
* aes的key必须是256byte长比如32个字符可以使用AesKit.genAesKey()来生成一组key
*
* @author L.cm
*/
public class AesUtil {
public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
public static String genAesKey() {
return StringUtil.random(32);
}
/**
* 转换成mysql aes
*
* @param key key
* @return SecretKeySpec
*/
public static SecretKeySpec genMySqlAesKey(final byte[] key) {
final byte[] finalKey = new byte[16];
int i = 0;
for (byte b : key) {
finalKey[i++ % 16] ^= b;
}
return new SecretKeySpec(finalKey, "AES");
}
/**
* 转换成mysql aes
*
* @param key key
* @return SecretKeySpec
*/
public static SecretKeySpec genMySqlAesKey(final String key) {
return genMySqlAesKey(key.getBytes(DEFAULT_CHARSET));
}
public static String encryptToHex(String content, String aesTextKey) {
return HexUtil.encodeToString(encrypt(content, aesTextKey));
}
public static String encryptToHex(byte[] content, String aesTextKey) {
return HexUtil.encodeToString(encrypt(content, aesTextKey));
}
public static String encryptToBase64(String content, String aesTextKey) {
return Base64Util.encodeToString(encrypt(content, aesTextKey));
}
public static String encryptToBase64(byte[] content, String aesTextKey) {
return Base64Util.encodeToString(encrypt(content, aesTextKey));
}
public static byte[] encrypt(String content, String aesTextKey) {
return encrypt(content.getBytes(DEFAULT_CHARSET), aesTextKey);
}
public static byte[] encrypt(String content, Charset charset, String aesTextKey) {
return encrypt(content.getBytes(charset), aesTextKey);
}
public static byte[] encrypt(byte[] content, String aesTextKey) {
return encrypt(content, Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET));
}
@Nullable
public static String decryptFormHexToString(@Nullable String content, String aesTextKey) {
byte[] hexBytes = decryptFormHex(content, aesTextKey);
if (hexBytes == null) {
return null;
}
return new String(hexBytes, DEFAULT_CHARSET);
}
@Nullable
public static byte[] decryptFormHex(@Nullable String content, String aesTextKey) {
if (StringUtil.isBlank(content)) {
return null;
}
return decryptFormHex(content.getBytes(DEFAULT_CHARSET), aesTextKey);
}
public static byte[] decryptFormHex(byte[] content, String aesTextKey) {
return decrypt(HexUtil.decode(content), aesTextKey);
}
@Nullable
public static String decryptFormBase64ToString(@Nullable String content, String aesTextKey) {
byte[] hexBytes = decryptFormBase64(content, aesTextKey);
if (hexBytes == null) {
return null;
}
return new String(hexBytes, DEFAULT_CHARSET);
}
@Nullable
public static byte[] decryptFormBase64(@Nullable String content, String aesTextKey) {
if (StringUtil.isBlank(content)) {
return null;
}
return decryptFormBase64(content.getBytes(DEFAULT_CHARSET), aesTextKey);
}
public static byte[] decryptFormBase64(byte[] content, String aesTextKey) {
return decrypt(Base64Util.decode(content), aesTextKey);
}
public static String decryptToString(byte[] content, String aesTextKey) {
return new String(decrypt(content, aesTextKey), DEFAULT_CHARSET);
}
public static byte[] decrypt(byte[] content, String aesTextKey) {
return decrypt(content, Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET));
}
public static byte[] encrypt(byte[] content, byte[] aesKey) {
return aes(Pkcs7Encoder.encode(content), aesKey, Cipher.ENCRYPT_MODE);
}
public static byte[] decrypt(byte[] encrypted, byte[] aesKey) {
return Pkcs7Encoder.decode(aes(encrypted, aesKey, Cipher.DECRYPT_MODE));
}
private static byte[] aes(byte[] encrypted, byte[] aesKey, int mode) {
Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32");
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
cipher.init(mode, keySpec, iv);
return cipher.doFinal(encrypted);
} catch (Exception e) {
throw Exceptions.unchecked(e);
}
}
/**
* 兼容 mysql aes 加密
*
* @param input input
* @param aesKey aesKey
* @return byte array
*/
public static byte[] encryptMysql(String input, String aesKey) {
return encryptMysql(input, aesKey, Function.identity());
}
/**
* 兼容 mysql aes 加密
*
* @param input input
* @param aesKey aesKey
* @param <T> 泛型标记
* @return T 泛型对象
*/
public static <T> T encryptMysql(String input, String aesKey, Function<byte[], T> mapper) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, genMySqlAesKey(aesKey));
byte[] bytes = cipher.doFinal(input.getBytes(StandardCharsets.UTF_8));
return mapper.apply(bytes);
} catch (Exception e) {
throw Exceptions.unchecked(e);
}
}
/**
* 兼容 mysql aes 加密
*
* @param input input
* @param aesKey aesKey
* @return byte 数组
*/
public static byte[] decryptMysql(String input, String aesKey) {
return decryptMysql(input, txt -> txt.getBytes(DEFAULT_CHARSET), aesKey);
}
/**
* 兼容 mysql aes 加密
*
* @param input input
* @param aesKey aesKey
* @return byte 数组
*/
public static byte[] decryptMysql(String input, Function<String, byte[]> inputMapper, String aesKey) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, genMySqlAesKey(aesKey));
return cipher.doFinal(inputMapper.apply(input));
} catch (Exception e) {
throw Exceptions.unchecked(e);
}
}
/**
* 兼容 mysql aes 加密
*
* @param input input
* @param inputMapper Function
* @param aesKey aesKey
* @return 字符串
*/
public static String decryptMysqlToString(String input, Function<String, byte[]> inputMapper, String aesKey) {
return new String(decryptMysql(input, inputMapper, aesKey), DEFAULT_CHARSET);
}
/**
* 兼容 mysql aes 加密
*
* @param input input
* @param aesKey aesKey
* @return 字符串
*/
public static String decryptMysqlToString(String input, String aesKey) {
return decryptMysqlToString(input, txt -> txt.getBytes(DEFAULT_CHARSET), aesKey);
}
}
@@ -0,0 +1,60 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import lombok.AllArgsConstructor;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import java.io.File;
import java.io.FileFilter;
import java.io.Serializable;
/**
* Spring AntPath 规则文件过滤
*
* @author L.cm
*/
@AllArgsConstructor
public class AntPathFilter implements FileFilter, Serializable {
private static final long serialVersionUID = 812598009067554612L;
private static final PathMatcher PATH_MATCHER = new AntPathMatcher();
private final String pattern;
/**
* 过滤规则
*
* @param pathname 路径
* @return boolean
*/
@Override
public boolean accept(File pathname) {
String filePath = pathname.getAbsolutePath();
return PATH_MATCHER.match(pattern, filePath);
}
}
@@ -0,0 +1,235 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import java.nio.charset.Charset;
import java.util.Base64;
/**
* Base64工具
*
* @author L.cm
*/
public class Base64Util {
public static final Base64.Encoder ENCODER = Base64.getEncoder();
public static final Base64.Encoder URL_ENCODER = Base64.getUrlEncoder();
public static final Base64.Decoder DECODER = Base64.getDecoder();
public static final Base64.Decoder URL_DECODER = Base64.getUrlDecoder();
/**
* 编码
*
* @param value 字符串
* @return {String}
*/
public static String encode(String value) {
return encode(value, Charsets.UTF_8);
}
/**
* 编码
*
* @param value 字符串
* @param charset 字符集
* @return {String}
*/
public static String encode(String value, Charset charset) {
byte[] val = value.getBytes(charset);
return new String(encode(val), charset);
}
/**
* 编码URL安全
*
* @param value 字符串
* @return {String}
*/
public static String encodeUrlSafe(String value) {
return encodeUrlSafe(value, Charsets.UTF_8);
}
/**
* 编码URL安全
*
* @param value 字符串
* @param charset 字符集
* @return {String}
*/
public static String encodeUrlSafe(String value, Charset charset) {
byte[] val = value.getBytes(charset);
return new String(encodeUrlSafe(val), charset);
}
/**
* 解码
*
* @param value 字符串
* @return {String}
*/
public static String decode(String value) {
return decode(value, Charsets.UTF_8);
}
/**
* 解码
*
* @param value 字符串
* @param charset 字符集
* @return {String}
*/
public static String decode(String value, Charset charset) {
byte[] val = value.getBytes(charset);
byte[] decodedValue = decode(val);
return new String(decodedValue, charset);
}
/**
* 解码URL安全
*
* @param value 字符串
* @return {String}
*/
public static String decodeUrlSafe(String value) {
return decodeUrlSafe(value, Charsets.UTF_8);
}
/**
* 解码URL安全
*
* @param value 字符串
* @param charset 字符集
* @return {String}
*/
public static String decodeUrlSafe(String value, Charset charset) {
byte[] val = value.getBytes(charset);
byte[] decodedValue = decodeUrlSafe(val);
return new String(decodedValue, charset);
}
/**
* Base64-encode the given byte array.
*
* @param src the original byte array
* @return the encoded byte array
*/
public static byte[] encode(byte[] src) {
if (src.length == 0) {
return src;
}
return ENCODER.encode(src);
}
/**
* Base64-decode the given byte array.
*
* @param src the encoded byte array
* @return the original byte array
*/
public static byte[] decode(byte[] src) {
if (src.length == 0) {
return src;
}
return DECODER.decode(src);
}
/**
* Base64-encode the given byte array using the RFC 4648
* "URL and Filename Safe Alphabet".
*
* @param src the original byte array
* @return the encoded byte array
*/
public static byte[] encodeUrlSafe(byte[] src) {
if (src.length == 0) {
return src;
}
return URL_ENCODER.encode(src);
}
/**
* Base64-decode the given byte array using the RFC 4648
* "URL and Filename Safe Alphabet".
*
* @param src the encoded byte array
* @return the original byte array
* @since 4.2.4
*/
public static byte[] decodeUrlSafe(byte[] src) {
if (src.length == 0) {
return src;
}
return URL_DECODER.decode(src);
}
/**
* Base64-encode the given byte array to a String.
*
* @param src the original byte array
* @return the encoded byte array as a UTF-8 String
*/
public static String encodeToString(byte[] src) {
if (src.length == 0) {
return "";
}
return new String(encode(src), Charsets.UTF_8);
}
/**
* Base64-decode the given byte array from a UTF-8 String.
*
* @param src the encoded UTF-8 String
* @return the original byte array
*/
public static byte[] decodeFromString(String src) {
if (src.isEmpty()) {
return new byte[0];
}
return decode(src.getBytes(Charsets.UTF_8));
}
/**
* Base64-encode the given byte array to a String using the RFC 4648
* "URL and Filename Safe Alphabet".
*
* @param src the original byte array
* @return the encoded byte array as a UTF-8 String
*/
public static String encodeToUrlSafeString(byte[] src) {
return new String(encodeUrlSafe(src), Charsets.UTF_8);
}
/**
* Base64-decode the given byte array from a UTF-8 String using the RFC 4648
* "URL and Filename Safe Alphabet".
*
* @param src the encoded UTF-8 String
* @return the original byte array
*/
public static byte[] decodeFromUrlSafeString(String src) {
return decodeUrlSafe(src.getBytes(Charsets.UTF_8));
}
}
@@ -0,0 +1,433 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springblade.core.tool.beans.BeanProperty;
import org.springblade.core.tool.beans.BladeBeanCopier;
import org.springblade.core.tool.beans.BladeBeanMap;
import org.springblade.core.tool.convert.BladeConverter;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.cglib.beans.BeanGenerator;
import org.springframework.lang.Nullable;
import java.util.*;
/**
* 实体工具类
*
* @author L.cm
*/
public class BeanUtil extends org.springframework.beans.BeanUtils {
/**
* 实例化对象
*
* @param clazz
* @param <T> 泛型标记
* @return 对象
*/
@SuppressWarnings("unchecked")
public static <T> T newInstance(Class<?> clazz) {
return (T) instantiateClass(clazz);
}
/**
* 实例化对象
*
* @param clazzStr 类名
* @param <T> 泛型标记
* @return 对象
*/
public static <T> T newInstance(String clazzStr) {
try {
Class<?> clazz = ClassUtil.forName(clazzStr, null);
return newInstance(clazz);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
/**
* 获取Bean的属性, 支持 propertyName 多级 test.user.name
*
* @param bean bean
* @param propertyName 属性名
* @return 属性值
*/
@Nullable
public static Object getProperty(@Nullable Object bean, String propertyName) {
if (bean == null) {
return null;
}
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean);
return beanWrapper.getPropertyValue(propertyName);
}
/**
* 设置Bean属性, 支持 propertyName 多级 test.user.name
*
* @param bean bean
* @param propertyName 属性名
* @param value 属性值
*/
public static void setProperty(Object bean, String propertyName, Object value) {
Objects.requireNonNull(bean, "bean Could not null");
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean);
beanWrapper.setPropertyValue(propertyName, value);
}
/**
* 深复制
*
* <p>
* 支持 map bean
* </p>
*
* @param source 源对象
* @param <T> 泛型标记
* @return T
*/
@SuppressWarnings("unchecked")
@Nullable
public static <T> T clone(@Nullable T source) {
if (source == null) {
return null;
}
return (T) BeanUtil.copy(source, source.getClass());
}
/**
* copy 对象属性默认不使用Convert
*
* <p>
* 支持 map bean copy
* </p>
*
* @param source 源对象
* @param clazz 类名
* @param <T> 泛型标记
* @return T
*/
@Nullable
public static <T> T copy(@Nullable Object source, Class<T> clazz) {
if (source == null) {
return null;
}
return BeanUtil.copy(source, source.getClass(), clazz);
}
/**
* copy 对象属性默认不使用Convert
*
* <p>
* 支持 map bean copy
* </p>
*
* @param source 源对象
* @param sourceClazz 源类型
* @param targetClazz 转换成的类型
* @param <T> 泛型标记
* @return T
*/
@Nullable
public static <T> T copy(@Nullable Object source, Class sourceClazz, Class<T> targetClazz) {
if (source == null) {
return null;
}
BladeBeanCopier copier = BladeBeanCopier.create(sourceClazz, targetClazz, false);
T to = newInstance(targetClazz);
copier.copy(source, to, null);
return to;
}
/**
* copy 列表对象默认不使用Convert
*
* <p>
* 支持 map bean copy
* </p>
*
* @param sourceList 源列表
* @param targetClazz 转换成的类型
* @param <T> 泛型标记
* @return T
*/
public static <T> List<T> copy(@Nullable Collection<?> sourceList, Class<T> targetClazz) {
if (sourceList == null || sourceList.isEmpty()) {
return Collections.emptyList();
}
List<T> outList = new ArrayList<>(sourceList.size());
Class<?> sourceClazz = null;
for (Object source : sourceList) {
if (source == null) {
continue;
}
if (sourceClazz == null) {
sourceClazz = source.getClass();
}
T bean = BeanUtil.copy(source, sourceClazz, targetClazz);
outList.add(bean);
}
return outList;
}
/**
* 拷贝对象
*
* <p>
* 支持 map bean copy
* </p>
*
* @param source 源对象
* @param targetBean 需要赋值的对象
*/
public static void copy(@Nullable Object source, @Nullable Object targetBean) {
if (source == null || targetBean == null) {
return;
}
BladeBeanCopier copier = BladeBeanCopier
.create(source.getClass(), targetBean.getClass(), false);
copier.copy(source, targetBean, null);
}
/**
* 拷贝对象source 属性做 null 判断Map 不支持map 会做 instanceof 判断不会
*
* <p>
* 支持 bean copy
* </p>
*
* @param source 源对象
* @param targetBean 需要赋值的对象
*/
public static void copyNonNull(@Nullable Object source, @Nullable Object targetBean) {
if (source == null || targetBean == null) {
return;
}
BladeBeanCopier copier = BladeBeanCopier
.create(source.getClass(), targetBean.getClass(), false, true);
copier.copy(source, targetBean, null);
}
/**
* 拷贝对象并对不同类型属性进行转换
*
* <p>
* 支持 map bean copy
* </p>
*
* @param source 源对象
* @param targetClazz 转换成的类
* @param <T> 泛型标记
* @return T
*/
@Nullable
public static <T> T copyWithConvert(@Nullable Object source, Class<T> targetClazz) {
if (source == null) {
return null;
}
return BeanUtil.copyWithConvert(source, source.getClass(), targetClazz);
}
/**
* 拷贝对象并对不同类型属性进行转换
*
* <p>
* 支持 map bean copy
* </p>
*
* @param source 源对象
* @param sourceClazz 源类
* @param targetClazz 转换成的类
* @param <T> 泛型标记
* @return T
*/
@Nullable
public static <T> T copyWithConvert(@Nullable Object source, Class<?> sourceClazz, Class<T> targetClazz) {
if (source == null) {
return null;
}
BladeBeanCopier copier = BladeBeanCopier.create(sourceClazz, targetClazz, true);
T to = newInstance(targetClazz);
copier.copy(source, to, new BladeConverter(sourceClazz, targetClazz));
return to;
}
/**
* 拷贝列表并对不同类型属性进行转换
*
* <p>
* 支持 map bean copy
* </p>
*
* @param sourceList 源对象列表
* @param targetClazz 转换成的类
* @param <T> 泛型标记
* @return List
*/
public static <T> List<T> copyWithConvert(@Nullable Collection<?> sourceList, Class<T> targetClazz) {
if (sourceList == null || sourceList.isEmpty()) {
return Collections.emptyList();
}
List<T> outList = new ArrayList<>(sourceList.size());
Class<?> sourceClazz = null;
for (Object source : sourceList) {
if (source == null) {
continue;
}
if (sourceClazz == null) {
sourceClazz = source.getClass();
}
T bean = BeanUtil.copyWithConvert(source, sourceClazz, targetClazz);
outList.add(bean);
}
return outList;
}
/**
* Copy the property values of the given source bean into the target class.
* <p>Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
*
* @param source the source bean
* @param targetClazz the target bean class
* @param <T> 泛型标记
* @return T
* @throws BeansException if the copying failed
*/
@Nullable
public static <T> T copyProperties(@Nullable Object source, Class<T> targetClazz) throws BeansException {
if (source == null) {
return null;
}
T to = newInstance(targetClazz);
BeanUtil.copyProperties(source, to);
return to;
}
/**
* Copy the property values of the given source bean into the target class.
* <p>Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
*
* @param sourceList the source list bean
* @param targetClazz the target bean class
* @param <T> 泛型标记
* @return List
* @throws BeansException if the copying failed
*/
public static <T> List<T> copyProperties(@Nullable Collection<?> sourceList, Class<T> targetClazz) throws BeansException {
if (sourceList == null || sourceList.isEmpty()) {
return Collections.emptyList();
}
List<T> outList = new ArrayList<>(sourceList.size());
for (Object source : sourceList) {
if (source == null) {
continue;
}
T bean = BeanUtil.copyProperties(source, targetClazz);
outList.add(bean);
}
return outList;
}
/**
* 将对象装成map形式
*
* @param bean 源对象
* @return {Map}
*/
@SuppressWarnings("unchecked")
public static Map<String, Object> toMap(@Nullable Object bean) {
if (bean == null) {
return new HashMap<>(0);
}
return BladeBeanMap.create(bean);
}
/**
* 将map 转为 bean
*
* @param beanMap map
* @param valueType 对象类型
* @param <T> 泛型标记
* @return {T}
*/
public static <T> T toBean(Map<String, Object> beanMap, Class<T> valueType) {
Objects.requireNonNull(beanMap, "beanMap Could not null");
T to = newInstance(valueType);
if (beanMap.isEmpty()) {
return to;
}
BeanUtil.copy(beanMap, to);
return to;
}
/**
* 给一个Bean添加字段
*
* @param superBean 父级Bean
* @param props 新增属性
* @return {Object}
*/
@Nullable
public static Object generator(@Nullable Object superBean, BeanProperty... props) {
if (superBean == null) {
return null;
}
Class<?> superclass = superBean.getClass();
Object genBean = generator(superclass, props);
BeanUtil.copy(superBean, genBean);
return genBean;
}
/**
* 给一个class添加字段
*
* @param superclass 父级
* @param props 新增属性
* @return {Object}
*/
public static Object generator(Class<?> superclass, BeanProperty... props) {
BeanGenerator generator = new BeanGenerator();
generator.setSuperclass(superclass);
generator.setUseCache(true);
for (BeanProperty prop : props) {
generator.addProperty(prop.getName(), prop.getType());
}
return generator.create();
}
}
@@ -0,0 +1,83 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
/**
* char 常量池
*
* @author L.cm
*/
public interface CharPool {
// @formatter:off
char UPPER_A = 'A';
char LOWER_A = 'a';
char UPPER_Z = 'Z';
char LOWER_Z = 'z';
char DOT = '.';
char AT = '@';
char LEFT_BRACE = '{';
char RIGHT_BRACE = '}';
char LEFT_BRACKET = '(';
char RIGHT_BRACKET = ')';
char DASH = '-';
char PERCENT = '%';
char PIPE = '|';
char PLUS = '+';
char QUESTION_MARK = '?';
char EXCLAMATION_MARK = '!';
char EQUALS = '=';
char AMPERSAND = '&';
char ASTERISK = '*';
char STAR = ASTERISK;
char BACK_SLASH = '\\';
char COLON = ':';
char COMMA = ',';
char DOLLAR = '$';
char SLASH = '/';
char HASH = '#';
char HAT = '^';
char LEFT_CHEV = '<';
char NEWLINE = '\n';
char N = 'n';
char Y = 'y';
char QUOTE = '\"';
char RETURN = '\r';
char TAB = '\t';
char RIGHT_CHEV = '>';
char SEMICOLON = ';';
char SINGLE_QUOTE = '\'';
char BACKTICK = '`';
char SPACE = ' ';
char TILDA = '~';
char LEFT_SQ_BRACKET = '[';
char RIGHT_SQ_BRACKET = ']';
char UNDERSCORE = '_';
char ONE = '1';
char ZERO = '0';
// @formatter:on
}
@@ -0,0 +1,69 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
/**
* 字符集工具类
*
* @author L.cm
*/
public class Charsets {
/**
* 字符集ISO-8859-1
*/
public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
public static final String ISO_8859_1_NAME = ISO_8859_1.name();
/**
* 字符集GBK
*/
public static final Charset GBK = Charset.forName(StringPool.GBK);
public static final String GBK_NAME = GBK.name();
/**
* 字符集utf-8
*/
public static final Charset UTF_8 = StandardCharsets.UTF_8;
public static final String UTF_8_NAME = UTF_8.name();
/**
* 转换为Charset对象
*
* @param charsetName 字符集为空则返回默认字符集
* @return Charsets
* @throws UnsupportedCharsetException 编码不支持
*/
public static Charset charset(String charsetName) throws UnsupportedCharsetException {
return StringUtil.isBlank(charsetName) ? Charset.defaultCharset() : Charset.forName(charsetName);
}
}
@@ -0,0 +1,181 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.web.method.HandlerMethod;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 类操作工具
*
* @author L.cm
*/
public class ClassUtil extends org.springframework.util.ClassUtils {
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
/**
* 获取方法参数信息
*
* @param constructor 构造器
* @param parameterIndex 参数序号
* @return {MethodParameter}
*/
public static MethodParameter getMethodParameter(Constructor<?> constructor, int parameterIndex) {
MethodParameter methodParameter = new SynthesizingMethodParameter(constructor, parameterIndex);
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
return methodParameter;
}
/**
* 获取方法参数信息
*
* @param method 方法
* @param parameterIndex 参数序号
* @return {MethodParameter}
*/
public static MethodParameter getMethodParameter(Method method, int parameterIndex) {
MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex);
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
return methodParameter;
}
/**
* 获取Annotation
*
* @param method Method
* @param annotationType 注解类
* @param <A> 泛型标记
* @return {Annotation}
*/
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
Class<?> targetClass = method.getDeclaringClass();
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
Method specificMethod = ClassUtil.getMostSpecificMethod(method, targetClass);
// If we are dealing with method with generic parameters, find the original method.
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// 先找方法再找方法上的类
A annotation = AnnotatedElementUtils.findMergedAnnotation(specificMethod, annotationType);
;
if (null != annotation) {
return annotation;
}
// 获取类上面的Annotation可能包含组合注解故采用spring的工具类
return AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), annotationType);
}
/**
* 获取Annotation先找方法再找方法上的类
*
* @param point AOP切点
* @param annotationType 注解类
* @param <A> 泛型标记
* @return {Annotation}
*/
public static <A extends Annotation> A getAnnotation(ProceedingJoinPoint point, Class<A> annotationType) {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
return getAnnotation(methodSignature.getMethod(), annotationType);
}
/**
* 获取Annotation
*
* @param handlerMethod HandlerMethod
* @param annotationType 注解类
* @param <A> 泛型标记
* @return {Annotation}
*/
public static <A extends Annotation> A getAnnotation(HandlerMethod handlerMethod, Class<A> annotationType) {
// 先找方法再找方法上的类
A annotation = handlerMethod.getMethodAnnotation(annotationType);
if (null != annotation) {
return annotation;
}
// 获取类上面的Annotation可能包含组合注解故采用spring的工具类
Class<?> beanType = handlerMethod.getBeanType();
return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType);
}
/**
* 判断是否有注解 Annotation
*
* @param method Method
* @param annotationType 注解类
* @param <A> 泛型标记
* @return {boolean}
*/
public static <A extends Annotation> boolean isAnnotated(Method method, Class<A> annotationType) {
// 先找方法再找方法上的类
boolean isMethodAnnotated = AnnotatedElementUtils.isAnnotated(method, annotationType);
if (isMethodAnnotated) {
return true;
}
// 获取类上面的Annotation可能包含组合注解故采用spring的工具类
Class<?> targetClass = method.getDeclaringClass();
return AnnotatedElementUtils.isAnnotated(targetClass, annotationType);
}
/**
* 获取类的字段名合集
*
* @param clazz
* @param <T> 泛型标记
* @return 字段名合集
*/
public static <T> Set<String> getClassFieldNames(Class<T> clazz) {
Set<String> fieldNames = new HashSet<>();
// 循环遍历 clazz 及其父类获取字段
while (clazz != null && clazz != Object.class) {
Field[] fields = clazz.getDeclaredFields(); // 获取当前类的所有字段
fieldNames.addAll(
Set.of(fields) // 使用 Set.of() 创建不可变集合
.stream()
.map(Field::getName) // 提取字段名
.collect(Collectors.toSet()) // 收集成 Set
);
clazz = (Class<T>) clazz.getSuperclass(); // 获取父类
}
return fieldNames;
}
}
@@ -0,0 +1,186 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import java.lang.reflect.Array;
import java.util.*;
import java.util.stream.Collectors;
/**
* 集合工具类
*
* @author L.cm
*/
public class CollectionUtil extends CollectionUtils {
/**
* Return {@code true} if the supplied Collection is not {@code null} or empty.
* Otherwise, return {@code false}.
*
* @param collection the Collection to check
* @return whether the given Collection is not empty
*/
public static boolean isNotEmpty(@Nullable Collection<?> collection) {
return !CollectionUtil.isEmpty(collection);
}
/**
* Return {@code true} if the supplied Map is not {@code null} or empty.
* Otherwise, return {@code false}.
*
* @param map the Map to check
* @return whether the given Map is not empty
*/
public static boolean isNotEmpty(@Nullable Map<?, ?> map) {
return !CollectionUtil.isEmpty(map);
}
/**
* Check whether the given Array contains the given element.
*
* @param array the Array to check
* @param element the element to look for
* @param <T> The generic tag
* @return {@code true} if found, {@code false} else
*/
public static <T> boolean contains(@Nullable T[] array, final T element) {
if (array == null) {
return false;
}
return Arrays.stream(array).anyMatch(x -> ObjectUtil.nullSafeEquals(x, element));
}
/**
* Concatenates 2 arrays
*
* @param one 数组1
* @param other 数组2
* @return 新数组
*/
public static String[] concat(String[] one, String[] other) {
return concat(one, other, String.class);
}
/**
* Concatenates 2 arrays
*
* @param one 数组1
* @param other 数组2
* @param clazz 数组类
* @return 新数组
*/
public static <T> T[] concat(T[] one, T[] other, Class<T> clazz) {
T[] target = (T[]) Array.newInstance(clazz, one.length + other.length);
System.arraycopy(one, 0, target, 0, one.length);
System.arraycopy(other, 0, target, one.length, other.length);
return target;
}
/**
* 对象是否为数组对象
*
* @param obj 对象
* @return 是否为数组对象如果为{@code null} 返回false
*/
public static boolean isArray(Object obj) {
if (null == obj) {
return false;
}
return obj.getClass().isArray();
}
/**
* 不可变 Set
*
* @param es 对象
* @param <E> 泛型
* @return 集合
*/
@SafeVarargs
public static <E> Set<E> ofImmutableSet(E... es) {
Objects.requireNonNull(es, "args es is null.");
return Arrays.stream(es).collect(Collectors.toSet());
}
/**
* 不可变 List
*
* @param es 对象
* @param <E> 泛型
* @return 集合
*/
@SafeVarargs
public static <E> List<E> ofImmutableList(E... es) {
Objects.requireNonNull(es, "args es is null.");
return Arrays.stream(es).collect(Collectors.toList());
}
/**
* Iterable 转换为List集合
*
* @param elements Iterable
* @param <E> 泛型
* @return 集合
*/
public static <E> List<E> toList(Iterable<E> elements) {
Objects.requireNonNull(elements, "elements es is null.");
if (elements instanceof Collection) {
return new ArrayList((Collection) elements);
}
Iterator<E> iterator = elements.iterator();
List<E> list = new ArrayList<>();
while (iterator.hasNext()) {
list.add(iterator.next());
}
return list;
}
/**
* 将key value 数组转为 map
*
* @param keysValues key value 数组
* @param <K> key
* @param <V> value
* @return map 集合
*/
public static <K, V> Map<K, V> toMap(Object... keysValues) {
int kvLength = keysValues.length;
if (kvLength % 2 != 0) {
throw new IllegalArgumentException("wrong number of arguments for met, keysValues length can not be odd");
}
Map<K, V> keyValueMap = new HashMap<>(kvLength);
for (int i = kvLength - 2; i >= 0; i -= 2) {
Object key = keysValues[i];
Object value = keysValues[i + 1];
keyValueMap.put((K) key, (V) value);
}
return keyValueMap;
}
}
@@ -0,0 +1,96 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Queue;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
* 参考tomcat8中的并发DateFormat
* <p>
* {@link SimpleDateFormat}的线程安全包装器
* 不使用ThreadLocal创建足够的SimpleDateFormat对象来满足并发性要求
* </p>
*
* @author L.cm
*/
public class ConcurrentDateFormat {
private final String format;
private final Locale locale;
private final TimeZone timezone;
private final Queue<SimpleDateFormat> queue = new ConcurrentLinkedQueue<>();
private ConcurrentDateFormat(String format, Locale locale, TimeZone timezone) {
this.format = format;
this.locale = locale;
this.timezone = timezone;
SimpleDateFormat initial = createInstance();
queue.add(initial);
}
public static ConcurrentDateFormat of(String format) {
return new ConcurrentDateFormat(format, Locale.getDefault(), TimeZone.getDefault());
}
public static ConcurrentDateFormat of(String format, TimeZone timezone) {
return new ConcurrentDateFormat(format, Locale.getDefault(), timezone);
}
public static ConcurrentDateFormat of(String format, Locale locale, TimeZone timezone) {
return new ConcurrentDateFormat(format, locale, timezone);
}
public String format(Date date) {
SimpleDateFormat sdf = queue.poll();
if (sdf == null) {
sdf = createInstance();
}
String result = sdf.format(date);
queue.add(sdf);
return result;
}
public Date parse(String source) throws ParseException {
SimpleDateFormat sdf = queue.poll();
if (sdf == null) {
sdf = createInstance();
}
Date result = sdf.parse(source);
queue.add(sdf);
return result;
}
private SimpleDateFormat createInstance() {
SimpleDateFormat sdf = new SimpleDateFormat(format, locale);
sdf.setTimeZone(timezone);
return sdf;
}
}
@@ -0,0 +1,81 @@
package org.springblade.core.tool.utils;
import org.springblade.core.tool.convert.BladeConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.lang.Nullable;
/**
* 基于 spring ConversionService 类型转换
*
* @author L.cm
*/
@SuppressWarnings("unchecked")
public class ConvertUtil {
/**
* Convenience operation for converting a source object to the specified targetType.
* {@link TypeDescriptor#forObject(Object)}.
* @param source the source object
* @param targetType the target type
* @param <T> 泛型标记
* @return the converted value
* @throws IllegalArgumentException if targetType is {@code null},
* or sourceType is {@code null} but source is not {@code null}
*/
@Nullable
public static <T> T convert(@Nullable Object source, Class<T> targetType) {
if (source == null) {
return null;
}
if (ClassUtil.isAssignableValue(targetType, source)) {
return (T) source;
}
GenericConversionService conversionService = BladeConversionService.getInstance();
return conversionService.convert(source, targetType);
}
/**
* Convenience operation for converting a source object to the specified targetType,
* where the target type is a descriptor that provides additional conversion context.
* {@link TypeDescriptor#forObject(Object)}.
* @param source the source object
* @param sourceType the source type
* @param targetType the target type
* @param <T> 泛型标记
* @return the converted value
* @throws IllegalArgumentException if targetType is {@code null},
* or sourceType is {@code null} but source is not {@code null}
*/
@Nullable
public static <T> T convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
GenericConversionService conversionService = BladeConversionService.getInstance();
return (T) conversionService.convert(source, sourceType, targetType);
}
/**
* Convenience operation for converting a source object to the specified targetType,
* where the target type is a descriptor that provides additional conversion context.
* Simply delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)} and
* encapsulates the construction of the source type descriptor using
* {@link TypeDescriptor#forObject(Object)}.
* @param source the source object
* @param targetType the target type
* @param <T> 泛型标记
* @return the converted value
* @throws IllegalArgumentException if targetType is {@code null},
* or sourceType is {@code null} but source is not {@code null}
*/
@Nullable
public static <T> T convert(@Nullable Object source, TypeDescriptor targetType) {
if (source == null) {
return null;
}
GenericConversionService conversionService = BladeConversionService.getInstance();
return (T) conversionService.convert(source, targetType);
}
}
@@ -0,0 +1,57 @@
package org.springblade.core.tool.utils;
/**
* 数据类型转换工具类
*
* @author Chill
*/
public class DatatypeConverterUtil {
/**
* hex文本转换为二进制
*
* @param hexStr hex文本
* @return byte[]
*/
public static byte[] parseHexBinary(String hexStr) {
final int len = hexStr.length();
if (len % 2 != 0) {
throw new IllegalArgumentException("hexBinary needs to be even-length: " + hexStr);
}
byte[] out = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
int h = hexToBin(hexStr.charAt(i));
int l = hexToBin(hexStr.charAt(i + 1));
if (h == -1 || l == -1) {
throw new IllegalArgumentException("contains illegal character for hexBinary: " + hexStr);
}
out[i / 2] = (byte) (h * 16 + l);
}
return out;
}
/**
* hex文本转换为int
*
* @param ch hex文本
* @return int
*/
private static int hexToBin(char ch) {
if ('0' <= ch && ch <= '9') {
return ch - '0';
}
if ('A' <= ch && ch <= 'F') {
return ch - 'A' + 10;
}
if ('a' <= ch && ch <= 'f') {
return ch - 'a' + 10;
}
return -1;
}
}
@@ -0,0 +1,235 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
/**
* DateTime 工具类
*
* @author L.cm
*/
public class DateTimeUtil {
public static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME);
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE);
public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME);
/**
* 日期时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatDateTime(TemporalAccessor temporal) {
return DATETIME_FORMAT.format(temporal);
}
/**
* 日期时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatDate(TemporalAccessor temporal) {
return DATE_FORMAT.format(temporal);
}
/**
* 时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatTime(TemporalAccessor temporal) {
return TIME_FORMAT.format(temporal);
}
/**
* 日期格式化
*
* @param temporal 时间
* @param pattern 表达式
* @return 格式化后的时间
*/
public static String format(TemporalAccessor temporal, String pattern) {
return DateTimeFormatter.ofPattern(pattern).format(temporal);
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param pattern 表达式
* @return 时间
*/
public static LocalDateTime parseDateTime(String dateStr, String pattern) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return DateTimeUtil.parseDateTime(dateStr, formatter);
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param formatter DateTimeFormatter
* @return 时间
*/
public static LocalDateTime parseDateTime(String dateStr, DateTimeFormatter formatter) {
return LocalDateTime.parse(dateStr, formatter);
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @return 时间
*/
public static LocalDateTime parseDateTime(String dateStr) {
return DateTimeUtil.parseDateTime(dateStr, DateTimeUtil.DATETIME_FORMAT);
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param pattern 表达式
* @return 时间
*/
public static LocalDate parseDate(String dateStr, String pattern) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return DateTimeUtil.parseDate(dateStr, formatter);
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param formatter DateTimeFormatter
* @return 时间
*/
public static LocalDate parseDate(String dateStr, DateTimeFormatter formatter) {
return LocalDate.parse(dateStr, formatter);
}
/**
* 将字符串转换为日期
*
* @param dateStr 时间字符串
* @return 时间
*/
public static LocalDate parseDate(String dateStr) {
return DateTimeUtil.parseDate(dateStr, DateTimeUtil.DATE_FORMAT);
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param pattern 时间正则
* @return 时间
*/
public static LocalTime parseTime(String dateStr, String pattern) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return DateTimeUtil.parseTime(dateStr, formatter);
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param formatter DateTimeFormatter
* @return 时间
*/
public static LocalTime parseTime(String dateStr, DateTimeFormatter formatter) {
return LocalTime.parse(dateStr, formatter);
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @return 时间
*/
public static LocalTime parseTime(String dateStr) {
return DateTimeUtil.parseTime(dateStr, DateTimeUtil.TIME_FORMAT);
}
/**
* 时间转 Instant
*
* @param dateTime 时间
* @return Instant
*/
public static Instant toInstant(LocalDateTime dateTime) {
return dateTime.atZone(ZoneId.systemDefault()).toInstant();
}
/**
* Instant 时间
*
* @param instant Instant
* @return Instant
*/
public static LocalDateTime toDateTime(Instant instant) {
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
/**
* 转换成 date
*
* @param dateTime LocalDateTime
* @return Date
*/
public static Date toDate(LocalDateTime dateTime) {
return Date.from(DateTimeUtil.toInstant(dateTime));
}
/**
* 比较2个时间差跨度比较小
*
* @param startInclusive 开始时间
* @param endExclusive 结束时间
* @return 时间间隔
*/
public static Duration between(Temporal startInclusive, Temporal endExclusive) {
return Duration.between(startInclusive, endExclusive);
}
/**
* 比较2个时间差跨度比较大年月日为单位
*
* @param startDate 开始时间
* @param endDate 结束时间
* @return 时间间隔
*/
public static Period between(LocalDate startDate, LocalDate endDate) {
return Period.between(startDate, endDate);
}
}
@@ -0,0 +1,634 @@
package org.springblade.core.tool.utils;
import org.springframework.util.Assert;
import java.text.ParseException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalAmount;
import java.time.temporal.TemporalQuery;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
/**
* 日期工具类
*
* @author L.cm
*/
public class DateUtil {
public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
public static final String PATTERN_DATETIME_MINI = "yyyyMMddHHmmss";
public static final String PATTERN_DATE = "yyyy-MM-dd";
public static final String PATTERN_TIME = "HH:mm:ss";
/**
* date 格式化
*/
public static final ConcurrentDateFormat DATETIME_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME);
public static final ConcurrentDateFormat DATETIME_MINI_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME_MINI);
public static final ConcurrentDateFormat DATE_FORMAT = ConcurrentDateFormat.of(PATTERN_DATE);
public static final ConcurrentDateFormat TIME_FORMAT = ConcurrentDateFormat.of(PATTERN_TIME);
/**
* java 8 时间格式化
*/
public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATETIME);
public static final DateTimeFormatter DATETIME_MINI_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATETIME_MINI);
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATE);
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_TIME);
/**
* 获取当前日期
*
* @return 当前日期
*/
public static Date now() {
return new Date();
}
/**
* 添加年
*
* @param date 时间
* @param yearsToAdd 添加的年数
* @return 设置后的时间
*/
public static Date plusYears(Date date, int yearsToAdd) {
return DateUtil.set(date, Calendar.YEAR, yearsToAdd);
}
/**
* 添加月
*
* @param date 时间
* @param monthsToAdd 添加的月数
* @return 设置后的时间
*/
public static Date plusMonths(Date date, int monthsToAdd) {
return DateUtil.set(date, Calendar.MONTH, monthsToAdd);
}
/**
* 添加周
*
* @param date 时间
* @param weeksToAdd 添加的周数
* @return 设置后的时间
*/
public static Date plusWeeks(Date date, int weeksToAdd) {
return DateUtil.plus(date, Period.ofWeeks(weeksToAdd));
}
/**
* 添加天
*
* @param date 时间
* @param daysToAdd 添加的天数
* @return 设置后的时间
*/
public static Date plusDays(Date date, long daysToAdd) {
return DateUtil.plus(date, Duration.ofDays(daysToAdd));
}
/**
* 添加小时
*
* @param date 时间
* @param hoursToAdd 添加的小时数
* @return 设置后的时间
*/
public static Date plusHours(Date date, long hoursToAdd) {
return DateUtil.plus(date, Duration.ofHours(hoursToAdd));
}
/**
* 添加分钟
*
* @param date 时间
* @param minutesToAdd 添加的分钟数
* @return 设置后的时间
*/
public static Date plusMinutes(Date date, long minutesToAdd) {
return DateUtil.plus(date, Duration.ofMinutes(minutesToAdd));
}
/**
* 添加秒
*
* @param date 时间
* @param secondsToAdd 添加的秒数
* @return 设置后的时间
*/
public static Date plusSeconds(Date date, long secondsToAdd) {
return DateUtil.plus(date, Duration.ofSeconds(secondsToAdd));
}
/**
* 添加毫秒
*
* @param date 时间
* @param millisToAdd 添加的毫秒数
* @return 设置后的时间
*/
public static Date plusMillis(Date date, long millisToAdd) {
return DateUtil.plus(date, Duration.ofMillis(millisToAdd));
}
/**
* 添加纳秒
*
* @param date 时间
* @param nanosToAdd 添加的纳秒数
* @return 设置后的时间
*/
public static Date plusNanos(Date date, long nanosToAdd) {
return DateUtil.plus(date, Duration.ofNanos(nanosToAdd));
}
/**
* 日期添加时间量
*
* @param date 时间
* @param amount 时间量
* @return 设置后的时间
*/
public static Date plus(Date date, TemporalAmount amount) {
Instant instant = date.toInstant();
return Date.from(instant.plus(amount));
}
/**
* 减少年
*
* @param date 时间
* @param years 减少的年数
* @return 设置后的时间
*/
public static Date minusYears(Date date, int years) {
return DateUtil.set(date, Calendar.YEAR, -years);
}
/**
* 减少月
*
* @param date 时间
* @param months 减少的月数
* @return 设置后的时间
*/
public static Date minusMonths(Date date, int months) {
return DateUtil.set(date, Calendar.MONTH, -months);
}
/**
* 减少周
*
* @param date 时间
* @param weeks 减少的周数
* @return 设置后的时间
*/
public static Date minusWeeks(Date date, int weeks) {
return DateUtil.minus(date, Period.ofWeeks(weeks));
}
/**
* 减少天
*
* @param date 时间
* @param days 减少的天数
* @return 设置后的时间
*/
public static Date minusDays(Date date, long days) {
return DateUtil.minus(date, Duration.ofDays(days));
}
/**
* 减少小时
*
* @param date 时间
* @param hours 减少的小时数
* @return 设置后的时间
*/
public static Date minusHours(Date date, long hours) {
return DateUtil.minus(date, Duration.ofHours(hours));
}
/**
* 减少分钟
*
* @param date 时间
* @param minutes 减少的分钟数
* @return 设置后的时间
*/
public static Date minusMinutes(Date date, long minutes) {
return DateUtil.minus(date, Duration.ofMinutes(minutes));
}
/**
* 减少秒
*
* @param date 时间
* @param seconds 减少的秒数
* @return 设置后的时间
*/
public static Date minusSeconds(Date date, long seconds) {
return DateUtil.minus(date, Duration.ofSeconds(seconds));
}
/**
* 减少毫秒
*
* @param date 时间
* @param millis 减少的毫秒数
* @return 设置后的时间
*/
public static Date minusMillis(Date date, long millis) {
return DateUtil.minus(date, Duration.ofMillis(millis));
}
/**
* 减少纳秒
*
* @param date 时间
* @param nanos 减少的纳秒数
* @return 设置后的时间
*/
public static Date minusNanos(Date date, long nanos) {
return DateUtil.minus(date, Duration.ofNanos(nanos));
}
/**
* 日期减少时间量
*
* @param date 时间
* @param amount 时间量
* @return 设置后的时间
*/
public static Date minus(Date date, TemporalAmount amount) {
Instant instant = date.toInstant();
return Date.from(instant.minus(amount));
}
/**
* 设置日期属性
*
* @param date 时间
* @param calendarField 更改的属性
* @param amount 更改数-1表示减少
* @return 设置后的时间
*/
private static Date set(Date date, int calendarField, int amount) {
Assert.notNull(date, "The date must not be null");
Calendar c = Calendar.getInstance();
c.setLenient(false);
c.setTime(date);
c.add(calendarField, amount);
return c.getTime();
}
/**
* 日期时间格式化
*
* @param date 时间
* @return 格式化后的时间
*/
public static String formatDateTime(Date date) {
return DATETIME_FORMAT.format(date);
}
/**
* 日期时间格式化
*
* @param date 时间
* @return 格式化后的时间
*/
public static String formatDateTimeMini(Date date) {
return DATETIME_MINI_FORMAT.format(date);
}
/**
* 日期格式化
*
* @param date 时间
* @return 格式化后的时间
*/
public static String formatDate(Date date) {
return DATE_FORMAT.format(date);
}
/**
* 时间格式化
*
* @param date 时间
* @return 格式化后的时间
*/
public static String formatTime(Date date) {
return TIME_FORMAT.format(date);
}
/**
* 日期格式化
*
* @param date 时间
* @param pattern 表达式
* @return 格式化后的时间
*/
public static String format(Date date, String pattern) {
return ConcurrentDateFormat.of(pattern).format(date);
}
/**
* java8 日期时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatDateTime(TemporalAccessor temporal) {
return DATETIME_FORMATTER.format(temporal);
}
/**
* java8 日期时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatDateTimeMini(TemporalAccessor temporal) {
return DATETIME_MINI_FORMATTER.format(temporal);
}
/**
* java8 日期时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatDate(TemporalAccessor temporal) {
return DATE_FORMATTER.format(temporal);
}
/**
* java8 时间格式化
*
* @param temporal 时间
* @return 格式化后的时间
*/
public static String formatTime(TemporalAccessor temporal) {
return TIME_FORMATTER.format(temporal);
}
/**
* java8 日期格式化
*
* @param temporal 时间
* @param pattern 表达式
* @return 格式化后的时间
*/
public static String format(TemporalAccessor temporal, String pattern) {
return DateTimeFormatter.ofPattern(pattern).format(temporal);
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param pattern 表达式
* @return 时间
*/
public static Date parse(String dateStr, String pattern) {
ConcurrentDateFormat format = ConcurrentDateFormat.of(pattern);
try {
return format.parse(dateStr);
} catch (ParseException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param format ConcurrentDateFormat
* @return 时间
*/
public static Date parse(String dateStr, ConcurrentDateFormat format) {
try {
return format.parse(dateStr);
} catch (ParseException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 将字符串转换为时间
*
* @param dateStr 时间字符串
* @param pattern 表达式
* @return 时间
*/
public static <T> T parse(String dateStr, String pattern, TemporalQuery<T> query) {
return DateTimeFormatter.ofPattern(pattern).parse(dateStr, query);
}
/**
* 时间转 Instant
*
* @param dateTime 时间
* @return Instant
*/
public static Instant toInstant(LocalDateTime dateTime) {
return dateTime.atZone(ZoneId.systemDefault()).toInstant();
}
/**
* Instant 时间
*
* @param instant Instant
* @return Instant
*/
public static LocalDateTime toDateTime(Instant instant) {
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
/**
* 转换成 date
*
* @param dateTime LocalDateTime
* @return Date
*/
public static Date toDate(LocalDateTime dateTime) {
return Date.from(DateUtil.toInstant(dateTime));
}
/**
* 转换成 date
*
* @param localDate LocalDate
* @return Date
*/
public static Date toDate(final LocalDate localDate) {
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
}
/**
* Converts local date time to Calendar.
*/
public static Calendar toCalendar(final LocalDateTime localDateTime) {
return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));
}
/**
* localDateTime 转换成毫秒数
*
* @param localDateTime LocalDateTime
* @return long
*/
public static long toMilliseconds(final LocalDateTime localDateTime) {
return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
/**
* localDate 转换成毫秒数
*
* @param localDate LocalDate
* @return long
*/
public static long toMilliseconds(LocalDate localDate) {
return toMilliseconds(localDate.atStartOfDay());
}
/**
* 转换成java8 时间
*
* @param calendar 日历
* @return LocalDateTime
*/
public static LocalDateTime fromCalendar(final Calendar calendar) {
TimeZone tz = calendar.getTimeZone();
ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId();
return LocalDateTime.ofInstant(calendar.toInstant(), zid);
}
/**
* 转换成java8 时间
*
* @param instant Instant
* @return LocalDateTime
*/
public static LocalDateTime fromInstant(final Instant instant) {
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
/**
* 转换成java8 时间
*
* @param date Date
* @return LocalDateTime
*/
public static LocalDateTime fromDate(final Date date) {
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
/**
* 转换成java8 时间
*
* @param milliseconds 毫秒数
* @return LocalDateTime
*/
public static LocalDateTime fromMilliseconds(final long milliseconds) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault());
}
/**
* 比较2个时间差跨度比较小
*
* @param startInclusive 开始时间
* @param endExclusive 结束时间
* @return 时间间隔
*/
public static Duration between(Temporal startInclusive, Temporal endExclusive) {
return Duration.between(startInclusive, endExclusive);
}
/**
* 比较2个时间差跨度比较大年月日为单位
*
* @param startDate 开始时间
* @param endDate 结束时间
* @return 时间间隔
*/
public static Period between(LocalDate startDate, LocalDate endDate) {
return Period.between(startDate, endDate);
}
/**
* 比较2个 时间差
*
* @param startDate 开始时间
* @param endDate 结束时间
* @return 时间间隔
*/
public static Duration between(Date startDate, Date endDate) {
return Duration.between(startDate.toInstant(), endDate.toInstant());
}
/**
* 将秒数转换为日时分秒
*
* @param second 秒数
* @return 时间
*/
public static String secondToTime(Long second) {
// 判断是否为空
if (second == null || second == 0L) {
return StringPool.EMPTY;
}
//转换天数
long days = second / 86400;
//剩余秒数
second = second % 86400;
//转换小时
long hours = second / 3600;
//剩余秒数
second = second % 3600;
//转换分钟
long minutes = second / 60;
//剩余秒数
second = second % 60;
if (days > 0) {
return StringUtil.format("{}天{}小时{}分{}秒", days, hours, minutes, second);
} else {
return StringUtil.format("{}小时{}分{}秒", hours, minutes, second);
}
}
/**
* 获取今天的日期
*
* @return 时间
*/
public static String today() {
return format(now(), "yyyyMMdd");
}
/**
* 获取今天的时间
*
* @return 时间
*/
public static String time() {
return format(now(), PATTERN_DATETIME_MINI);
}
/**
* 获取今天的小时数
*
* @return 时间
*/
public static Integer hour() {
return NumberUtil.toInt(format(now(), "HH"));
}
}
@@ -0,0 +1,217 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.util.Objects;
/**
* DES加解密处理工具
*
* @author L.cm
*/
public class DesUtil {
/**
* 数字签名密钥算法
*/
public static final String DES_ALGORITHM = "DES";
/**
* 生成 des 密钥
*
* @return 密钥
*/
public static String genDesKey() {
return StringUtil.random(16);
}
/**
* DES加密
*
* @param data byte array
* @param password 密钥
* @return des hex
*/
public static String encryptToHex(byte[] data, String password) {
return HexUtil.encodeToString(encrypt(data, password));
}
/**
* DES加密
*
* @param data 字符串内容
* @param password 密钥
* @return des hex
*/
@Nullable
public static String encryptToHex(@Nullable String data, String password) {
if (StringUtil.isBlank(data)) {
return null;
}
byte[] dataBytes = data.getBytes(Charsets.UTF_8);
return encryptToHex(dataBytes, password);
}
/**
* DES解密
*
* @param data 字符串内容
* @param password 密钥
* @return des context
*/
@Nullable
public static String decryptFormHex(@Nullable String data, String password) {
if (StringUtil.isBlank(data)) {
return null;
}
byte[] hexBytes = HexUtil.decode(data);
return new String(decrypt(hexBytes, password), Charsets.UTF_8);
}
/**
* DES加密
*
* @param data byte array
* @param password 密钥
* @return des hex
*/
public static String encryptToBase64(byte[] data, String password) {
return Base64Util.encodeToString(encrypt(data, password));
}
/**
* DES加密
*
* @param data 字符串内容
* @param password 密钥
* @return des hex
*/
@Nullable
public static String encryptToBase64(@Nullable String data, String password) {
if (StringUtil.isBlank(data)) {
return null;
}
byte[] dataBytes = data.getBytes(Charsets.UTF_8);
return encryptToBase64(dataBytes, password);
}
/**
* DES解密
*
* @param data 字符串内容
* @param password 密钥
* @return des context
*/
public static byte[] decryptFormBase64(byte[] data, String password) {
byte[] dataBytes = Base64Util.decode(data);
return decrypt(dataBytes, password);
}
/**
* DES解密
*
* @param data 字符串内容
* @param password 密钥
* @return des context
*/
@Nullable
public static String decryptFormBase64(@Nullable String data, String password) {
if (StringUtil.isBlank(data)) {
return null;
}
byte[] dataBytes = Base64Util.decodeFromString(data);
return new String(decrypt(dataBytes, password), Charsets.UTF_8);
}
/**
* DES加密
*
* @param data 内容
* @param desKey 密钥
* @return byte array
*/
public static byte[] encrypt(byte[] data, byte[] desKey) {
return des(data, desKey, Cipher.ENCRYPT_MODE);
}
/**
* DES加密
*
* @param data 内容
* @param desKey 密钥
* @return byte array
*/
public static byte[] encrypt(byte[] data, String desKey) {
return encrypt(data, Objects.requireNonNull(desKey).getBytes(Charsets.UTF_8));
}
/**
* DES解密
*
* @param data 内容
* @param desKey 密钥
* @return byte array
*/
public static byte[] decrypt(byte[] data, byte[] desKey) {
return des(data, desKey, Cipher.DECRYPT_MODE);
}
/**
* DES解密
*
* @param data 内容
* @param desKey 密钥
* @return byte array
*/
public static byte[] decrypt(byte[] data, String desKey) {
return decrypt(data, Objects.requireNonNull(desKey).getBytes(Charsets.UTF_8));
}
/**
* DES加密/解密公共方法
*
* @param data byte数组
* @param desKey 密钥
* @param mode 加密{@link Cipher#ENCRYPT_MODE}解密{@link Cipher#DECRYPT_MODE}
* @return des
*/
private static byte[] des(byte[] data, byte[] desKey, int mode) {
try {
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);
Cipher cipher = Cipher.getInstance(DES_ALGORITHM);
DESKeySpec desKeySpec = new DESKeySpec(desKey);
cipher.init(mode, keyFactory.generateSecret(desKeySpec), Holder.SECURE_RANDOM);
return cipher.doFinal(data);
} catch (Exception e) {
throw Exceptions.unchecked(e);
}
}
}
@@ -0,0 +1,304 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import java.util.Arrays;
/**
* 脱敏工具类
*
* @author Katrel
* @author L.cm
*/
public class DesensitizationUtil {
/**
* [中文姓名] 只显示第一个汉字其他隐藏为2个星号(例子**)
*
* @param fullName 全名
* @return 脱敏后的字符串
*/
@Nullable
public static String chineseName(@Nullable final String fullName) {
return sensitive(fullName, 1, 0);
}
/**
* [身份证号] 显示最后四位其他隐藏共计18位或者15位(例子*************5762)
*
* @param id 身份证号
* @return 脱敏后的字符串
*/
@Nullable
public static String idCardNum(@Nullable final String id) {
return sensitive(id, 0, 4);
}
/**
* [固定电话] 后四位其他隐藏(例子****1234)
*
* @param num 固定电话号
* @return 脱敏后的字符串
*/
@Nullable
public static String phoneNo(@Nullable final String num) {
return sensitive(num, 0, 4);
}
/**
* [手机号码] 前三位后四位其他隐藏(例子:138****1234)
*
* @param num 手机号
* @return 脱敏后的字符串
*/
@Nullable
public static String mobileNo(@Nullable final String num) {
return sensitive(num, 3, 4);
}
/**
* [地址] 只显示到地区不显示详细地址我们要对个人信息增强保护(例子北京市海淀区****)
*
* @param address 地区
* @param sensitiveSize 敏感信息长度
* @return 脱敏后的字符串
*/
@Nullable
public static String address(@Nullable final String address, final int sensitiveSize) {
return sensitive(address, 0, sensitiveSize);
}
/**
* [电子邮箱] 邮箱前缀仅显示第一个字母前缀其他隐藏用星号代替@及后面的地址显示(例子:g**@163.com)
*
* @param email 邮箱
* @return 脱敏后的字符串
*/
@Nullable
public static String email(@Nullable final String email) {
if (email == null) {
return null;
}
if (!StringUtils.hasText(email)) {
return StringPool.EMPTY;
}
final int index = email.indexOf(CharPool.AT);
if (index <= 1) {
return email;
} else {
return sensitive(email, 1, email.length() - index);
}
}
/**
* [银行卡号] 前六位后四位其他用星号隐藏每位1个星号(例子:622260***********1234)
*
* @param cardNum 银行卡号
* @return 脱敏后的字符串
*/
@Nullable
public static String bankCard(@Nullable final String cardNum) {
return sensitive(cardNum, 6, 4);
}
/**
* [公司开户银行联号] 公司开户银行联行号,显示前两位其他用星号隐藏每位1个星号(例子:12********)
*
* @param code 银行联行号
* @return 脱敏后的字符串
*/
@Nullable
public static String cnApsCode(@Nullable final String code) {
return sensitive(code, 2, 0);
}
/**
* 右边脱敏
*
* @param sensitiveStr 待脱敏的字符串
* @return 脱敏后的字符串
*/
@Nullable
public static String right(@Nullable final String sensitiveStr) {
if (sensitiveStr == null) {
return null;
}
if (!StringUtils.hasText(sensitiveStr)) {
return StringPool.EMPTY;
}
int length = sensitiveStr.length();
return sensitive(sensitiveStr, length / 2, 0);
}
/**
* 左边脱敏
*
* @param sensitiveStr 待脱敏的字符串
* @return 脱敏后的字符串
*/
@Nullable
public static String left(@Nullable final String sensitiveStr) {
if (sensitiveStr == null) {
return null;
}
if (!StringUtils.hasText(sensitiveStr)) {
return StringPool.EMPTY;
}
int length = sensitiveStr.length();
return sensitive(sensitiveStr, 0, length / 2);
}
/**
* 中间脱敏保留两端
*
* @param sensitiveStr 待脱敏的字符串
* @return 脱敏后的字符串
*/
@Nullable
public static String middle(@Nullable final String sensitiveStr) {
if (sensitiveStr == null) {
return null;
}
if (!StringUtils.hasText(sensitiveStr)) {
return StringPool.EMPTY;
}
int length = sensitiveStr.length();
if (length < 3) {
return StringUtil.leftPad(StringPool.EMPTY, length, CharPool.STAR);
} else if (length < 6) {
// 小于6个字符脱敏中间
char[] chars = new char[length];
int last = length - 1;
Arrays.fill(chars, 1, last, CharPool.STAR);
chars[0] = sensitiveStr.charAt(0);
chars[last] = sensitiveStr.charAt(last);
return new String(chars);
} else {
// 大于6个字符
int fromLastLen = length / 3;
return sensitive(sensitiveStr, fromLastLen, fromLastLen);
}
}
/**
* 全部脱敏
*
* @param sensitiveStr 待脱敏的字符串
* @return 脱敏后的字符串
*/
@Nullable
public static String all(@Nullable final String sensitiveStr) {
return sensitive(sensitiveStr, 0, 0);
}
/**
* 文本脱敏
*
* @param str 字符串
* @param fromIndex 开始的索引
* @param lastSize 尾部长度
* @return 脱敏后的字符串
*/
@Nullable
public static String sensitive(@Nullable String str, int fromIndex, int lastSize) {
return sensitive(str, fromIndex, lastSize, CharPool.STAR);
}
/**
* 文本脱敏
*
* @param str 字符串
* @param fromIndex 开始的索引
* @param lastSize 尾部长度
* @param padSize 填充的长度
* @return 脱敏后的字符串
*/
@Nullable
public static String sensitive(@Nullable String str, int fromIndex, int lastSize, int padSize) {
return sensitive(str, fromIndex, lastSize, CharPool.STAR, padSize);
}
/**
* 文本脱敏
*
* @param str 字符串
* @param fromIndex 开始的索引
* @param lastSize 尾部长度
* @param padChar 填充的字符
* @return 脱敏后的字符串
*/
@Nullable
public static String sensitive(@Nullable String str, int fromIndex, int lastSize, char padChar) {
return sensitive(str, fromIndex, lastSize, padChar, -1);
}
/**
* 文本脱敏
*
* @param str 字符串
* @param fromIndex 开始的索引
* @param lastSize 尾部长度
* @param padChar 填充的字符
* @param padSize 填充的长度
* @return 脱敏后的字符串
*/
@Nullable
public static String sensitive(@Nullable String str, int fromIndex, int lastSize, char padChar, int padSize) {
if (str == null) {
return null;
}
if (!StringUtils.hasText(str)) {
return StringPool.EMPTY;
}
int length = str.length();
// 全部脱敏
if (fromIndex == 0 && lastSize == 0) {
int padSiz = padSize > 0 ? padSize : length;
return StringUtil.repeat(CharPool.STAR, padSiz);
}
int toIndex = length - lastSize;
int padSiz = padSize > 0 ? padSize : toIndex - fromIndex;
// 头部脱敏
if (fromIndex == 0) {
String tail = str.substring(toIndex);
return StringUtil.repeat(padChar, padSiz).concat(tail);
}
// 尾部脱敏
if (toIndex == length) {
String head = str.substring(0, fromIndex);
return head.concat(StringUtil.repeat(padChar, padSiz));
}
// 中部
String head = str.substring(0, fromIndex);
String tail = str.substring(toIndex);
return head + StringUtil.repeat(padChar, padSiz) + tail;
}
}
@@ -0,0 +1,459 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import org.springframework.util.DigestUtils;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 加密相关工具类直接使用Spring util封装减少jar依赖
*
* @author L.cm
*/
public class DigestUtil extends org.springframework.util.DigestUtils {
private static final char[] HEX_CODE = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* Calculates the MD5 digest and returns the value as a 32 character hex string.
*
* @param data Data to digest
* @return MD5 digest as a hex string
*/
public static String md5Hex(final String data) {
return DigestUtils.md5DigestAsHex(data.getBytes(Charsets.UTF_8));
}
/**
* Return a hexadecimal string representation of the MD5 digest of the given bytes.
*
* @param bytes the bytes to calculate the digest over
* @return a hexadecimal digest string
*/
public static String md5Hex(final byte[] bytes) {
return DigestUtils.md5DigestAsHex(bytes);
}
/**
* sha1Hex
*
* @param data Data to digest
* @return digest as a hex string
*/
public static String sha1Hex(String data) {
return DigestUtil.sha1Hex(data.getBytes(Charsets.UTF_8));
}
/**
* sha1Hex
*
* @param bytes Data to digest
* @return digest as a hex string
*/
public static String sha1Hex(final byte[] bytes) {
return DigestUtil.digestHex("SHA-1", bytes);
}
/**
* SHA224Hex
*
* @param data Data to digest
* @return digest as a hex string
*/
public static String sha224Hex(String data) {
return DigestUtil.sha224Hex(data.getBytes(Charsets.UTF_8));
}
/**
* SHA224Hex
*
* @param bytes Data to digest
* @return digest as a hex string
*/
public static String sha224Hex(final byte[] bytes) {
return DigestUtil.digestHex("SHA-224", bytes);
}
/**
* sha256Hex
*
* @param data Data to digest
* @return digest as a hex string
*/
public static String sha256Hex(String data) {
return DigestUtil.sha256Hex(data.getBytes(Charsets.UTF_8));
}
/**
* sha256Hex
*
* @param bytes Data to digest
* @return digest as a hex string
*/
public static String sha256Hex(final byte[] bytes) {
return DigestUtil.digestHex("SHA-256", bytes);
}
/**
* sha384Hex
*
* @param data Data to digest
* @return digest as a hex string
*/
public static String sha384Hex(String data) {
return DigestUtil.sha384Hex(data.getBytes(Charsets.UTF_8));
}
/**
* sha384Hex
*
* @param bytes Data to digest
* @return digest as a hex string
*/
public static String sha384Hex(final byte[] bytes) {
return DigestUtil.digestHex("SHA-384", bytes);
}
/**
* sha512Hex
*
* @param data Data to digest
* @return digest as a hex string
*/
public static String sha512Hex(String data) {
return DigestUtil.sha512Hex(data.getBytes(Charsets.UTF_8));
}
/**
* sha512Hex
*
* @param bytes Data to digest
* @return digest as a hex string
*/
public static String sha512Hex(final byte[] bytes) {
return DigestUtil.digestHex("SHA-512", bytes);
}
/**
* digest Hex
*
* @param algorithm 算法
* @param bytes Data to digest
* @return digest as a hex string
*/
public static String digestHex(String algorithm, byte[] bytes) {
try {
MessageDigest md = MessageDigest.getInstance(algorithm);
return encodeHex(md.digest(bytes));
} catch (NoSuchAlgorithmException e) {
throw Exceptions.unchecked(e);
}
}
/**
* hmacMd5 Hex
*
* @param data Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacMd5Hex(String data, String key) {
return DigestUtil.hmacMd5Hex(data.getBytes(Charsets.UTF_8), key);
}
/**
* hmacMd5 Hex
*
* @param bytes Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacMd5Hex(final byte[] bytes, String key) {
return DigestUtil.digestHMacHex("HmacMD5", bytes, key);
}
/**
* hmacSha1 Hex
*
* @param data Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha1Hex(String data, String key) {
return DigestUtil.hmacSha1Hex(data.getBytes(Charsets.UTF_8), key);
}
/**
* hmacSha1 Hex
*
* @param bytes Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha1Hex(final byte[] bytes, String key) {
return DigestUtil.digestHMacHex("HmacSHA1", bytes, key);
}
/**
* hmacSha224 Hex
*
* @param data Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha224Hex(String data, String key) {
return DigestUtil.hmacSha224Hex(data.getBytes(Charsets.UTF_8), key);
}
/**
* hmacSha224 Hex
*
* @param bytes Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha224Hex(final byte[] bytes, String key) {
return DigestUtil.digestHMacHex("HmacSHA224", bytes, key);
}
/**
* hmacSha256
*
* @param data Data to digest
* @param key key
* @return digest as a hex string
*/
public static byte[] hmacSha256(String data, String key) {
return DigestUtil.hmacSha256(data.getBytes(Charsets.UTF_8), key);
}
/**
* hmacSha256
*
* @param bytes Data to digest
* @param key key
* @return digest as a byte array
*/
public static byte[] hmacSha256(final byte[] bytes, String key) {
return DigestUtil.digestHMac("HmacSHA256", bytes, key);
}
/**
* hmacSha256 Hex
*
* @param data Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha256Hex(String data, String key) {
return DigestUtil.hmacSha256Hex(data.getBytes(Charsets.UTF_8), key);
}
/**
* hmacSha256 Hex
*
* @param bytes Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha256Hex(final byte[] bytes, String key) {
return DigestUtil.digestHMacHex("HmacSHA256", bytes, key);
}
/**
* hmacSha384 Hex
*
* @param data Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha384Hex(String data, String key) {
return DigestUtil.hmacSha384Hex(data.getBytes(Charsets.UTF_8), key);
}
/**
* hmacSha384 Hex
*
* @param bytes Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha384Hex(final byte[] bytes, String key) {
return DigestUtil.digestHMacHex("HmacSHA384", bytes, key);
}
/**
* hmacSha512 Hex
*
* @param data Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha512Hex(String data, String key) {
return DigestUtil.hmacSha512Hex(data.getBytes(Charsets.UTF_8), key);
}
/**
* hmacSha512 Hex
*
* @param bytes Data to digest
* @param key key
* @return digest as a hex string
*/
public static String hmacSha512Hex(final byte[] bytes, String key) {
return DigestUtil.digestHMacHex("HmacSHA512", bytes, key);
}
/**
* digest HMac Hex
*
* @param algorithm 算法
* @param bytes Data to digest
* @return digest as a hex string
*/
public static String digestHMacHex(String algorithm, final byte[] bytes, String key) {
SecretKey secretKey = new SecretKeySpec(key.getBytes(Charsets.UTF_8), algorithm);
try {
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
return DigestUtil.encodeHex(mac.doFinal(bytes));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw Exceptions.unchecked(e);
}
}
/**
* digest HMac
*
* @param algorithm 算法
* @param bytes Data to digest
* @return digest as a byte array
*/
public static byte[] digestHMac(String algorithm, final byte[] bytes, String key) {
SecretKey secretKey = new SecretKeySpec(key.getBytes(Charsets.UTF_8), algorithm);
try {
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
return mac.doFinal(bytes);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw Exceptions.unchecked(e);
}
}
/**
* encode Hex
*
* @param bytes Data to Hex
* @return bytes as a hex string
*/
public static String encodeHex(byte[] bytes) {
StringBuilder r = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
r.append(HEX_CODE[(b >> 4) & 0xF]);
r.append(HEX_CODE[(b & 0xF)]);
}
return r.toString();
}
/**
* decode Hex
*
* @param hexStr Hex string
* @return decode hex to bytes
*/
public static byte[] decodeHex(final String hexStr) {
return DatatypeConverterUtil.parseHexBinary(hexStr);
}
/**
* 比较字符串避免字符串因为过长产生耗时
*
* @param a String
* @param b String
* @return 是否相同
*/
public static boolean slowEquals(@Nullable String a, @Nullable String b) {
if (a == null || b == null) {
return false;
}
return DigestUtil.slowEquals(a.getBytes(Charsets.UTF_8), b.getBytes(Charsets.UTF_8));
}
/**
* 比较 byte 数组避免字符串因为过长产生耗时
*
* @param a byte array
* @param b byte array
* @return 是否相同
*/
public static boolean slowEquals(@Nullable byte[] a, @Nullable byte[] b) {
if (a == null || b == null) {
return false;
}
if (a.length != b.length) {
return false;
}
int diff = a.length ^ b.length;
for (int i = 0; i < a.length; i++) {
diff |= a[i] ^ b[i];
}
return diff == 0;
}
/**
* 自定义加密 将前端传递的密码再次加密
*
* @param data 数据
* @return {String}
*/
public static String hex(String data) {
if (StringUtil.isBlank(data)) {
return StringPool.EMPTY;
}
return sha1Hex(data);
}
/**
* 用户密码加密规则 先MD5再SHA1
*
* @param data 数据
* @return {String}
*/
public static String encrypt(String data) {
if (StringUtil.isBlank(data)) {
return StringPool.EMPTY;
}
return sha1Hex(md5Hex(data));
}
}
@@ -0,0 +1,108 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springblade.core.tool.support.FastStringWriter;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
/**
* 异常处理工具类
*
* @author L.cm
*/
public class Exceptions {
/**
* 将CheckedException转换为UncheckedException.
*
* @param e Throwable
* @return {RuntimeException}
*/
public static RuntimeException unchecked(Throwable e) {
if (e instanceof Error) {
throw (Error) e;
} else if (e instanceof IllegalAccessException ||
e instanceof IllegalArgumentException ||
e instanceof NoSuchMethodException) {
return new IllegalArgumentException(e);
} else if (e instanceof InvocationTargetException) {
return new RuntimeException(((InvocationTargetException) e).getTargetException());
} else if (e instanceof RuntimeException) {
return (RuntimeException) e;
} else if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
return Exceptions.runtime(e);
}
/**
* 不采用 RuntimeException 包装直接抛出使异常更加精准
*
* @param throwable Throwable
* @param <T> 泛型标记
* @return Throwable
* @throws T 泛型
*/
@SuppressWarnings("unchecked")
private static <T extends Throwable> T runtime(Throwable throwable) throws T {
throw (T) throwable;
}
/**
* 代理异常解包
*
* @param wrapped 包装过得异常
* @return 解包后的异常
*/
public static Throwable unwrap(Throwable wrapped) {
Throwable unwrapped = wrapped;
while (true) {
if (unwrapped instanceof InvocationTargetException) {
unwrapped = ((InvocationTargetException) unwrapped).getTargetException();
} else if (unwrapped instanceof UndeclaredThrowableException) {
unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable();
} else {
return unwrapped;
}
}
}
/**
* 将ErrorStack转化为String.
*
* @param ex Throwable
* @return {String}
*/
public static String getStackTraceAsString(Throwable ex) {
FastStringWriter stringWriter = new FastStringWriter();
ex.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
}
@@ -0,0 +1,418 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
/**
* 文件工具类
*
* @author L.cm
*/
public class FileUtil extends org.springframework.util.FileCopyUtils {
/**
* 特殊后缀
*/
private static final CharSequence[] SPECIAL_SUFFIX = {"tar.bz2", "tar.Z", "tar.gz", "tar.xz"};
/**
* 默认为true
*
* @author L.cm
*/
public static class TrueFilter implements FileFilter, Serializable {
@Serial
private static final long serialVersionUID = -6420452043795072619L;
public final static TrueFilter TRUE = new TrueFilter();
@Override
public boolean accept(File pathname) {
return true;
}
}
/**
* 扫描目录下的文件
*
* @param path 路径
* @return 文件集合
*/
public static List<File> list(String path) {
File file = new File(path);
return list(file, TrueFilter.TRUE);
}
/**
* 扫描目录下的文件
*
* @param path 路径
* @param fileNamePattern 文件名 *
* @return 文件集合
*/
public static List<File> list(String path, final String fileNamePattern) {
File file = new File(path);
return list(file, pathname -> {
String fileName = pathname.getName();
return PatternMatchUtils.simpleMatch(fileNamePattern, fileName);
});
}
/**
* 扫描目录下的文件
*
* @param path 路径
* @param filter 文件过滤
* @return 文件集合
*/
public static List<File> list(String path, FileFilter filter) {
File file = new File(path);
return list(file, filter);
}
/**
* 扫描目录下的文件
*
* @param file 文件
* @return 文件集合
*/
public static List<File> list(File file) {
List<File> fileList = new ArrayList<>();
return list(file, fileList, TrueFilter.TRUE);
}
/**
* 扫描目录下的文件
*
* @param file 文件
* @param fileNamePattern Spring AntPathMatcher 规则
* @return 文件集合
*/
public static List<File> list(File file, final String fileNamePattern) {
List<File> fileList = new ArrayList<>();
return list(file, fileList, pathname -> {
String fileName = pathname.getName();
return PatternMatchUtils.simpleMatch(fileNamePattern, fileName);
});
}
/**
* 扫描目录下的文件
*
* @param file 文件
* @param filter 文件过滤
* @return 文件集合
*/
public static List<File> list(File file, FileFilter filter) {
List<File> fileList = new ArrayList<>();
return list(file, fileList, filter);
}
/**
* 扫描目录下的文件
*
* @param file 文件
* @param filter 文件过滤
* @return 文件集合
*/
private static List<File> list(File file, List<File> fileList, FileFilter filter) {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
list(f, fileList, filter);
}
}
} else {
// 过滤文件
boolean accept = filter.accept(file);
if (file.exists() && accept) {
fileList.add(file);
}
}
return fileList;
}
/**
* 获取文件后缀名
* @param fullName 文件全名
* @return {String}
*/
public static String getFileExtension(String fullName) {
if (StringUtil.isBlank(fullName)) return StringPool.EMPTY;
String fileName = new File(fullName).getName();
final int index = fileName.lastIndexOf(CharPool.DOT);
if (index == -1) {
return StringPool.EMPTY;
}
// 特殊文件名称如tar.gz,tar.bz2,tar.Z,tar.xz
final int secondToLastIndex = fileName.substring(0, index).lastIndexOf(CharPool.DOT);
final String substr = fileName.substring(secondToLastIndex == -1 ? index : secondToLastIndex + 1);
if (StringUtil.containsAny(substr, SPECIAL_SUFFIX)) {
return substr;
}
return fileName.substring(index + 1);
}
/**
* 获取文件名去除后缀名
* @param fullName 文件全名
* @return {String}
*/
public static String getNameWithoutExtension(String fullName) {
if (StringUtil.isBlank(fullName)) return StringPool.EMPTY;
String fileName = new File(fullName).getName();
final int index = fileName.lastIndexOf(CharPool.DOT);
if (index == -1) {
return fileName;
}
// 检查是否为特殊后缀名
int secondToLastIndex = fileName.substring(0, index).lastIndexOf(CharPool.DOT);
String substr = fileName.substring(secondToLastIndex == -1? index : secondToLastIndex + 1);
if (StringUtil.containsAny(substr, SPECIAL_SUFFIX)) {
return fileName.substring(0, secondToLastIndex);
}
return fileName.substring(0, index);
}
/**
* Returns the path to the system temporary directory.
*
* @return the path to the system temporary directory.
*/
public static String getTempDirPath() {
return System.getProperty("java.io.tmpdir");
}
/**
* Returns a {@link File} representing the system temporary directory.
*
* @return the system temporary directory.
*/
public static File getTempDir() {
return new File(getTempDirPath());
}
/**
* Reads the contents of a file into a String.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @return the file contents, never {@code null}
*/
public static String readToString(final File file) {
return readToString(file, Charsets.UTF_8);
}
/**
* Reads the contents of a file into a String.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @param encoding the encoding to use, {@code null} means platform default
* @return the file contents, never {@code null}
*/
public static String readToString(final File file, final Charset encoding) {
try (InputStream in = Files.newInputStream(file.toPath())) {
return IoUtil.readToString(in, encoding);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* Reads the contents of a file into a String.
* The file is always closed.
*
* @param file the file to read, must not be {@code null}
* @return the file contents, never {@code null}
*/
public static byte[] readToByteArray(final File file) {
try (InputStream in = Files.newInputStream(file.toPath())) {
return IoUtil.readToByteArray(in);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* Writes a String to a file creating the file if it does not exist.
*
* @param file the file to write
* @param data the content to write to the file
*/
public static void writeToFile(final File file, final String data) {
writeToFile(file, data, Charsets.UTF_8, false);
}
/**
* Writes a String to a file creating the file if it does not exist.
*
* @param file the file to write
* @param data the content to write to the file
* @param append if {@code true}, then the String will be added to the
* end of the file rather than overwriting
*/
public static void writeToFile(final File file, final String data, final boolean append){
writeToFile(file, data, Charsets.UTF_8, append);
}
/**
* Writes a String to a file creating the file if it does not exist.
*
* @param file the file to write
* @param data the content to write to the file
* @param encoding the encoding to use, {@code null} means platform default
*/
public static void writeToFile(final File file, final String data, final Charset encoding) {
writeToFile(file, data, encoding, false);
}
/**
* Writes a String to a file creating the file if it does not exist.
*
* @param file the file to write
* @param data the content to write to the file
* @param encoding the encoding to use, {@code null} means platform default
* @param append if {@code true}, then the String will be added to the
* end of the file rather than overwriting
*/
public static void writeToFile(final File file, final String data, final Charset encoding, final boolean append) {
try (OutputStream out = new FileOutputStream(file, append)) {
IoUtil.write(data, out, encoding);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 转成file
* @param multipartFile MultipartFile
* @param file File
*/
public static void toFile(MultipartFile multipartFile, final File file) {
try {
FileUtil.toFile(multipartFile.getInputStream(), file);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 转成file
* @param in InputStream
* @param file File
*/
public static void toFile(InputStream in, final File file) {
try (OutputStream out = new FileOutputStream(file)) {
FileUtil.copy(in, out);
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* Moves a file.
* <p>
* When the destination file is on another file system, do a "copy and delete".
*
* @param srcFile the file to be moved
* @param destFile the destination file
* @throws NullPointerException if source or destination is {@code null}
* @throws IOException if source or destination is invalid
* @throws IOException if an IO error occurs moving the file
*/
public static void moveFile(final File srcFile, final File destFile) throws IOException {
Assert.notNull(srcFile, "Source must not be null");
Assert.notNull(destFile, "Destination must not be null");
if (!srcFile.exists()) {
throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
}
if (srcFile.isDirectory()) {
throw new IOException("Source '" + srcFile + "' is a directory");
}
if (destFile.exists()) {
throw new IOException("Destination '" + destFile + "' already exists");
}
if (destFile.isDirectory()) {
throw new IOException("Destination '" + destFile + "' is a directory");
}
final boolean rename = srcFile.renameTo(destFile);
if (!rename) {
FileUtil.copy(srcFile, destFile);
if (!srcFile.delete()) {
FileUtil.deleteQuietly(destFile);
throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'");
}
}
}
/**
* Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
* <p>
* The difference between File.delete() and this method are:
* <ul>
* <li>A directory to be deleted does not have to be empty.</li>
* <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
* </ul>
*
* @param file file or directory to delete, can be {@code null}
* @return {@code true} if the file or directory was deleted, otherwise
* {@code false}
*/
public static boolean deleteQuietly(@Nullable final File file) {
if (file == null) {
return false;
}
try {
if (file.isDirectory()) {
FileSystemUtils.deleteRecursively(file);
}
} catch (final Exception ignored) {
}
try {
return file.delete();
} catch (final Exception ignored) {
return false;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,204 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import java.nio.charset.Charset;
/**
* hex 工具编解码全用 byte
*
* @author L.cm
*/
public class HexUtil {
public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
private static final byte[] DIGITS_LOWER = new byte[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static final byte[] DIGITS_UPPER = new byte[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* encode Hex
*
* @param data data to hex
* @return hex bytes
*/
public static byte[] encode(byte[] data) {
return encode(data, true);
}
/**
* encode Hex
*
* @param data data to hex
* @param toLowerCase 是否小写
* @return hex bytes
*/
public static byte[] encode(byte[] data, boolean toLowerCase) {
return encode(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* encode Hex
*
* @param data Data to Hex
* @return bytes as a hex string
*/
private static byte[] encode(byte[] data, byte[] digits) {
int len = data.length;
byte[] out = new byte[len << 1];
for (int i = 0, j = 0; i < len; i++) {
out[j++] = digits[(0xF0 & data[i]) >>> 4];
out[j++] = digits[0xF & data[i]];
}
return out;
}
/**
* encode Hex
*
* @param data Data to Hex
* @param toLowerCase 是否小写
* @return bytes as a hex string
*/
public static String encodeToString(byte[] data, boolean toLowerCase) {
return new String(encode(data, toLowerCase), DEFAULT_CHARSET);
}
/**
* encode Hex
*
* @param data Data to Hex
* @return bytes as a hex string
*/
public static String encodeToString(byte[] data) {
return encodeToString(data, DEFAULT_CHARSET);
}
/**
* encode Hex
*
* @param data Data to Hex
* @param charset Charset
* @return bytes as a hex string
*/
public static String encodeToString(byte[] data, Charset charset) {
return new String(encode(data), charset);
}
/**
* encode Hex
*
* @param data Data to Hex
* @return bytes as a hex string
*/
@Nullable
public static String encodeToString(@Nullable String data) {
if (StringUtil.isBlank(data)) {
return null;
}
return encodeToString(data.getBytes(DEFAULT_CHARSET));
}
/**
* decode Hex
*
* @param data Hex data
* @return decode hex to bytes
*/
public static byte[] decode(String data) {
return decode(data, DEFAULT_CHARSET);
}
/**
* decode Hex
*
* @param data Hex data
* @param charset Charset
* @return decode hex to bytes
*/
public static byte[] decode(String data, Charset charset) {
if (StringUtil.isBlank(data)) {
return null;
}
return decode(data.getBytes(charset));
}
/**
* decodeToString Hex
*
* @param data Data to Hex
* @return bytes as a hex string
*/
public static String decodeToString(byte[] data) {
byte[] decodeBytes = decode(data);
return new String(decodeBytes, DEFAULT_CHARSET);
}
/**
* decodeToString Hex
*
* @param data Data to Hex
* @return bytes as a hex string
*/
@Nullable
public static String decodeToString(@Nullable String data) {
if (StringUtil.isBlank(data)) {
return null;
}
return decodeToString(data.getBytes(DEFAULT_CHARSET));
}
/**
* decode Hex
*
* @param data Hex data
* @return decode hex to bytes
*/
public static byte[] decode(byte[] data) {
int len = data.length;
if ((len & 0x01) != 0) {
throw new IllegalArgumentException("hexBinary needs to be even-length: " + len);
}
byte[] out = new byte[len >> 1];
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(data[j], j) << 4;
j++;
f |= toDigit(data[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
private static int toDigit(byte b, int index) {
int digit = Character.digit(b, 16);
if (digit == -1) {
throw new IllegalArgumentException("Illegal hexadecimal byte " + b + " at index " + index);
}
return digit;
}
}
@@ -0,0 +1,48 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import java.security.SecureRandom;
import java.util.Random;
/**
* 一些常用的单利对象
*
* @author L.cm
*/
public class Holder {
/**
* RANDOM
*/
public final static Random RANDOM = new Random();
/**
* SECURE_RANDOM
*/
public final static SecureRandom SECURE_RANDOM = new SecureRandom();
}
@@ -0,0 +1,490 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.utils;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.tool.support.IMultiOutputStream;
import org.springblade.core.tool.support.ImagePosition;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import java.io.*;
import java.net.URL;
/**
* 图片工具类
*
* @author Chill
*/
@Slf4j
public final class ImageUtil {
/**
* 默认输出图片类型
*/
public static final String DEFAULT_IMG_TYPE = "JPEG";
/**
* 转换输入流到byte
*
* @param src
* @param type 类型
* @return byte[]
* @throws IOException 异常
*/
public static byte[] toByteArray(BufferedImage src, String type) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(src, defaultString(type, DEFAULT_IMG_TYPE), os);
return os.toByteArray();
}
/**
* 获取图像内容
*
* @param srcImageFile 文件路径
* @return BufferedImage
*/
public static BufferedImage readImage(String srcImageFile) {
try {
return ImageIO.read(new File(srcImageFile));
} catch (IOException e) {
log.error("Error readImage", e);
}
return null;
}
/**
* 获取图像内容
*
* @param srcImageFile 文件
* @return BufferedImage
*/
public static BufferedImage readImage(File srcImageFile) {
try {
return ImageIO.read(srcImageFile);
} catch (IOException e) {
log.error("Error readImage", e);
}
return null;
}
/**
* 获取图像内容
*
* @param srcInputStream 输入流
* @return BufferedImage
*/
public static BufferedImage readImage(InputStream srcInputStream) {
try {
return ImageIO.read(srcInputStream);
} catch (IOException e) {
log.error("Error readImage", e);
}
return null;
}
/**
* 获取图像内容
*
* @param url URL地址
* @return BufferedImage
*/
public static BufferedImage readImage(URL url) {
try {
return ImageIO.read(url);
} catch (IOException e) {
log.error("Error readImage", e);
}
return null;
}
/**
* 缩放图像按比例缩放
*
* @param src 源图像
* @param output 输出流
* @param type 类型
* @param scale 缩放比例
* @param flag 缩放选择:true 放大; false 缩小;
*/
public static void zoomScale(BufferedImage src, OutputStream output, String type, double scale, boolean flag) {
try {
// 得到源图宽
int width = src.getWidth();
// 得到源图长
int height = src.getHeight();
if (flag) {
// 放大
width = Long.valueOf(Math.round(width * scale)).intValue();
height = Long.valueOf(Math.round(height * scale)).intValue();
} else {
// 缩小
width = Long.valueOf(Math.round(width / scale)).intValue();
height = Long.valueOf(Math.round(height / scale)).intValue();
}
Image image = src.getScaledInstance(width, height, Image.SCALE_DEFAULT);
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), output);
output.close();
} catch (IOException e) {
log.error("Error in zoom image", e);
}
}
/**
* 缩放图像按高度和宽度缩放
*
* @param src 源图像
* @param output 输出流
* @param type 类型
* @param height 缩放后的高度
* @param width 缩放后的宽度
* @param bb 比例不对时是否需要补白true为补白; false为不补白;
* @param fillColor 填充色null时为Color.WHITE
*/
public static void zoomFixed(BufferedImage src, OutputStream output, String type, int height, int width, boolean bb, Color fillColor) {
try {
double ratio = 0.0;
Image itemp = src.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
// 计算比例
if (src.getHeight() > src.getWidth()) {
ratio = Integer.valueOf(height).doubleValue() / src.getHeight();
} else {
ratio = Integer.valueOf(width).doubleValue() / src.getWidth();
}
AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
itemp = op.filter(src, null);
if (bb) {
//补白
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
Color fill = fillColor == null ? Color.white : fillColor;
g.setColor(fill);
g.fillRect(0, 0, width, height);
if (width == itemp.getWidth(null)) {
g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2, itemp.getWidth(null), itemp.getHeight(null), fill, null);
} else {
g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0, itemp.getWidth(null), itemp.getHeight(null), fill, null);
}
g.dispose();
itemp = image;
}
// 输出为文件
ImageIO.write((BufferedImage) itemp, defaultString(type, DEFAULT_IMG_TYPE), output);
// 关闭流
output.close();
} catch (IOException e) {
log.error("Error in zoom image", e);
}
}
/**
* 图像裁剪(按指定起点坐标和宽高切割)
*
* @param src 源图像
* @param output 切片后的图像地址
* @param type 类型
* @param x 目标切片起点坐标X
* @param y 目标切片起点坐标Y
* @param width 目标切片宽度
* @param height 目标切片高度
*/
public static void crop(BufferedImage src, OutputStream output, String type, int x, int y, int width, int height) {
try {
// 源图宽度
int srcWidth = src.getWidth();
// 源图高度
int srcHeight = src.getHeight();
if (srcWidth > 0 && srcHeight > 0) {
Image image = src.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
// 四个参数分别为图像起点坐标和宽高
ImageFilter cropFilter = new CropImageFilter(x, y, width, height);
Image img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(img, 0, 0, width, height, null);
g.dispose();
// 输出为文件
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), output);
// 关闭流
output.close();
}
} catch (Exception e) {
log.error("Error in cut image", e);
}
}
/**
* 图像切割指定切片的行数和列数
*
* @param src 源图像地址
* @param mos 切片目标文件夹
* @param type 类型
* @param prows 目标切片行数默认2必须是范围 [1, 20] 之内
* @param pcols 目标切片列数默认2必须是范围 [1, 20] 之内
*/
public static void sliceWithNumber(BufferedImage src, IMultiOutputStream mos, String type, int prows, int pcols) {
try {
int rows = prows <= 0 || prows > 20 ? 2 : prows;
int cols = pcols <= 0 || pcols > 20 ? 2 : pcols;
// 源图宽度
int srcWidth = src.getWidth();
// 源图高度
int srcHeight = src.getHeight();
if (srcWidth > 0 && srcHeight > 0) {
Image img;
ImageFilter cropFilter;
Image image = src.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
// 每张切片的宽度
int destWidth = (srcWidth % cols == 0) ? (srcWidth / cols) : (srcWidth / cols + 1);
// 每张切片的高度
int destHeight = (srcHeight % rows == 0) ? (srcHeight / rows) : (srcHeight / rows + 1);
// 循环建立切片
// 改进的想法:是否可用多线程加快切割速度
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// 四个参数分别为图像起点坐标和宽高
cropFilter = new CropImageFilter(j * destWidth, i * destHeight, destWidth, destHeight);
img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
// 绘制缩小后的图
g.drawImage(img, 0, 0, null);
g.dispose();
// 输出为文件
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), mos.buildOutputStream(i, j));
}
}
}
} catch (Exception e) {
log.error("Error in slice image", e);
}
}
/**
* 图像切割指定切片的宽度和高度
*
* @param src 源图像地址
* @param mos 切片目标文件夹
* @param type 类型
* @param pdestWidth 目标切片宽度默认200
* @param pdestHeight 目标切片高度默认150
*/
public static void sliceWithSize(BufferedImage src, IMultiOutputStream mos, String type, int pdestWidth, int pdestHeight) {
try {
int destWidth = pdestWidth <= 0 ? 200 : pdestWidth;
int destHeight = pdestHeight <= 0 ? 150 : pdestHeight;
// 源图宽度
int srcWidth = src.getWidth();
// 源图高度
int srcHeight = src.getHeight();
if (srcWidth > destWidth && srcHeight > destHeight) {
Image img;
ImageFilter cropFilter;
Image image = src.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
// 切片横向数量
int cols = (srcWidth % destWidth == 0) ? (srcWidth / destWidth) : (srcWidth / destWidth + 1);
// 切片纵向数量
int rows = (srcHeight % destHeight == 0) ? (srcHeight / destHeight) : (srcHeight / destHeight + 1);
// 循环建立切片
// 改进的想法:是否可用多线程加快切割速度
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// 四个参数分别为图像起点坐标和宽高
cropFilter = new CropImageFilter(j * destWidth, i * destHeight, destWidth, destHeight);
img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
// 绘制缩小后的图
g.drawImage(img, 0, 0, null);
g.dispose();
// 输出为文件
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), mos.buildOutputStream(i, j));
}
}
}
} catch (Exception e) {
log.error("Error in slice image", e);
}
}
/**
* 图像类型转换GIF-JPGGIF-PNGPNG-JPGPNG-GIF(X)BMP-PNG
*
* @param src 源图像地址
* @param formatName 包含格式非正式名称的 String如JPGJPEGGIF等
* @param output 目标图像地址
*/
public static void convert(BufferedImage src, OutputStream output, String formatName) {
try {
// 输出为文件
ImageIO.write(src, formatName, output);
// 关闭流
output.close();
} catch (Exception e) {
log.error("Error in convert image", e);
}
}
/**
* 彩色转为黑白
*
* @param src 源图像地址
* @param output 目标图像地址
* @param type 类型
*/
public static void gray(BufferedImage src, OutputStream output, String type) {
try {
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorConvertOp op = new ColorConvertOp(cs, null);
src = op.filter(src, null);
// 输出为文件
ImageIO.write(src, defaultString(type, DEFAULT_IMG_TYPE), output);
// 关闭流
output.close();
} catch (IOException e) {
log.error("Error in gray image", e);
}
}
/**
* 给图片添加文字水印
*
* @param src 源图像
* @param output 输出流
* @param type 类型
* @param text 水印文字
* @param font 水印的字体
* @param color 水印的字体颜色
* @param position 水印位置 {@link ImagePosition}
* @param x 修正值
* @param y 修正值
* @param alpha 透明度alpha 必须是范围 [0.0, 1.0] 之内包含边界值的一个浮点数字
*/
public static void textStamp(BufferedImage src, OutputStream output, String type, String text, Font font, Color color
, int position, int x, int y, float alpha) {
try {
int width = src.getWidth(null);
int height = src.getHeight(null);
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.drawImage(src, 0, 0, width, height, null);
g.setColor(color);
g.setFont(font);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
// 在指定坐标绘制水印文字
ImagePosition boxPos = new ImagePosition(width, height, calcTextWidth(text) * font.getSize(), font.getSize(), position);
g.drawString(text, boxPos.getX(x), boxPos.getY(y));
g.dispose();
// 输出为文件
ImageIO.write((BufferedImage) image, defaultString(type, DEFAULT_IMG_TYPE), output);
// 关闭流
output.close();
} catch (Exception e) {
log.error("Error in textStamp image", e);
}
}
/**
* 给图片添加图片水印
*
* @param src 源图像
* @param output 输出流
* @param type 类型
* @param stamp 水印图片
* @param position 水印位置 {@link ImagePosition}
* @param x 修正值
* @param y 修正值
* @param alpha 透明度alpha 必须是范围 [0.0, 1.0] 之内包含边界值的一个浮点数字
*/
public static void imageStamp(BufferedImage src, OutputStream output, String type, BufferedImage stamp
, int position, int x, int y, float alpha) {
try {
int width = src.getWidth();
int height = src.getHeight();
BufferedImage image = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.drawImage(src, 0, 0, width, height, null);
// 水印文件
int stampWidth = stamp.getWidth();
int stampHeight = stamp.getHeight();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
ImagePosition boxPos = new ImagePosition(width, height, stampWidth, stampHeight, position);
g.drawImage(stamp, boxPos.getX(x), boxPos.getY(y), stampWidth, stampHeight, null);
// 水印文件结束
g.dispose();
// 输出为文件
ImageIO.write((BufferedImage) image, defaultString(type, DEFAULT_IMG_TYPE), output);
// 关闭流
output.close();
} catch (Exception e) {
log.error("Error imageStamp", e);
}
}
/**
* 计算text的长度一个中文算两个字符
*
* @param text text
* @return int
*/
public static int calcTextWidth(String text) {
int length = 0;
for (int i = 0; i < text.length(); i++) {
if ((text.charAt(i) + "").getBytes().length > 1) {
length += 2;
} else {
length += 1;
}
}
return length / 2;
}
/**
* 默认字符串
*
* @param str 字符串
* @param defaultStr 默认值
* @return String
*/
public static String defaultString(String str, String defaultStr) {
return ((str == null) ? defaultStr : str);
}
}
@@ -0,0 +1,37 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: Chill Zhuang (bladejava@qq.com)
*/
package org.springblade.core.tool.utils;
/**
* 静态 Integer .
*
* @author Chill
*/
public interface IntegerPool {
Integer INT_1024 = 1024;
}
@@ -0,0 +1,118 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import java.io.*;
import java.nio.charset.Charset;
/**
* 流工具类
*
* @author L.cm
*/
public class IoUtil extends org.springframework.util.StreamUtils {
/**
* closeQuietly
*
* @param closeable 自动关闭
*/
public static void closeQuietly(@Nullable Closeable closeable) {
if (closeable == null) {
return;
}
if (closeable instanceof Flushable) {
try {
((Flushable) closeable).flush();
} catch (IOException ignored) {
// ignore
}
}
try {
closeable.close();
} catch (IOException ignored) {
// ignore
}
}
/**
* InputStream to String utf-8
*
* @param input the <code>InputStream</code> to read from
* @return the requested String
*/
public static String readToString(InputStream input) {
return readToString(input, Charsets.UTF_8);
}
/**
* InputStream to String
*
* @param input the <code>InputStream</code> to read from
* @param charset the <code>Charset</code>
* @return the requested String
*/
public static String readToString(@Nullable InputStream input, Charset charset) {
try {
return IoUtil.copyToString(input, charset);
} catch (IOException e) {
throw Exceptions.unchecked(e);
} finally {
IoUtil.closeQuietly(input);
}
}
public static byte[] readToByteArray(@Nullable InputStream input) {
try {
return IoUtil.copyToByteArray(input);
} catch (IOException e) {
throw Exceptions.unchecked(e);
} finally {
IoUtil.closeQuietly(input);
}
}
/**
* Writes chars from a <code>String</code> to bytes on an
* <code>OutputStream</code> using the specified character encoding.
* <p>
* This method uses {@link String#getBytes(String)}.
* </p>
* @param data the <code>String</code> to write, null ignored
* @param output the <code>OutputStream</code> to write to
* @param encoding the encoding to use, null means platform default
* @throws NullPointerException if output is null
* @throws IOException if an I/O error occurs
*/
public static void write(@Nullable final String data, final OutputStream output, final Charset encoding) throws IOException {
if (data != null) {
output.write(data.getBytes(encoding));
}
}
}

Some files were not shown because too many files have changed in this diff Show More