This commit is contained in:
2025-11-28 16:23:32 +08:00
commit a9e0e16c29
826 changed files with 89805 additions and 0 deletions

78
blade-core-tool/pom.xml Normal file
View File

@@ -0,0 +1,78 @@
<?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>
<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</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>
<!-- Auto -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -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();
}

View File

@@ -0,0 +1,238 @@
/**
* 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);
}
/**
* 返回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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -0,0 +1,414 @@
/**
* 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 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.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 Type CONVERTER = TypeUtils.parseType("org.springframework.cglib.core.Converter");
private static final Type BEAN_COPIER = TypeUtils.parseType(BladeBeanCopier.class.getName());
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();
gen.setSource(key.getSource());
gen.setTarget(key.getTarget());
gen.setUseConverter(key.isUseConverter());
gen.setNonNull(key.isNonNull());
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(BladeBeanCopier.class.getName());
private Class source;
private Class target;
private boolean useConverter;
private boolean nonNull;
Generator() {
super(SOURCE);
}
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;
}
public void setUseConverter(boolean useConverter) {
this.useConverter = useConverter;
}
public void setNonNull(boolean nonNull) {
this.nonNull = nonNull;
}
@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);
}
@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,
getClassName(),
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;
}
/**
* 处理 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();
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -0,0 +1,91 @@
/**
* 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.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springblade.core.tool.jackson.BladeJacksonProperties;
import org.springblade.core.tool.jackson.BladeJavaTimeModule;
import org.springblade.core.tool.utils.DateUtil;
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;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.util.Locale;
import java.util.TimeZone;
/**
* Jackson配置类
*
* @author Chill
*/
@AutoConfiguration(before = JacksonAutoConfiguration.class)
@ConditionalOnClass(ObjectMapper.class)
@EnableConfigurationProperties(BladeJacksonProperties.class)
public class JacksonConfiguration {
@Bean
@ConditionalOnMissingBean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
builder.simpleDateFormat(DateUtil.PATTERN_DATETIME);
//创建ObjectMapper
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
//设置地点为中国
objectMapper.setLocale(Locale.CHINA);
//去掉默认的时间戳格式
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//设置为中国上海时区
objectMapper.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
//序列化时,日期的统一格式
objectMapper.setDateFormat(new SimpleDateFormat(DateUtil.PATTERN_DATETIME, Locale.CHINA));
//序列化处理
objectMapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
objectMapper.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true);
//失败处理
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//单引号处理
objectMapper.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature(), true);
//反序列化时,属性不存在的兼容处理
objectMapper.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//日期格式化
objectMapper.configure(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS, false);
objectMapper.registerModule(BladeJavaTimeModule.INSTANCE);
objectMapper.configure(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS, true);
objectMapper.findAndRegisterModules();
return objectMapper;
}
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,155 @@
/**
* 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";
/**
* 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";
/**
* 业务状态[1:正常]
*/
int DB_STATUS_NORMAL = 1;
/**
* 删除状态[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 = "签名认证失败";
}

View File

@@ -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 + "')";
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,132 @@
/**
* 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;
/**
* 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)) {
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();
}
};
}
}

View File

@@ -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.jackson;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 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;
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,772 @@
/**
* 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.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.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);
}
}
/**
* 封装 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 String content) {
return readMap(content, String.class, Object.class);
}
/**
* 读取集合
*
* @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, new TypeReference<List<Map<String, Object>>>() {
});
} catch (IOException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 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());
}
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;
}
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);
}
public static ObjectMapper getInstance() {
return JacksonHolder.INSTANCE;
}
private static class JacksonHolder {
private static final ObjectMapper INSTANCE = new JacksonObjectMapper();
}
private static class JacksonObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 4288193147502386170L;
private static final Locale CHINA = Locale.CHINA;
public JacksonObjectMapper(ObjectMapper src) {
super(src);
}
public JacksonObjectMapper() {
super();
//设置地点为中国
super.setLocale(CHINA);
//去掉默认的时间戳格式
super.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//设置为中国上海时区
super.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
//序列化时,日期的统一格式
super.setDateFormat(new SimpleDateFormat(DateUtil.PATTERN_DATETIME, Locale.CHINA));
// 单引号
super.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 允许JSON字符串包含非引号控制字符值小于32的ASCII字符包含制表符和换行符
super.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
super.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true);
super.findAndRegisterModules();
//失败处理
super.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
super.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//单引号处理
super.configure(JsonReadFeature.ALLOW_SINGLE_QUOTES.mappedFeature(), true);
//反序列化时属性不存在的兼容处理s
super.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
//日期格式化
super.registerModule(new BladeJavaTimeModule());
super.findAndRegisterModules();
}
@Override
public ObjectMapper copy() {
return new JacksonObjectMapper(this);
}
}
}

View File

@@ -0,0 +1,133 @@
/**
* 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 服务对 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();
// 大数字 转 字符串
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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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))
);
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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];
}
}

View File

@@ -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;
}
}

View File

@@ -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<>();
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}

View File

@@ -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() {
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,239 @@
/**
* 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();
}
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;
}
}

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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 an 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 an 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));
}
}

View File

@@ -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();
}
}

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,139 @@
/**
* 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.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.Method;
/**
* 类操作工具
*
* @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 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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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(DateUtil.PATTERN_DATETIME);
public static final DateTimeFormatter DATETIME_MINI_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME_MINI);
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE);
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.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(new Date(), "yyyyMMdd");
}
/**
* 获取今天的时间
*
* @return 时间
*/
public static String time() {
return format(new Date(), PATTERN_DATETIME_MINI);
}
/**
* 获取今天的小时数
*
* @return 时间
*/
public static Integer hour() {
return NumberUtil.toInt(format(new Date(), "HH"));
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,392 @@
/**
* 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 {
/**
* 默认为true
*
* @author L.cm
*/
public static class TrueFilter implements FileFilter, Serializable {
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();
int dotIndex = fileName.lastIndexOf(CharPool.DOT);
return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
}
/**
* 获取文件名,去除后缀名
* @param fullName 文件全名
* @return {String}
*/
public static String getNameWithoutExtension(String fullName) {
if (StringUtil.isBlank(fullName)) return StringPool.EMPTY;
String fileName = new File(fullName).getName();
int dotIndex = fileName.lastIndexOf(CharPool.DOT);
return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
}
/**
* 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

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -0,0 +1,498 @@
/**
* 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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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
*/
public final class ImageUtil {
/**
* Logger for this class
*/
private static Logger LOGGER = LoggerFactory.getLogger(ImageUtil.class);
/**
* 默认输出图片类型
*/
public static final String DEFAULT_IMG_TYPE = "JPEG";
private ImageUtil() {
}
/**
* 转换输入流到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) {
LOGGER.error("Error readImage", e);
}
return null;
}
/**
* 获取图像内容
*
* @param srcImageFile 文件
* @return BufferedImage
*/
public static BufferedImage readImage(File srcImageFile) {
try {
return ImageIO.read(srcImageFile);
} catch (IOException e) {
LOGGER.error("Error readImage", e);
}
return null;
}
/**
* 获取图像内容
*
* @param srcInputStream 输入流
* @return BufferedImage
*/
public static BufferedImage readImage(InputStream srcInputStream) {
try {
return ImageIO.read(srcInputStream);
} catch (IOException e) {
LOGGER.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) {
LOGGER.error("Error readImage", e);
}
return null;
}
/**
* 缩放图像(按比例缩放)
*
* @param src 源图像
* @param output 输出流
* @param type 类型
* @param scale 缩放比例
* @param flag 缩放选择:true 放大; false 缩小;
*/
public final 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) {
LOGGER.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 final 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) {
LOGGER.error("Error in zoom image", e);
}
}
/**
* 图像裁剪(按指定起点坐标和宽高切割)
*
* @param src 源图像
* @param output 切片后的图像地址
* @param type 类型
* @param x 目标切片起点坐标X
* @param y 目标切片起点坐标Y
* @param width 目标切片宽度
* @param height 目标切片高度
*/
public final 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) {
LOGGER.error("Error in cut image", e);
}
}
/**
* 图像切割(指定切片的行数和列数)
*
* @param src 源图像地址
* @param mos 切片目标文件夹
* @param type 类型
* @param prows 目标切片行数。默认2必须是范围 [1, 20] 之内
* @param pcols 目标切片列数。默认2必须是范围 [1, 20] 之内
*/
public final 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) {
LOGGER.error("Error in slice image", e);
}
}
/**
* 图像切割(指定切片的宽度和高度)
*
* @param src 源图像地址
* @param mos 切片目标文件夹
* @param type 类型
* @param pdestWidth 目标切片宽度。默认200
* @param pdestHeight 目标切片高度。默认150
*/
public final 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) {
LOGGER.error("Error in slice image", e);
}
}
/**
* 图像类型转换GIF-JPG、GIF-PNG、PNG-JPG、PNG-GIF(X)、BMP-PNG
*
* @param src 源图像地址
* @param formatName 包含格式非正式名称的 String如JPG、JPEG、GIF等
* @param output 目标图像地址
*/
public final static void convert(BufferedImage src, OutputStream output, String formatName) {
try {
// 输出为文件
ImageIO.write(src, formatName, output);
// 关闭流
output.close();
} catch (Exception e) {
LOGGER.error("Error in convert image", e);
}
}
/**
* 彩色转为黑白
*
* @param src 源图像地址
* @param output 目标图像地址
* @param type 类型
*/
public final 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) {
LOGGER.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 final 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) {
LOGGER.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 final 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) {
LOGGER.error("Error imageStamp", e);
}
}
/**
* 计算text的长度一个中文算两个字符
*
* @param text text
* @return int
*/
public final static int calcTextWidth(String text) {
int length = 0;
for (int i = 0; i < text.length(); i++) {
if (new String(text.charAt(i) + "").getBytes().length > 1) {
length += 2;
} else {
length += 1;
}
}
return length / 2;
}
/**
* 默认字符串
* @param str 字符串
* @param defaultStr 默认值
* @return
*/
public static String defaultString(String str, String defaultStr) {
return ((str == null) ? defaultStr : str);
}
}

View File

@@ -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;
}

View File

@@ -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));
}
}
}

View File

@@ -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: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import java.io.Serializable;
import java.util.function.Supplier;
/**
* Holder of a value that is computed lazy.
*
* @author L.cm
*/
public class Lazy<T> implements Supplier<T>, Serializable {
@Nullable
private transient volatile Supplier<? extends T> supplier;
@Nullable
private T value;
/**
* Creates new instance of Lazy.
*
* @param supplier Supplier
* @param <T> 泛型标记
* @return Lazy
*/
public static <T> Lazy<T> of(final Supplier<T> supplier) {
return new Lazy<>(supplier);
}
private Lazy(final Supplier<T> supplier) {
this.supplier = supplier;
}
/**
* Returns the value. Value will be computed on first call.
*
* @return lazy value
*/
@Nullable
@Override
public T get() {
return (supplier == null) ? value : computeValue();
}
@Nullable
private synchronized T computeValue() {
final Supplier<? extends T> s = supplier;
if (s != null) {
value = s.get();
supplier = null;
}
return value;
}
}

View File

@@ -0,0 +1,66 @@
/**
* 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 jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* MultipartUtils
*
* @author Chill
*/
public class MultipartUtil {
/**
* 从HttpServletRequest中解析并返回所有的MultipartFile
*
* @param request HttpServletRequest对象应为MultipartHttpServletRequest类型
* @return 包含所有MultipartFile的列表如果没有文件或请求不是多部分请求则返回空列表
*/
public static List<MultipartFile> extractMultipartFiles(HttpServletRequest request) {
List<MultipartFile> files = new ArrayList<>();
if (request instanceof MultipartHttpServletRequest multipartRequest) {
// 获取所有文件的映射
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
// 遍历映射并将所有MultipartFile添加到列表中
for (MultipartFile file : fileMap.values()) {
if (file != null && !file.isEmpty()) {
files.add(file);
}
}
}
return files;
}
}

View File

@@ -0,0 +1,248 @@
/**
* 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;
/**
* 数字类型工具类
*
* @author L.cm
*/
public class NumberUtil extends org.springframework.util.NumberUtils {
//-----------------------------------------------------------------------
/**
* <p>Convert a <code>String</code> to an <code>int</code>, returning
* <code>zero</code> if the conversion fails.</p>
*
* <p>If the string is <code>null</code>, <code>zero</code> is returned.</p>
*
* <pre>
* NumberUtil.toInt(null) = 0
* NumberUtil.toInt("") = 0
* NumberUtil.toInt("1") = 1
* </pre>
*
* @param str the string to convert, may be null
* @return the int represented by the string, or <code>zero</code> if
* conversion fails
*/
public static int toInt(final String str) {
return toInt(str, -1);
}
/**
* <p>Convert a <code>String</code> to an <code>int</code>, returning a
* default value if the conversion fails.</p>
*
* <p>If the string is <code>null</code>, the default value is returned.</p>
*
* <pre>
* NumberUtil.toInt(null, 1) = 1
* NumberUtil.toInt("", 1) = 1
* NumberUtil.toInt("1", 0) = 1
* </pre>
*
* @param str the string to convert, may be null
* @param defaultValue the default value
* @return the int represented by the string, or the default if conversion fails
*/
public static int toInt(@Nullable final String str, final int defaultValue) {
if (str == null) {
return defaultValue;
}
try {
return Integer.valueOf(str);
} catch (final NumberFormatException nfe) {
return defaultValue;
}
}
/**
* <p>Convert a <code>String</code> to a <code>long</code>, returning
* <code>zero</code> if the conversion fails.</p>
*
* <p>If the string is <code>null</code>, <code>zero</code> is returned.</p>
*
* <pre>
* NumberUtil.toLong(null) = 0L
* NumberUtil.toLong("") = 0L
* NumberUtil.toLong("1") = 1L
* </pre>
*
* @param str the string to convert, may be null
* @return the long represented by the string, or <code>0</code> if
* conversion fails
*/
public static long toLong(final String str) {
return toLong(str, 0L);
}
/**
* <p>Convert a <code>String</code> to a <code>long</code>, returning a
* default value if the conversion fails.</p>
*
* <p>If the string is <code>null</code>, the default value is returned.</p>
*
* <pre>
* NumberUtil.toLong(null, 1L) = 1L
* NumberUtil.toLong("", 1L) = 1L
* NumberUtil.toLong("1", 0L) = 1L
* </pre>
*
* @param str the string to convert, may be null
* @param defaultValue the default value
* @return the long represented by the string, or the default if conversion fails
*/
public static long toLong(@Nullable final String str, final long defaultValue) {
if (str == null) {
return defaultValue;
}
try {
return Long.valueOf(str);
} catch (final NumberFormatException nfe) {
return defaultValue;
}
}
/**
* <p>Convert a <code>String</code> to a <code>Double</code>
*
* @param value value
* @return double value
*/
public static Double toDouble(String value) {
return toDouble(value, null);
}
/**
* <p>Convert a <code>String</code> to a <code>Double</code>
*
* @param value value
* @param defaultValue 默认值
* @return double value
*/
public static Double toDouble(@Nullable String value, Double defaultValue) {
if (value != null) {
return Double.valueOf(value.trim());
}
return defaultValue;
}
/**
* <p>Convert a <code>String</code> to a <code>Double</code>
*
* @param value value
* @return double value
*/
public static Float toFloat(String value) {
return toFloat(value, null);
}
/**
* <p>Convert a <code>String</code> to a <code>Double</code>
*
* @param value value
* @param defaultValue 默认值
* @return double value
*/
public static Float toFloat(@Nullable String value, Float defaultValue) {
if (value != null) {
return Float.valueOf(value.trim());
}
return defaultValue;
}
/**
* All possible chars for representing a number as a String
*/
private final static char[] DIGITS = {
'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z'
};
/**
* 将 long 转短字符串 为 62 进制
*
* @param i 数字
* @return 短字符串
*/
public static String to62String(long i) {
int radix = DIGITS.length;
char[] buf = new char[65];
int charPos = 64;
i = -i;
while (i <= -radix) {
buf[charPos--] = DIGITS[(int) (-(i % radix))];
i = i / radix;
}
buf[charPos] = DIGITS[(int) (-i)];
return new String(buf, charPos, (65 - charPos));
}
/**
* 根据指定的分隔符分割字符串并尝试解析为整数。
*
* @param str 需要分割和解析的字符串
* @return 分割后数组的第一个元素转换成的整数或者在无法解析时返回0
*/
public static int parseFirstInt(String str) {
// 定义分隔符数组
String[] delimiters = {"-", ":", ","};
// 对每个分隔符进行处理
for (String delimiter : delimiters) {
String[] parts = str.split(delimiter);
// 如果分割后数组长度大于2尝试解析第一个元素
if (parts.length >= 2) {
try {
return Integer.parseInt(parts[0].trim());
} catch (NumberFormatException e) {
// 如果解析失败返回0
return 999;
}
}
}
// 如果没有分隔符或者分割后数组长度不足2返回999
return 999;
}
}

View File

@@ -0,0 +1,46 @@
/**
* 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;
/**
* 对象工具类
*
* @author L.cm
*/
public class ObjectUtil extends org.springframework.util.ObjectUtils {
/**
* 判断元素不为空
* @param obj object
* @return boolean
*/
public static boolean isNotEmpty(@Nullable Object obj) {
return !ObjectUtil.isEmpty(obj);
}
}

View File

@@ -0,0 +1,64 @@
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
import java.io.File;
import java.net.URL;
/**
* 用来获取各种目录
*
* @author L.cm
*/
public class PathUtil {
public static final String FILE_PROTOCOL = "file";
public static final String JAR_PROTOCOL = "jar";
public static final String ZIP_PROTOCOL = "zip";
public static final String FILE_PROTOCOL_PREFIX = "file:";
public static final String JAR_FILE_SEPARATOR = "!/";
/**
* 获取jar包运行时的当前目录
*
* @return {String}
*/
@Nullable
public static String getJarPath() {
try {
URL url = PathUtil.class.getResource(StringPool.SLASH).toURI().toURL();
return PathUtil.toFilePath(url);
} catch (Exception e) {
String path = PathUtil.class.getResource(StringPool.EMPTY).getPath();
return new File(path).getParentFile().getParentFile().getAbsolutePath();
}
}
/**
* 转换为文件路径
*
* @param url 路径
* @return {String}
*/
@Nullable
public static String toFilePath(@Nullable URL url) {
if (url == null) {
return null;
}
String protocol = url.getProtocol();
String file = UrlUtil.decode(url.getPath(), Charsets.UTF_8);
if (FILE_PROTOCOL.equals(protocol)) {
return new File(file).getParentFile().getParentFile().getAbsolutePath();
} else if (JAR_PROTOCOL.equals(protocol) || ZIP_PROTOCOL.equals(protocol)) {
int ipos = file.indexOf(JAR_FILE_SEPARATOR);
if (ipos > 0) {
file = file.substring(0, ipos);
}
if (file.startsWith(FILE_PROTOCOL_PREFIX)) {
file = file.substring(FILE_PROTOCOL_PREFIX.length());
}
return new File(file).getParentFile().getAbsolutePath();
}
return file;
}
}

View File

@@ -0,0 +1,61 @@
package org.springblade.core.tool.utils;
import java.util.Arrays;
/**
* 提供基于 PKCS7 算法的加解密接口.
* <p>
* 参考自jFinal 方便使用
*
* @author L.cm
*/
public class Pkcs7Encoder {
/**
* 默认为 16保持跟其他语言的一致性
*/
private static final int BLOCK_SIZE = 16;
/**
* PKCS7 编码 padding 补位
*
* @param src 原数据
* @return padding 补位
*/
public static byte[] encode(byte[] src) {
int count = src.length;
// 计算需要填充的位数
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
if (amountToPad == 0) {
amountToPad = BLOCK_SIZE;
}
// 获得补位所用的字符
byte pad = (byte) (amountToPad & 0xFF);
byte[] pads = new byte[amountToPad];
for (int index = 0; index < amountToPad; index++) {
pads[index] = pad;
}
int length = count + amountToPad;
byte[] dest = new byte[length];
System.arraycopy(src, 0, dest, 0, count);
System.arraycopy(pads, 0, dest, count, amountToPad);
return dest;
}
/**
* PKCS7 解码
*
* @param decrypted 编码的数据
* @return 解码后的数据
*/
public static byte[] decode(byte[] decrypted) {
int pad = decrypted[decrypted.length - 1];
if (pad < 1 || pad > BLOCK_SIZE) {
pad = 0;
}
if (pad > 0) {
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
}
return decrypted;
}
}

View File

@@ -0,0 +1,152 @@
package org.springblade.core.tool.utils;
import java.util.Map;
import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* 占位符解析器
*
* @author meilin.huang, chill
*/
public class PlaceholderUtil {
/**
* 默认前缀占位符
*/
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
/**
* 默认后缀占位符
*/
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
/**
* 默认单例解析器
*/
private static final PlaceholderUtil DEFAULT_RESOLVER = new PlaceholderUtil();
/**
* 占位符前缀
*/
private String placeholderPrefix = DEFAULT_PLACEHOLDER_PREFIX;
/**
* 占位符后缀
*/
private String placeholderSuffix = DEFAULT_PLACEHOLDER_SUFFIX;
private PlaceholderUtil() {
}
private PlaceholderUtil(String placeholderPrefix, String placeholderSuffix) {
this.placeholderPrefix = placeholderPrefix;
this.placeholderSuffix = placeholderSuffix;
}
/**
* 获取默认的占位符解析器,即占位符前缀为"${", 后缀为"}"
*
* @return PlaceholderUtil
*/
public static PlaceholderUtil getDefaultResolver() {
return DEFAULT_RESOLVER;
}
public static PlaceholderUtil getResolver(String placeholderPrefix, String placeholderSuffix) {
return new PlaceholderUtil(placeholderPrefix, placeholderSuffix);
}
/**
* 解析带有指定占位符的模板字符串,默认占位符为前缀:${ 后缀:}<br/><br/>
* 如template = category:${}:product:${}<br/>
* values = {"1", "2"}<br/>
* 返回 category:1:product:2<br/>
*
* @param content 要解析的带有占位符的模板字符串
* @param values 按照模板占位符索引位置设置对应的值
* @return {String}
*/
public String resolve(String content, String... values) {
int start = content.indexOf(this.placeholderPrefix);
if (start == -1) {
return content;
}
//值索引
int valueIndex = 0;
StringBuilder result = new StringBuilder(content);
while (start != -1) {
int end = result.indexOf(this.placeholderSuffix);
String replaceContent = values[valueIndex++];
result.replace(start, end + this.placeholderSuffix.length(), replaceContent);
start = result.indexOf(this.placeholderPrefix, start + replaceContent.length());
}
return result.toString();
}
/**
* 解析带有指定占位符的模板字符串,默认占位符为前缀:${ 后缀:}<br/><br/>
* 如template = category:${}:product:${}<br/>
* values = {"1", "2"}<br/>
* 返回 category:1:product:2<br/>
*
* @param content 要解析的带有占位符的模板字符串
* @param values 按照模板占位符索引位置设置对应的值
* @return {String}
*/
public String resolve(String content, Object[] values) {
return resolve(content, Stream.of(values).map(String::valueOf).toArray(String[]::new));
}
/**
* 根据替换规则来替换指定模板中的占位符值
*
* @param content 要解析的字符串
* @param rule 解析规则回调
* @return {String}
*/
public String resolveByRule(String content, Function<String, String> rule) {
int start = content.indexOf(this.placeholderPrefix);
if (start == -1) {
return content;
}
StringBuilder result = new StringBuilder(content);
while (start != -1) {
int end = result.indexOf(this.placeholderSuffix, start + 1);
//获取占位符属性值,如${id}, 即获取id
String placeholder = result.substring(start + this.placeholderPrefix.length(), end);
//替换整个占位符内容,即将${id}值替换为替换规则回调中的内容
String replaceContent = placeholder.trim().isEmpty() ? "" : rule.apply(placeholder);
result.replace(start, end + this.placeholderSuffix.length(), replaceContent);
start = result.indexOf(this.placeholderPrefix, start + replaceContent.length());
}
return result.toString();
}
/**
* 替换模板中占位符内容占位符的内容即为map key对应的值key为占位符中的内容。<br/><br/>
* 如content = product:${id}:detail:${did}<br/>
* valueMap = id -> 1; pid -> 2<br/>
* 经过解析返回 product:1:detail:2<br/>
*
* @param content 模板内容
* @param valueMap 值映射
* @return 替换完成后的字符串
*/
public String resolveByMap(String content, final Map<String, Object> valueMap) {
return resolveByRule(content, placeholderValue -> String.valueOf(valueMap.get(placeholderValue)));
}
/**
* 根据properties文件替换占位符内容
*
* @param content 模板内容
* @param properties 配置
* @return {String}
*/
public String resolveByProperties(String content, final Properties properties) {
return resolveByRule(content, properties::getProperty);
}
}

View File

@@ -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 io.protostuff.LinkedBuffer;
import io.protostuff.ProtobufIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* Protostuff 工具类
*
* @author L.cm
*/
public class ProtostuffUtil {
/**
* 避免每次序列化都重新申请Buffer空间
*/
private static final LinkedBuffer BUFFER = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
/**
* 缓存Schema
*/
private static final Map<Class<?>, Schema<?>> SCHEMA_CACHE = new ConcurrentHashMap<>();
/**
* 序列化方法,把指定对象序列化成字节数组
*
* @param obj obj
* @param <T> T
* @return byte[]
*/
@SuppressWarnings("unchecked")
public static <T> byte[] serialize(T obj) {
Class<T> clazz = (Class<T>) obj.getClass();
Schema<T> schema = getSchema(clazz);
byte[] data;
try {
data = ProtobufIOUtil.toByteArray(obj, schema, BUFFER);
} finally {
BUFFER.clear();
}
return data;
}
/**
* 反序列化方法将字节数组反序列化成指定Class类型
*
* @param data data
* @param clazz clazz
* @param <T> T
* @return T
*/
public static <T> T deserialize(byte[] data, Class<T> clazz) {
Schema<T> schema = getSchema(clazz);
T obj = schema.newMessage();
ProtobufIOUtil.mergeFrom(data, obj, schema);
return obj;
}
/**
* 获取Schema
* @param clazz clazz
* @param <T> T
* @return T
*/
@SuppressWarnings("unchecked")
private static <T> Schema<T> getSchema(Class<T> clazz) {
Schema<T> schema = (Schema<T>) SCHEMA_CACHE.get(clazz);
if (Objects.isNull(schema)) {
//这个schema通过RuntimeSchema进行懒创建并缓存
//所以可以一直调用RuntimeSchema.getSchema(),这个方法是线程安全的
schema = RuntimeSchema.getSchema(clazz);
if (Objects.nonNull(schema)) {
SCHEMA_CACHE.put(clazz, schema);
}
}
return schema;
}
}

View File

@@ -0,0 +1,54 @@
/**
* 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.Getter;
import lombok.RequiredArgsConstructor;
/**
* 生成的随机数类型
*
* @author L.cm
*/
@Getter
@RequiredArgsConstructor
public enum RandomType {
/**
* INT STRING ALL
*/
INT(RandomType.INT_STR),
STRING(RandomType.STR_STR),
ALL(RandomType.ALL_STR);
private final String factor;
/**
* 随机字符串因子
*/
private static final String INT_STR = "0123456789";
private static final String STR_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
private static final String ALL_STR = INT_STR + STR_STR;
}

View File

@@ -0,0 +1,189 @@
/**
* 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.beans.BeansException;
import org.springframework.cglib.core.CodeGenerationException;
import org.springframework.core.convert.Property;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.lang.Nullable;
import org.springframework.util.ReflectionUtils;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 反射工具类
*
* @author L.cm
*/
public class ReflectUtil extends ReflectionUtils {
/**
* 获取 Bean 的所有 get方法
*
* @param type 类
* @return PropertyDescriptor数组
*/
public static PropertyDescriptor[] getBeanGetters(Class type) {
return getPropertiesHelper(type, true, false);
}
/**
* 获取 Bean 的所有 set方法
*
* @param type 类
* @return PropertyDescriptor数组
*/
public static PropertyDescriptor[] getBeanSetters(Class type) {
return getPropertiesHelper(type, false, true);
}
/**
* 获取 Bean 的所有 PropertyDescriptor
*
* @param type 类
* @param read 读取方法
* @param write 写方法
* @return PropertyDescriptor数组
*/
public static PropertyDescriptor[] getPropertiesHelper(Class type, boolean read, boolean write) {
try {
PropertyDescriptor[] all = BeanUtil.getPropertyDescriptors(type);
if (read && write) {
return all;
} else {
List<PropertyDescriptor> properties = new ArrayList<>(all.length);
for (PropertyDescriptor pd : all) {
if (read && pd.getReadMethod() != null) {
properties.add(pd);
} else if (write && pd.getWriteMethod() != null) {
properties.add(pd);
}
}
return properties.toArray(new PropertyDescriptor[0]);
}
} catch (BeansException ex) {
throw new CodeGenerationException(ex);
}
}
/**
* 获取 bean 的属性信息
* @param propertyType 类型
* @param propertyName 属性名
* @return {Property}
*/
@Nullable
public static Property getProperty(Class<?> propertyType, String propertyName) {
PropertyDescriptor propertyDescriptor = BeanUtil.getPropertyDescriptor(propertyType, propertyName);
if (propertyDescriptor == null) {
return null;
}
return ReflectUtil.getProperty(propertyType, propertyDescriptor, propertyName);
}
/**
* 获取 bean 的属性信息
* @param propertyType 类型
* @param propertyDescriptor PropertyDescriptor
* @param propertyName 属性名
* @return {Property}
*/
public static Property getProperty(Class<?> propertyType, PropertyDescriptor propertyDescriptor, String propertyName) {
Method readMethod = propertyDescriptor.getReadMethod();
Method writeMethod = propertyDescriptor.getWriteMethod();
return new Property(propertyType, readMethod, writeMethod, propertyName);
}
/**
* 获取 bean 的属性信息
* @param propertyType 类型
* @param propertyName 属性名
* @return {Property}
*/
@Nullable
public static TypeDescriptor getTypeDescriptor(Class<?> propertyType, String propertyName) {
Property property = ReflectUtil.getProperty(propertyType, propertyName);
if (property == null) {
return null;
}
return new TypeDescriptor(property);
}
/**
* 获取 类属性信息
* @param propertyType 类型
* @param propertyDescriptor PropertyDescriptor
* @param propertyName 属性名
* @return {Property}
*/
public static TypeDescriptor getTypeDescriptor(Class<?> propertyType, PropertyDescriptor propertyDescriptor, String propertyName) {
Method readMethod = propertyDescriptor.getReadMethod();
Method writeMethod = propertyDescriptor.getWriteMethod();
Property property = new Property(propertyType, readMethod, writeMethod, propertyName);
return new TypeDescriptor(property);
}
/**
* 获取 类属性
* @param clazz 类信息
* @param fieldName 属性名
* @return Field
*/
@Nullable
public static Field getField(Class<?> clazz, String fieldName) {
while (clazz != Object.class) {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
clazz = clazz.getSuperclass();
}
}
return null;
}
/**
* 获取 所有 field 属性上的注解
* @param clazz 类
* @param fieldName 属性名
* @param annotationClass 注解
* @param <T> 注解泛型
* @return 注解
*/
@Nullable
public static <T extends Annotation> T getAnnotation(Class<?> clazz, String fieldName, Class<T> annotationClass) {
Field field = ReflectUtil.getField(clazz, fieldName);
if (field == null) {
return null;
}
return field.getAnnotation(annotationClass);
}
}

View File

@@ -0,0 +1,123 @@
/**
* 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.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 正则表达式工具
*
* @author L.cm
*/
public class RegexUtil {
/**
* 用户名
*/
public static final String USER_NAME = "^[a-zA-Z\\u4E00-\\u9FA5][a-zA-Z0-9_\\u4E00-\\u9FA5]{1,11}$";
/**
* 密码
*/
public static final String USER_PASSWORD = "^.{6,32}$";
/**
* 邮箱
*/
public static final String EMAIL = "^\\w+([-+.]*\\w+)*@([\\da-z](-[\\da-z])?)+(\\.{1,2}[a-z]+)+$";
/**
* 手机号
*/
public static final String PHONE = "^1[3456789]\\d{9}$";
/**
* 手机号或者邮箱
*/
public static final String EMAIL_OR_PHONE = EMAIL + "|" + PHONE;
/**
* URL路径
*/
public static final String URL = "^(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})(:[\\d]+)?([\\/\\w\\.-]*)*\\/?$";
/**
* 身份证校验,初级校验,具体规则有一套算法
*/
public static final String ID_CARD = "^\\d{15}$|^\\d{17}([0-9]|X)$";
/**
* 域名校验
*/
public static final String DOMAIN = "^[0-9a-zA-Z]+[0-9a-zA-Z\\.-]*\\.[a-zA-Z]{2,4}$";
/**
* 编译传入正则表达式和字符串去匹配,忽略大小写
*
* @param regex 正则
* @param beTestString 字符串
* @return {boolean}
*/
public static boolean match(String regex, String beTestString) {
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(beTestString);
return matcher.matches();
}
/**
* 编译传入正则表达式在字符串中寻找如果匹配到则为true
*
* @param regex 正则
* @param beTestString 字符串
* @return {boolean}
*/
public static boolean find(String regex, String beTestString) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(beTestString);
return matcher.find();
}
/**
* 编译传入正则表达式在字符串中寻找,如果找到返回第一个结果
* 找不到返回null
*
* @param regex 正则
* @param beFoundString 字符串
* @return {boolean}
*/
@Nullable
public static String findResult(String regex, String beFoundString) {
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(beFoundString);
if (matcher.find()) {
return matcher.group();
}
return null;
}
}

View File

@@ -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: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.Assert;
import java.io.IOException;
/**
* 资源工具类
*
* @author L.cm
*/
public class ResourceUtil extends org.springframework.util.ResourceUtils {
public static final String HTTP_REGEX = "^https?:.+$";
public static final String FTP_URL_PREFIX = "ftp:";
/**
* 获取资源
* <p>
* 支持一下协议:
* <p>
* 1. classpath:
* 2. file:
* 3. ftp:
* 4. http: and https:
* 5. classpath*:
* 6. C:/dir1/ and /Users/lcm
* </p>
*
* @param resourceLocation 资源路径
* @return {Resource}
* @throws IOException IOException
*/
public static Resource getResource(String resourceLocation) throws IOException {
Assert.notNull(resourceLocation, "Resource location must not be null");
if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
return new ClassPathResource(path);
}
if (resourceLocation.startsWith(FTP_URL_PREFIX)) {
return new UrlResource(resourceLocation);
}
if (resourceLocation.matches(HTTP_REGEX)) {
return new UrlResource(resourceLocation);
}
if (resourceLocation.startsWith(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX)) {
return SpringUtil.getContext().getResource(resourceLocation);
}
return new FileSystemResource(resourceLocation);
}
}

View File

@@ -0,0 +1,390 @@
/**
* 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.Base64Utils;
import org.springblade.core.tool.tuple.KeyPair;
import javax.crypto.Cipher;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.*;
import java.util.Objects;
/**
* RSA加、解密工具
*
* <p>
* 1. 公钥负责加密,私钥负责解密;
* 2. 私钥负责签名,公钥负责验证。
* </p>
*
* @author L.cm
*/
public class RsaUtil {
/**
* 数字签名,密钥算法
*/
public static final String RSA_ALGORITHM = "RSA";
public static final String RSA_PADDING = "RSA/ECB/PKCS1Padding";
/**
* 获取 KeyPair
*
* @return KeyPair
*/
public static KeyPair genKeyPair() {
return genKeyPair(1024);
}
/**
* 获取 KeyPair
*
* @param keySize key size
* @return KeyPair
*/
public static KeyPair genKeyPair(int keySize) {
try {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(RSA_ALGORITHM);
// 密钥位数
keyPairGen.initialize(keySize);
// 密钥对
return new KeyPair(keyPairGen.generateKeyPair());
} catch (NoSuchAlgorithmException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 生成RSA私钥
*
* @param modulus N特征值
* @param exponent d特征值
* @return {@link PrivateKey}
*/
public static PrivateKey generatePrivateKey(String modulus, String exponent) {
return generatePrivateKey(new BigInteger(modulus), new BigInteger(exponent));
}
/**
* 生成RSA私钥
*
* @param modulus N特征值
* @param exponent d特征值
* @return {@link PrivateKey}
*/
public static PrivateKey generatePrivateKey(BigInteger modulus, BigInteger exponent) {
RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(modulus, exponent);
try {
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 生成RSA公钥
*
* @param modulus N特征值
* @param exponent e特征值
* @return {@link PublicKey}
*/
public static PublicKey generatePublicKey(String modulus, String exponent) {
return generatePublicKey(new BigInteger(modulus), new BigInteger(exponent));
}
/**
* 生成RSA公钥
*
* @param modulus N特征值
* @param exponent e特征值
* @return {@link PublicKey}
*/
public static PublicKey generatePublicKey(BigInteger modulus, BigInteger exponent) {
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent);
try {
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 得到公钥
*
* @param base64PubKey 密钥字符串经过base64编码
* @return PublicKey
*/
public static PublicKey getPublicKey(String base64PubKey) {
Objects.requireNonNull(base64PubKey, "base64 public key is null.");
byte[] keyBytes = Base64Util.decodeFromString(base64PubKey);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
try {
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 得到公钥字符串
*
* @param base64PubKey 密钥字符串经过base64编码
* @return PublicKey String
*/
public static String getPublicKeyToBase64(String base64PubKey) {
PublicKey publicKey = getPublicKey(base64PubKey);
return getKeyString(publicKey);
}
/**
* 得到私钥
*
* @param base64PriKey 密钥字符串经过base64编码
* @return PrivateKey
*/
public static PrivateKey getPrivateKey(String base64PriKey) {
Objects.requireNonNull(base64PriKey, "base64 private key is null.");
byte[] keyBytes = Base64Util.decodeFromString(base64PriKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
try {
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw Exceptions.unchecked(e);
}
}
/**
* 得到密钥字符串经过base64编码
*
* @param key key
* @return base 64 编码后的 key
*/
public static String getKeyString(Key key) {
return Base64Util.encodeToString(key.getEncoded());
}
/**
* 得到私钥 base64
*
* @param base64PriKey 密钥字符串经过base64编码
* @return PrivateKey String
*/
public static String getPrivateKeyToBase64(String base64PriKey) {
PrivateKey privateKey = getPrivateKey(base64PriKey);
return getKeyString(privateKey);
}
/**
* 共要加密
*
* @param base64PublicKey base64 的公钥
* @param data 待加密的内容
* @return 加密后的内容
*/
public static byte[] encrypt(String base64PublicKey, byte[] data) {
return encrypt(getPublicKey(base64PublicKey), data);
}
/**
* 共要加密
*
* @param publicKey 公钥
* @param data 待加密的内容
* @return 加密后的内容
*/
public static byte[] encrypt(PublicKey publicKey, byte[] data) {
return rsa(publicKey, data, Cipher.ENCRYPT_MODE);
}
/**
* 私钥加密,用于 qpp 内,公钥解密
*
* @param base64PrivateKey base64 的私钥
* @param data 待加密的内容
* @return 加密后的内容
*/
public static byte[] encryptByPrivateKey(String base64PrivateKey, byte[] data) {
return encryptByPrivateKey(getPrivateKey(base64PrivateKey), data);
}
/**
* 私钥加密,加密成 base64 字符串,用于 qpp 内,公钥解密
*
* @param base64PrivateKey base64 的私钥
* @param data 待加密的内容
* @return 加密后的内容
*/
public static String encryptByPrivateKeyToBase64(String base64PrivateKey, byte[] data) {
return Base64Util.encodeToString(encryptByPrivateKey(base64PrivateKey, data));
}
/**
* 私钥加密,用于 qpp 内,公钥解密
*
* @param privateKey 私钥
* @param data 待加密的内容
* @return 加密后的内容
*/
public static byte[] encryptByPrivateKey(PrivateKey privateKey, byte[] data) {
return rsa(privateKey, data, Cipher.ENCRYPT_MODE);
}
/**
* 公钥加密
*
* @param base64PublicKey base64 公钥
* @param data 待加密的内容
* @return 加密后的内容
*/
@Nullable
public static String encryptToBase64(String base64PublicKey, @Nullable String data) {
if (StringUtil.isBlank(data)) {
return null;
}
return Base64Util.encodeToString(encrypt(base64PublicKey, data.getBytes(Charsets.UTF_8)));
}
/**
* 解密
*
* @param base64PrivateKey base64 私钥
* @param data 数据
* @return 解密后的数据
*/
public static byte[] decrypt(String base64PrivateKey, byte[] data) {
return decrypt(getPrivateKey(base64PrivateKey), data);
}
/**
* 解密
*
* @param base64publicKey base64 公钥
* @param data 数据
* @return 解密后的数据
*/
public static byte[] decryptByPublicKey(String base64publicKey, byte[] data) {
return decryptByPublicKey(getPublicKey(base64publicKey), data);
}
/**
* 解密
*
* @param privateKey privateKey
* @param data 数据
* @return 解密后的数据
*/
public static byte[] decrypt(PrivateKey privateKey, byte[] data) {
return rsa(privateKey, data, Cipher.DECRYPT_MODE);
}
/**
* 解密
*
* @param publicKey PublicKey
* @param data 数据
* @return 解密后的数据
*/
public static byte[] decryptByPublicKey(PublicKey publicKey, byte[] data) {
return rsa(publicKey, data, Cipher.DECRYPT_MODE);
}
/**
* rsa 加、解密
*
* @param key key
* @param data 数据
* @param mode 模式
* @return 解密后的数据
*/
private static byte[] rsa(Key key, byte[] data, int mode) {
try {
Cipher cipher = Cipher.getInstance(RSA_PADDING);
cipher.init(mode, key);
return cipher.doFinal(data);
} catch (Exception e) {
throw Exceptions.unchecked(e);
}
}
/**
* base64 数据解密
*
* @param base64PublicKey base64 公钥
* @param base64Data base64数据
* @return 解密后的数据
*/
public static byte[] decryptByPublicKeyFromBase64(String base64PublicKey, byte[] base64Data) {
return decryptByPublicKey(getPublicKey(base64PublicKey), base64Data);
}
/**
* base64 数据解密
*
* @param base64PrivateKey base64 私钥
* @param base64Data base64数据
* @return 解密后的数据
*/
@Nullable
public static String decryptFromBase64(String base64PrivateKey, @Nullable String base64Data) {
if (StringUtil.isBlank(base64Data)) {
return null;
}
return new String(decrypt(base64PrivateKey, Base64Util.decodeFromString(base64Data)), Charsets.UTF_8);
}
/**
* base64 数据解密
*
* @param base64PrivateKey base64 私钥
* @param base64Data base64数据
* @return 解密后的数据
*/
public static byte[] decryptFromBase64(String base64PrivateKey, byte[] base64Data) {
return decrypt(base64PrivateKey, Base64Util.decode(base64Data));
}
/**
* base64 数据解密
*
* @param base64PublicKey base64 公钥
* @param base64Data base64数据
* @return 解密后的数据
*/
@Nullable
public static String decryptByPublicKeyFromBase64(String base64PublicKey, @Nullable String base64Data) {
if (StringUtil.isBlank(base64Data)) {
return null;
}
return new String(decryptByPublicKeyFromBase64(base64PublicKey, Base64Util.decodeFromString(base64Data)), Charsets.UTF_8);
}
}

View File

@@ -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: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import java.lang.management.ManagementFactory;
import java.time.Duration;
import java.util.List;
/**
* 运行时工具类
*
* @author L.cm
*/
public class RuntimeUtil {
/**
* 获得当前进程的PID
* <p>
* 当失败时返回-1
*
* @return pid
*/
public static int getPid() {
// something like '<pid>@<hostname>', at least in SUN / Oracle JVMs
final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
final int index = jvmName.indexOf(CharPool.AT);
if (index > 0) {
return NumberUtil.toInt(jvmName.substring(0, index), -1);
}
return -1;
}
/**
* 返回应用启动到现在的时间
*
* @return {Duration}
*/
public static Duration getUpTime() {
long upTime = ManagementFactory.getRuntimeMXBean().getUptime();
return Duration.ofMillis(upTime);
}
/**
* 返回输入的JVM参数列表
*
* @return jvm参数
*/
public static String getJvmArguments() {
List<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
return StringUtil.join(vmArguments, StringPool.SPACE);
}
/**
* 获取CPU核数
*
* @return cpu count
*/
public static int getCpuNum() {
return Runtime.getRuntime().availableProcessors();
}
}

View File

@@ -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: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.lang.Nullable;
/**
* spring 工具类
*
* @author Chill
*/
@Slf4j
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
SpringUtil.context = context;
}
/**
* 获取bean
*
* @param clazz class类
* @param <T> 泛型
* @return T
*/
public static <T> T getBean(Class<T> clazz) {
if (clazz == null) {
return null;
}
return context.getBean(clazz);
}
/**
* 获取bean
*
* @param beanId beanId
* @param <T> 泛型
* @return T
*/
public static <T> T getBean(String beanId) {
if (beanId == null) {
return null;
}
return (T) context.getBean(beanId);
}
/**
* 获取bean
*
* @param beanName bean名称
* @param clazz class类
* @param <T> 泛型
* @return T
*/
public static <T> T getBean(String beanName, Class<T> clazz) {
if (null == beanName || "".equals(beanName.trim())) {
return null;
}
if (clazz == null) {
return null;
}
return (T) context.getBean(beanName, clazz);
}
/**
* 获取 ApplicationContext
*
* @return ApplicationContext
*/
public static ApplicationContext getContext() {
if (context == null) {
return null;
}
return context;
}
/**
* 发布事件
*
* @param event 事件
*/
public static void publishEvent(ApplicationEvent event) {
if (context == null) {
return;
}
try {
context.publishEvent(event);
} catch (Exception ex) {
log.error(ex.getMessage());
}
}
}

View File

@@ -0,0 +1,754 @@
/*
* Copyright (C) 2015 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package org.springblade.core.tool.utils;
import java.util.*;
import java.util.Spliterators.AbstractSpliterator;
import java.util.function.*;
import java.util.stream.*;
import static java.lang.Math.min;
import static java.util.Objects.requireNonNull;
/**
* Static utility methods related to {@code Stream} instances.
*
* @author Guava
*/
public class StreamUtil {
/**
* Returns a sequential {@link Stream} of the contents of {@code iterable}, delegating to {@link
* Collection#stream} if possible.
*/
public static <T> Stream<T> stream(Iterable<T> iterable) {
return (iterable instanceof Collection)
? ((Collection<T>) iterable).stream()
: StreamSupport.stream(iterable.spliterator(), false);
}
/**
* Returns a sequential {@link Stream} of the remaining contents of {@code iterator}. Do not use
* {@code iterator} directly after passing it to this method.
*/
public static <T> Stream<T> stream(Iterator<T> iterator) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
}
/**
* If a value is present in {@code optional}, returns a stream containing only that element,
* otherwise returns an empty stream.
*/
public static <T> Stream<T> stream(Optional<T> optional) {
return optional.map(Stream::of).orElseGet(Stream::empty);
}
/**
* If a value is present in {@code optional}, returns a stream containing only that element,
* otherwise returns an empty stream.
*
* <p><b>Java 9 users:</b> use {@code optional.stream()} instead.
*/
public static IntStream stream(OptionalInt optional) {
return optional.isPresent() ? IntStream.of(optional.getAsInt()) : IntStream.empty();
}
/**
* If a value is present in {@code optional}, returns a stream containing only that element,
* otherwise returns an empty stream.
*
* <p><b>Java 9 users:</b> use {@code optional.stream()} instead.
*/
public static LongStream stream(OptionalLong optional) {
return optional.isPresent() ? LongStream.of(optional.getAsLong()) : LongStream.empty();
}
/**
* If a value is present in {@code optional}, returns a stream containing only that element,
* otherwise returns an empty stream.
*
* <p><b>Java 9 users:</b> use {@code optional.stream()} instead.
*/
public static DoubleStream stream(OptionalDouble optional) {
return optional.isPresent() ? DoubleStream.of(optional.getAsDouble()) : DoubleStream.empty();
}
/**
* Returns a stream in which each element is the result of passing the corresponding element of
* each of {@code streamA} and {@code streamB} to {@code function}.
*
* <p>For example:
*
* <pre>{@code
* Streams.zip(
* Stream.of("foo1", "foo2", "foo3"),
* Stream.of("bar1", "bar2"),
* (arg1, arg2) -> arg1 + ":" + arg2)
* }</pre>
*
* <p>will return {@code Stream.of("foo1:bar1", "foo2:bar2")}.
*
* <p>The resulting stream will only be as long as the shorter of the two input streams; if one
* stream is longer, its extra elements will be ignored.
*
* <p>Note that if you are calling {@link Stream#forEach} on the resulting stream, you might want
* to consider using {@link #forEachPair} instead of this method.
*
* <p><b>Performance note:</b> The resulting stream is not <a
* href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>.
* This may harm parallel performance.
*/
public static <A, B, R> Stream<R> zip(Stream<A> streamA, Stream<B> streamB, BiFunction<? super A, ? super B, R> function) {
// same as Stream.concat
boolean isParallel = streamA.isParallel() || streamB.isParallel();
Spliterator<A> splitA = streamA.spliterator();
Spliterator<B> splitB = streamB.spliterator();
int characteristics =
splitA.characteristics()
& splitB.characteristics()
& (Spliterator.SIZED | Spliterator.ORDERED);
Iterator<A> itrA = Spliterators.iterator(splitA);
Iterator<B> itrB = Spliterators.iterator(splitB);
return StreamSupport.stream(
new AbstractSpliterator<R>(
min(splitA.estimateSize(), splitB.estimateSize()), characteristics) {
@Override
public boolean tryAdvance(Consumer<? super R> action) {
if (itrA.hasNext() && itrB.hasNext()) {
action.accept(function.apply(itrA.next(), itrB.next()));
return true;
}
return false;
}
},
isParallel)
.onClose(streamA::close)
.onClose(streamB::close);
}
/**
* Invokes {@code consumer} once for each pair of <i>corresponding</i> elements in {@code streamA}
* and {@code streamB}. If one stream is longer than the other, the extra elements are silently
* ignored. Elements passed to the consumer are guaranteed to come from the same position in their
* respective source streams. For example:
*
* <pre>{@code
* Streams.forEachPair(
* Stream.of("foo1", "foo2", "foo3"),
* Stream.of("bar1", "bar2"),
* (arg1, arg2) -> System.out.println(arg1 + ":" + arg2)
* }</pre>
*
* <p>will print:
*
* <pre>{@code
* foo1:bar1
* foo2:bar2
* }</pre>
*
* <p><b>Warning:</b> If either supplied stream is a parallel stream, the same correspondence
* between elements will be made, but the order in which those pairs of elements are passed to the
* consumer is <i>not</i> defined.
*
* <p>Note that many usages of this method can be replaced with simpler calls to {@link #zip}.
* This method behaves equivalently to {@linkplain #zip zipping} the stream elements into
* temporary pair objects and then using {@link Stream#forEach} on that stream.
*/
public static <A, B> void forEachPair(Stream<A> streamA, Stream<B> streamB, BiConsumer<? super A, ? super B> consumer) {
if (streamA.isParallel() || streamB.isParallel()) {
zip(streamA, streamB, TemporaryPair::new).forEach(pair -> consumer.accept(pair.a, pair.b));
} else {
Iterator<A> iterA = streamA.iterator();
Iterator<B> iterB = streamB.iterator();
while (iterA.hasNext() && iterB.hasNext()) {
consumer.accept(iterA.next(), iterB.next());
}
}
}
// Use this carefully - it doesn't implement value semantics
private static class TemporaryPair<A, B> {
final A a;
final B b;
TemporaryPair(A a, B b) {
this.a = a;
this.b = b;
}
}
/**
* Returns a stream consisting of the results of applying the given function to the elements of
* {@code stream} and their indices in the stream. For example,
*
* <pre>{@code
* mapWithIndex(
* Stream.of("a", "b", "c"),
* (e, index) -> index + ":" + e)
* }</pre>
*
* <p>would return {@code Stream.of("0:a", "1:b", "2:c")}.
*
* <p>The resulting stream is <a
* href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>
* if and only if {@code stream} was efficiently splittable and its underlying spliterator
* reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream
* comes from a data structure supporting efficient indexed random access, typically an array or
* list.
*
* <p>The order of the resulting stream is defined if and only if the order of the original stream
* was defined.
*/
public static <T, R> Stream<R> mapWithIndex(Stream<T> stream, FunctionWithIndex<? super T, ? extends R> function) {
boolean isParallel = stream.isParallel();
Spliterator<T> fromSpliterator = stream.spliterator();
if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
Iterator<T> fromIterator = Spliterators.iterator(fromSpliterator);
return StreamSupport.stream(
new AbstractSpliterator<R>(
fromSpliterator.estimateSize(),
fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) {
long index = 0;
@Override
public boolean tryAdvance(Consumer<? super R> action) {
if (fromIterator.hasNext()) {
action.accept(function.apply(fromIterator.next(), index++));
return true;
}
return false;
}
},
isParallel)
.onClose(stream::close);
}
class Splitr extends MapWithIndexSpliterator<Spliterator<T>, R, Splitr> implements Consumer<T> {
T holder;
Splitr(Spliterator<T> splitr, long index) {
super(splitr, index);
}
@Override
public void accept(T t) {
this.holder = t;
}
@Override
public boolean tryAdvance(Consumer<? super R> action) {
if (fromSpliterator.tryAdvance(this)) {
try {
// The cast is safe because tryAdvance puts a T into `holder`.
action.accept(function.apply(uncheckedCastNullableTToT(holder), index++));
return true;
} finally {
holder = null;
}
}
return false;
}
@Override
Splitr createSplit(Spliterator<T> from, long i) {
return new Splitr(from, i);
}
}
return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close);
}
/**
* Returns a stream consisting of the results of applying the given function to the elements of
* {@code stream} and their indexes in the stream. For example,
*
* <pre>{@code
* mapWithIndex(
* IntStream.of(10, 11, 12),
* (e, index) -> index + ":" + e)
* }</pre>
*
* <p>...would return {@code Stream.of("0:10", "1:11", "2:12")}.
*
* <p>The resulting stream is <a
* href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>
* if and only if {@code stream} was efficiently splittable and its underlying spliterator
* reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream
* comes from a data structure supporting efficient indexed random access, typically an array or
* list.
*
* <p>The order of the resulting stream is defined if and only if the order of the original stream
* was defined.
*/
public static <R> Stream<R> mapWithIndex(IntStream stream, IntFunctionWithIndex<R> function) {
boolean isParallel = stream.isParallel();
Spliterator.OfInt fromSpliterator = stream.spliterator();
if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
PrimitiveIterator.OfInt fromIterator = Spliterators.iterator(fromSpliterator);
return StreamSupport.stream(
new AbstractSpliterator<R>(
fromSpliterator.estimateSize(),
fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) {
long index = 0;
@Override
public boolean tryAdvance(Consumer<? super R> action) {
if (fromIterator.hasNext()) {
action.accept(function.apply(fromIterator.nextInt(), index++));
return true;
}
return false;
}
},
isParallel)
.onClose(stream::close);
}
class Splitr extends MapWithIndexSpliterator<Spliterator.OfInt, R, Splitr>
implements IntConsumer, Spliterator<R> {
int holder;
Splitr(OfInt splitr, long index) {
super(splitr, index);
}
@Override
public void accept(int t) {
this.holder = t;
}
@Override
public boolean tryAdvance(Consumer<? super R> action) {
if (fromSpliterator.tryAdvance(this)) {
action.accept(function.apply(holder, index++));
return true;
}
return false;
}
@Override
Splitr createSplit(OfInt from, long i) {
return new Splitr(from, i);
}
}
return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close);
}
/**
* Returns a stream consisting of the results of applying the given function to the elements of
* {@code stream} and their indexes in the stream. For example,
*
* <pre>{@code
* mapWithIndex(
* LongStream.of(10, 11, 12),
* (e, index) -> index + ":" + e)
* }</pre>
*
* <p>...would return {@code Stream.of("0:10", "1:11", "2:12")}.
*
* <p>The resulting stream is <a
* href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>
* if and only if {@code stream} was efficiently splittable and its underlying spliterator
* reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream
* comes from a data structure supporting efficient indexed random access, typically an array or
* list.
*
* <p>The order of the resulting stream is defined if and only if the order of the original stream
* was defined.
*/
public static <R> Stream<R> mapWithIndex(LongStream stream, LongFunctionWithIndex<R> function) {
boolean isParallel = stream.isParallel();
Spliterator.OfLong fromSpliterator = stream.spliterator();
if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
PrimitiveIterator.OfLong fromIterator = Spliterators.iterator(fromSpliterator);
return StreamSupport.stream(
new AbstractSpliterator<R>(
fromSpliterator.estimateSize(),
fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) {
long index = 0;
@Override
public boolean tryAdvance(Consumer<? super R> action) {
if (fromIterator.hasNext()) {
action.accept(function.apply(fromIterator.nextLong(), index++));
return true;
}
return false;
}
},
isParallel)
.onClose(stream::close);
}
class Splitr extends MapWithIndexSpliterator<Spliterator.OfLong, R, Splitr>
implements LongConsumer, Spliterator<R> {
long holder;
Splitr(OfLong splitr, long index) {
super(splitr, index);
}
@Override
public void accept(long t) {
this.holder = t;
}
@Override
public boolean tryAdvance(Consumer<? super R> action) {
if (fromSpliterator.tryAdvance(this)) {
action.accept(function.apply(holder, index++));
return true;
}
return false;
}
@Override
Splitr createSplit(OfLong from, long i) {
return new Splitr(from, i);
}
}
return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close);
}
/**
* Returns a stream consisting of the results of applying the given function to the elements of
* {@code stream} and their indexes in the stream. For example,
*
* <pre>{@code
* mapWithIndex(
* DoubleStream.of(0.0, 1.0, 2.0)
* (e, index) -> index + ":" + e)
* }</pre>
*
* <p>...would return {@code Stream.of("0:0.0", "1:1.0", "2:2.0")}.
*
* <p>The resulting stream is <a
* href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>
* if and only if {@code stream} was efficiently splittable and its underlying spliterator
* reported {@link Spliterator#SUBSIZED}. This is generally the case if the underlying stream
* comes from a data structure supporting efficient indexed random access, typically an array or
* list.
*
* <p>The order of the resulting stream is defined if and only if the order of the original stream
* was defined.
*/
public static <R> Stream<R> mapWithIndex(DoubleStream stream, DoubleFunctionWithIndex<R> function) {
boolean isParallel = stream.isParallel();
Spliterator.OfDouble fromSpliterator = stream.spliterator();
if (!fromSpliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
PrimitiveIterator.OfDouble fromIterator = Spliterators.iterator(fromSpliterator);
return StreamSupport.stream(
new AbstractSpliterator<R>(
fromSpliterator.estimateSize(),
fromSpliterator.characteristics() & (Spliterator.ORDERED | Spliterator.SIZED)) {
long index = 0;
@Override
public boolean tryAdvance(Consumer<? super R> action) {
if (fromIterator.hasNext()) {
action.accept(function.apply(fromIterator.nextDouble(), index++));
return true;
}
return false;
}
},
isParallel)
.onClose(stream::close);
}
class Splitr extends MapWithIndexSpliterator<Spliterator.OfDouble, R, Splitr>
implements DoubleConsumer, Spliterator<R> {
double holder;
Splitr(OfDouble splitr, long index) {
super(splitr, index);
}
@Override
public void accept(double t) {
this.holder = t;
}
@Override
public boolean tryAdvance(Consumer<? super R> action) {
if (fromSpliterator.tryAdvance(this)) {
action.accept(function.apply(holder, index++));
return true;
}
return false;
}
@Override
Splitr createSplit(OfDouble from, long i) {
return new Splitr(from, i);
}
}
return StreamSupport.stream(new Splitr(fromSpliterator, 0), isParallel).onClose(stream::close);
}
/**
* An analogue of {@link Function} also accepting an index.
*
* <p>This interface is only intended for use by callers of {@link #mapWithIndex(Stream,
* FunctionWithIndex)}.
*
* @since 21.0
*/
public interface FunctionWithIndex<T, R> {
/**
* Applies this function to the given argument and its index within a stream.
*/
R apply(T from, long index);
}
private abstract static class MapWithIndexSpliterator<
F extends Spliterator<?>,
R,
S extends MapWithIndexSpliterator<F, R, S>>
implements Spliterator<R> {
final F fromSpliterator;
long index;
MapWithIndexSpliterator(F fromSpliterator, long index) {
this.fromSpliterator = fromSpliterator;
this.index = index;
}
abstract S createSplit(F from, long i);
@Override
public S trySplit() {
Spliterator<?> splitOrNull = fromSpliterator.trySplit();
if (splitOrNull == null) {
return null;
}
@SuppressWarnings("unchecked")
F split = (F) splitOrNull;
S result = createSplit(split, index);
this.index += split.getExactSizeIfKnown();
return result;
}
@Override
public long estimateSize() {
return fromSpliterator.estimateSize();
}
@Override
public int characteristics() {
return fromSpliterator.characteristics()
& (Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED);
}
}
/**
* An analogue of {@link IntFunction} also accepting an index.
*
* <p>This interface is only intended for use by callers of {@link #mapWithIndex(IntStream,
* IntFunctionWithIndex)}.
*
* @since 21.0
*/
public interface IntFunctionWithIndex<R> {
/**
* Applies this function to the given argument and its index within a stream.
*/
R apply(int from, long index);
}
/**
* An analogue of {@link LongFunction} also accepting an index.
*
* <p>This interface is only intended for use by callers of {@link #mapWithIndex(LongStream,
* LongFunctionWithIndex)}.
*
* @since 21.0
*/
public interface LongFunctionWithIndex<R> {
/**
* Applies this function to the given argument and its index within a stream.
*/
R apply(long from, long index);
}
/**
* An analogue of {@link DoubleFunction} also accepting an index.
*
* <p>This interface is only intended for use by callers of {@link #mapWithIndex(DoubleStream,
* DoubleFunctionWithIndex)}.
*
* @since 21.0
*/
public interface DoubleFunctionWithIndex<R> {
/**
* Applies this function to the given argument and its index within a stream.
*/
R apply(double from, long index);
}
/**
* Returns the last element of the specified stream, or {@link Optional#empty} if the
* stream is empty.
*
* <p>Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This
* method's runtime will be between O(log n) and O(n), performing better on <a
* href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>
* streams.
*
* <p>If the stream has nondeterministic order, this has equivalent semantics to {@link
* Stream#findAny} (which you might as well use).
*
* @throws NullPointerException if the last element of the stream is null
* @see Stream#findFirst()
*/
/*
* By declaring <T> instead of <T extends Object>, we declare this method as requiring a
* stream whose elements are non-null. However, the method goes out of its way to still handle
* nulls in the stream. This means that the method can safely be used with a stream that contains
* nulls as long as the *last* element is *not* null.
*
* (To "go out of its way," the method tracks a `set` bit so that it can distinguish "the final
* split has a last element of null, so throw NPE" from "the final split was empty, so look for an
* element in the prior one.")
*/
public static <T> Optional<T> findLast(Stream<T> stream) {
class OptionalState {
boolean set = false;
T value = null;
void set(T value) {
this.set = true;
this.value = value;
}
T get() {
/*
* requireNonNull is safe because we call get() only if we've previously called set().
*
* (For further discussion of nullness, see the comment above the method.)
*/
return requireNonNull(value);
}
}
OptionalState state = new OptionalState();
Deque<Spliterator<T>> splits = new ArrayDeque<>();
splits.addLast(stream.spliterator());
while (!splits.isEmpty()) {
Spliterator<T> spliterator = splits.removeLast();
if (spliterator.getExactSizeIfKnown() == 0) {
continue; // drop this split
}
// Many spliterators will have trySplits that are SUBSIZED even if they are not themselves
// SUBSIZED.
if (spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
// we can drill down to exactly the smallest nonempty spliterator
while (true) {
Spliterator<T> prefix = spliterator.trySplit();
if (prefix == null || prefix.getExactSizeIfKnown() == 0) {
break;
} else if (spliterator.getExactSizeIfKnown() == 0) {
spliterator = prefix;
break;
}
}
// spliterator is known to be nonempty now
spliterator.forEachRemaining(state::set);
return Optional.of(state.get());
}
Spliterator<T> prefix = spliterator.trySplit();
if (prefix == null || prefix.getExactSizeIfKnown() == 0) {
// we can't split this any further
spliterator.forEachRemaining(state::set);
if (state.set) {
return Optional.of(state.get());
}
// fall back to the last split
continue;
}
splits.addLast(prefix);
splits.addLast(spliterator);
}
return Optional.empty();
}
/**
* Returns the last element of the specified stream, or {@link OptionalInt#empty} if the stream is
* empty.
*
* <p>Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This
* method's runtime will be between O(log n) and O(n), performing better on <a
* href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>
* streams.
*
* @throws NullPointerException if the last element of the stream is null
* @see IntStream#findFirst()
*/
public static OptionalInt findLast(IntStream stream) {
// findLast(Stream) does some allocation, so we might as well box some more
Optional<Integer> boxedLast = findLast(stream.boxed());
return boxedLast.map(OptionalInt::of).orElseGet(OptionalInt::empty);
}
/**
* Returns the last element of the specified stream, or {@link OptionalLong#empty} if the stream
* is empty.
*
* <p>Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This
* method's runtime will be between O(log n) and O(n), performing better on <a
* href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>
* streams.
*
* @throws NullPointerException if the last element of the stream is null
* @see LongStream#findFirst()
*/
public static OptionalLong findLast(LongStream stream) {
// findLast(Stream) does some allocation, so we might as well box some more
Optional<Long> boxedLast = findLast(stream.boxed());
return boxedLast.map(OptionalLong::of).orElseGet(OptionalLong::empty);
}
/**
* Returns the last element of the specified stream, or {@link OptionalDouble#empty} if the stream
* is empty.
*
* <p>Equivalent to {@code stream.reduce((a, b) -> b)}, but may perform significantly better. This
* method's runtime will be between O(log n) and O(n), performing better on <a
* href="http://gee.cs.oswego.edu/dl/html/StreamParallelGuidance.html">efficiently splittable</a>
* streams.
*
* @throws NullPointerException if the last element of the stream is null
* @see DoubleStream#findFirst()
*/
public static OptionalDouble findLast(DoubleStream stream) {
// findLast(Stream) does some allocation, so we might as well box some more
Optional<Double> boxedLast = findLast(stream.boxed());
return boxedLast.map(OptionalDouble::of).orElseGet(OptionalDouble::empty);
}
public static <T> T uncheckedCastNullableTToT(T t) {
return t;
}
}

View File

@@ -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;
/**
* 静态 String 池
*
* @author L.cm
*/
public interface StringPool {
String AMPERSAND = "&";
String AND = "and";
String AT = "@";
String ASTERISK = "*";
String STAR = ASTERISK;
String SLASH = "/";
String BACK_SLASH = "\\";
String DOUBLE_SLASH = "#//";
String COLON = ":";
String COMMA = ",";
String DASH = "-";
String DOLLAR = "$";
String DOT = ".";
String EMPTY = "";
String EMPTY_JSON = "{}";
String EQUALS = "=";
String FALSE = "false";
String HASH = "#";
String HAT = "^";
String LEFT_BRACE = "{";
String LEFT_BRACKET = "(";
String LEFT_CHEV = "<";
String NEWLINE = "\n";
String N = "n";
String NO = "no";
String NULL = "null";
String OFF = "off";
String ON = "on";
String PERCENT = "%";
String PIPE = "|";
String PLUS = "+";
String QUESTION_MARK = "?";
String EXCLAMATION_MARK = "!";
String QUOTE = "\"";
String RETURN = "\r";
String TAB = "\t";
String RIGHT_BRACE = "}";
String RIGHT_BRACKET = ")";
String RIGHT_CHEV = ">";
String SEMICOLON = ";";
String SINGLE_QUOTE = "'";
String BACKTICK = "`";
String SPACE = " ";
String TILDA = "~";
String LEFT_SQ_BRACKET = "[";
String RIGHT_SQ_BRACKET = "]";
String TRUE = "true";
String UNDERSCORE = "_";
String UTF_8 = "UTF-8";
String GBK = "GBK";
String ISO_8859_1 = "ISO-8859-1";
String Y = "y";
String YES = "yes";
String ONE = "1";
String ZERO = "0";
String MINUS_ONE = "-1";
String DOLLAR_LEFT_BRACE= "${";
String UNKNOWN = "unknown";
String GET = "GET";
String POST = "POST";
}

File diff suppressed because it is too large Load Diff

View File

@@ -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: DreamLu (596392912@qq.com)
*/
package org.springblade.core.tool.utils;
import org.springframework.util.Assert;
import java.io.File;
import java.io.FileFilter;
import java.io.Serializable;
/**
* 文件后缀过滤器
*
* @author L.cm
*/
public class SuffixFileFilter implements FileFilter, Serializable {
private static final long serialVersionUID = -3389157631240246157L;
private final String[] suffixes;
public SuffixFileFilter(final String suffix) {
Assert.notNull(suffix, "The suffix must not be null");
this.suffixes = new String[]{suffix};
}
public SuffixFileFilter(final String[] suffixes) {
Assert.notNull(suffixes, "The suffix must not be null");
this.suffixes = new String[suffixes.length];
System.arraycopy(suffixes, 0, this.suffixes, 0, suffixes.length);
}
@Override
public boolean accept(File pathname) {
final String name = pathname.getName();
for (final String suffix : this.suffixes) {
if (checkEndsWith(name, suffix)) {
return true;
}
}
return false;
}
private boolean checkEndsWith(final String str, final String end) {
final int endLen = end.length();
return str.regionMatches(true, str.length() - endLen, end, 0, endLen);
}
}

View File

@@ -0,0 +1,87 @@
/**
* 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 org.springblade.core.tool.support.Kv;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 模版解析工具类
*/
public class TemplateUtil {
/**
* 支持 ${} 与 #{} 两种模版占位符
*/
private static final Pattern pattern = Pattern.compile("\\$\\{([^{}]+)}|\\#\\{([^{}]+)}");
/**
* 解析模版
*
* @param template 模版
* @param params 参数
* @return 解析后的字符串
*/
public static String process(String template, Kv params) {
Matcher matcher = pattern.matcher(template);
StringBuilder sb = new StringBuilder();
while (matcher.find()) {
String key = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
String replacement = params.getStr(key);
if (replacement == null) {
throw new IllegalArgumentException("参数中缺少必要的键: " + key);
}
matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb);
return sb.toString();
}
/**
* 解析模版
*
* @param template 模版
* @param params 参数
* @return 解析后的字符串
*/
public static String safeProcess(String template, Kv params) {
Matcher matcher = pattern.matcher(template);
StringBuilder sb = new StringBuilder();
while (matcher.find()) {
String key = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
String replacement = params.getStr(key);
if (replacement != null) {
matcher.appendReplacement(sb, replacement);
}
}
matcher.appendTail(sb);
return sb.toString();
}
}

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