init
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>BladeX-Tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>blade-core-tool</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${project.parent.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<module.name>org.springblade.blade.core.tool</module.name>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Blade -->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-launch</artifactId>
|
||||
</dependency>
|
||||
<!-- Jackson -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||
</dependency>
|
||||
<!-- Guava -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<!--Swagger-->
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations-jakarta</artifactId>
|
||||
</dependency>
|
||||
<!-- protostuff -->
|
||||
<dependency>
|
||||
<groupId>io.protostuff</groupId>
|
||||
<artifactId>protostuff-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.protostuff</groupId>
|
||||
<artifactId>protostuff-runtime</artifactId>
|
||||
</dependency>
|
||||
<!-- jackson -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.module</groupId>
|
||||
<artifactId>jackson-module-jaxb-annotations</artifactId>
|
||||
</dependency>
|
||||
<!-- validation -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<!-- xml bind -->
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
</dependency>
|
||||
<!-- sm2 -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
</dependency>
|
||||
<!-- Auto -->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-auto</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.api;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 业务代码接口
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IResultCode extends Serializable {
|
||||
|
||||
/**
|
||||
* 获取消息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getMessage();
|
||||
|
||||
/**
|
||||
* 获取状态码
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
int getCode();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springblade.core.tool.constant.BladeConstant;
|
||||
import org.springblade.core.tool.utils.ObjectUtil;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 统一API响应结果封装
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Schema(description = "返回信息")
|
||||
@NoArgsConstructor
|
||||
public class R<T> implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "状态码", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private int code;
|
||||
@Schema(description = "是否成功", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private boolean success;
|
||||
@Schema(description = "承载数据")
|
||||
private T data;
|
||||
@Schema(description = "返回消息", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String msg;
|
||||
|
||||
private R(IResultCode resultCode) {
|
||||
this(resultCode, null, resultCode.getMessage());
|
||||
}
|
||||
|
||||
private R(IResultCode resultCode, String msg) {
|
||||
this(resultCode, null, msg);
|
||||
}
|
||||
|
||||
private R(IResultCode resultCode, T data) {
|
||||
this(resultCode, data, resultCode.getMessage());
|
||||
}
|
||||
|
||||
private R(IResultCode resultCode, T data, String msg) {
|
||||
this(resultCode.getCode(), data, msg);
|
||||
}
|
||||
|
||||
private R(int code, T data, String msg) {
|
||||
this.code = code;
|
||||
this.data = data;
|
||||
this.msg = msg;
|
||||
this.success = ResultCode.SUCCESS.code == code;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断返回是否为成功
|
||||
*
|
||||
* @param result Result
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean isSuccess(@Nullable R<?> result) {
|
||||
return Optional.ofNullable(result)
|
||||
.map(x -> ObjectUtil.nullSafeEquals(ResultCode.SUCCESS.code, x.code))
|
||||
.orElse(Boolean.FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断返回是否为成功
|
||||
*
|
||||
* @param result Result
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean isNotSuccess(@Nullable R<?> result) {
|
||||
return !R.isSuccess(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param data 数据
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> data(T data) {
|
||||
return data(data, BladeConstant.DEFAULT_SUCCESS_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param data 数据
|
||||
* @param msg 消息
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> data(T data, String msg) {
|
||||
return data(HttpServletResponse.SC_OK, data, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param code 状态码
|
||||
* @param data 数据
|
||||
* @param msg 消息
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> data(int code, T data, String msg) {
|
||||
return new R<>(code, data, data == null ? BladeConstant.DEFAULT_NULL_MESSAGE : msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回成功
|
||||
*
|
||||
* @param <T> 泛型标记
|
||||
* @return Result
|
||||
*/
|
||||
public static <T> R<T> success() {
|
||||
return new R<>(ResultCode.SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param msg 消息
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> success(String msg) {
|
||||
return new R<>(ResultCode.SUCCESS, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param resultCode 业务代码
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> success(IResultCode resultCode) {
|
||||
return new R<>(resultCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param resultCode 业务代码
|
||||
* @param msg 消息
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> success(IResultCode resultCode, String msg) {
|
||||
return new R<>(resultCode, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param msg 消息
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> fail(String msg) {
|
||||
return new R<>(ResultCode.FAILURE, msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param code 状态码
|
||||
* @param msg 消息
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> fail(int code, String msg) {
|
||||
return new R<>(code, null, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param resultCode 业务代码
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> fail(IResultCode resultCode) {
|
||||
return new R<>(resultCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param resultCode 业务代码
|
||||
* @param msg 消息
|
||||
* @param <T> T 泛型标记
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> fail(IResultCode resultCode, String msg) {
|
||||
return new R<>(resultCode, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回R
|
||||
*
|
||||
* @param flag 成功状态
|
||||
* @return R
|
||||
*/
|
||||
public static <T> R<T> status(boolean flag) {
|
||||
return flag ? success(BladeConstant.DEFAULT_SUCCESS_MESSAGE) : fail(BladeConstant.DEFAULT_FAILURE_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据状态返回成功或者失败
|
||||
*
|
||||
* @param status 状态
|
||||
* @param msg 异常msg
|
||||
* @param <T> 泛型标记
|
||||
* @return Result
|
||||
*/
|
||||
public static <T> R<T> status(boolean status, String msg) {
|
||||
return status ? R.success(BladeConstant.DEFAULT_SUCCESS_MESSAGE) : R.fail(msg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.api;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 业务代码枚举
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ResultCode implements IResultCode {
|
||||
|
||||
/**
|
||||
* 操作成功
|
||||
*/
|
||||
SUCCESS(HttpServletResponse.SC_OK, "操作成功"),
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*/
|
||||
FAILURE(HttpServletResponse.SC_BAD_REQUEST, "业务异常"),
|
||||
|
||||
/**
|
||||
* 请求未授权
|
||||
*/
|
||||
UN_AUTHORIZED(HttpServletResponse.SC_UNAUTHORIZED, "请求未授权"),
|
||||
|
||||
/**
|
||||
* 客户端请求未授权
|
||||
*/
|
||||
CLIENT_UN_AUTHORIZED(HttpServletResponse.SC_UNAUTHORIZED, "客户端请求未授权"),
|
||||
|
||||
/**
|
||||
* 404 没找到请求
|
||||
*/
|
||||
NOT_FOUND(HttpServletResponse.SC_NOT_FOUND, "404 没找到请求"),
|
||||
|
||||
/**
|
||||
* 消息不能读取
|
||||
*/
|
||||
MSG_NOT_READABLE(HttpServletResponse.SC_BAD_REQUEST, "消息不能读取"),
|
||||
|
||||
/**
|
||||
* 不支持当前请求方法
|
||||
*/
|
||||
METHOD_NOT_SUPPORTED(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "不支持当前请求方法"),
|
||||
|
||||
/**
|
||||
* 不支持当前媒体类型
|
||||
*/
|
||||
MEDIA_TYPE_NOT_SUPPORTED(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "不支持当前媒体类型"),
|
||||
|
||||
/**
|
||||
* 请求被拒绝
|
||||
*/
|
||||
REQ_REJECT(HttpServletResponse.SC_FORBIDDEN, "请求被拒绝"),
|
||||
|
||||
/**
|
||||
* 服务器异常
|
||||
*/
|
||||
INTERNAL_SERVER_ERROR(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "请求未完成,请联系管理员"),
|
||||
|
||||
/**
|
||||
* 缺少必要的请求参数
|
||||
*/
|
||||
PARAM_MISS(HttpServletResponse.SC_BAD_REQUEST, "缺少必要的请求参数"),
|
||||
|
||||
/**
|
||||
* 请求参数类型错误
|
||||
*/
|
||||
PARAM_TYPE_ERROR(HttpServletResponse.SC_BAD_REQUEST, "请求参数类型错误"),
|
||||
|
||||
/**
|
||||
* 请求参数绑定错误
|
||||
*/
|
||||
PARAM_BIND_ERROR(HttpServletResponse.SC_BAD_REQUEST, "请求参数绑定错误"),
|
||||
|
||||
/**
|
||||
* 参数校验失败
|
||||
*/
|
||||
PARAM_VALID_ERROR(HttpServletResponse.SC_BAD_REQUEST, "参数校验失败"),
|
||||
;
|
||||
|
||||
/**
|
||||
* code编码
|
||||
*/
|
||||
final int code;
|
||||
/**
|
||||
* 中文信息描述
|
||||
*/
|
||||
final String message;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.springblade.core.tool.beans;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Bean属性
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class BeanProperty {
|
||||
private final String name;
|
||||
private final Class<?> type;
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.beans;
|
||||
|
||||
|
||||
import lombok.Setter;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.ClassUtil;
|
||||
import org.springblade.core.tool.utils.ReflectUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.Opcodes;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.cglib.core.*;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* spring cglib 魔改
|
||||
*
|
||||
* <p>
|
||||
* 1. 支持链式 bean,支持 map
|
||||
* 2. ClassLoader 跟 target 保持一致
|
||||
* </p>
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public abstract class BladeBeanCopier {
|
||||
private static final String BEAN_NAME_PREFIX = BladeBeanCopier.class.getName();
|
||||
private static final Type CONVERTER = TypeUtils.parseType("org.springframework.cglib.core.Converter");
|
||||
private static final Type BEAN_COPIER = TypeUtils.parseType(BEAN_NAME_PREFIX);
|
||||
private static final Type BEAN_MAP = TypeUtils.parseType(Map.class.getName());
|
||||
private static final Signature COPY = new Signature("copy", Type.VOID_TYPE, new Type[]{Constants.TYPE_OBJECT, Constants.TYPE_OBJECT, CONVERTER});
|
||||
private static final Signature CONVERT = TypeUtils.parseSignature("Object convert(Object, Class, Object)");
|
||||
private static final Signature BEAN_MAP_GET = TypeUtils.parseSignature("Object get(Object)");
|
||||
private static final Type CLASS_UTILS = TypeUtils.parseType(ClassUtils.class.getName());
|
||||
private static final Signature IS_ASSIGNABLE_VALUE = TypeUtils.parseSignature("boolean isAssignableValue(Class, Object)");
|
||||
/**
|
||||
* The map to store {@link BladeBeanCopier} of source type and class type for copy.
|
||||
*/
|
||||
private static final ConcurrentMap<BladeBeanCopierKey, BladeBeanCopier> BEAN_COPIER_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
public static BladeBeanCopier create(Class source, Class target, boolean useConverter) {
|
||||
return BladeBeanCopier.create(source, target, useConverter, false);
|
||||
}
|
||||
|
||||
public static BladeBeanCopier create(Class source, Class target, boolean useConverter, boolean nonNull) {
|
||||
BladeBeanCopierKey copierKey = new BladeBeanCopierKey(source, target, useConverter, nonNull);
|
||||
// 利用 ConcurrentMap 缓存 提高性能,接近 直接 get set
|
||||
return BEAN_COPIER_MAP.computeIfAbsent(copierKey, key -> {
|
||||
Generator gen = new Generator(copierKey);
|
||||
gen.setSource(key.getSource());
|
||||
gen.setTarget(key.getTarget());
|
||||
gen.setUseConverter(key.isUseConverter());
|
||||
gen.setNonNull(key.isNonNull());
|
||||
gen.setUseCache(true);
|
||||
return gen.create(key);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean copy
|
||||
*
|
||||
* @param from from Bean
|
||||
* @param to to Bean
|
||||
* @param converter Converter
|
||||
*/
|
||||
abstract public void copy(Object from, Object to, @Nullable Converter converter);
|
||||
|
||||
public static class Generator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(BEAN_NAME_PREFIX);
|
||||
private final Object key;
|
||||
private Class source;
|
||||
private Class target;
|
||||
@Setter
|
||||
private boolean useConverter;
|
||||
@Setter
|
||||
private boolean nonNull;
|
||||
private String className;
|
||||
|
||||
Generator(Object key) {
|
||||
super(SOURCE);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setSource(Class source) {
|
||||
if (!Modifier.isPublic(source.getModifiers())) {
|
||||
setNamePrefix(source.getName());
|
||||
}
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public void setTarget(Class target) {
|
||||
if (!Modifier.isPublic(target.getModifiers())) {
|
||||
setNamePrefix(target.getName());
|
||||
}
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
// L.cm 保证 和 返回使用同一个 ClassLoader
|
||||
return target.getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BladeBeanCopier create(Object key) {
|
||||
return (BladeBeanCopier) super.create(key);
|
||||
}
|
||||
|
||||
public BladeBeanCopier create() {
|
||||
return (BladeBeanCopier) super.create(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateClass(ClassVisitor v) {
|
||||
Type sourceType = Type.getType(source);
|
||||
Type targetType = Type.getType(target);
|
||||
ClassEmitter ce = new ClassEmitter(v);
|
||||
ce.begin_class(Constants.V1_2,
|
||||
Constants.ACC_PUBLIC,
|
||||
this.className,
|
||||
BEAN_COPIER,
|
||||
null,
|
||||
Constants.SOURCE_FILE);
|
||||
|
||||
EmitUtils.null_constructor(ce);
|
||||
CodeEmitter e = ce.begin_method(Constants.ACC_PUBLIC, COPY, null);
|
||||
|
||||
// map 单独处理
|
||||
if (Map.class.isAssignableFrom(source)) {
|
||||
generateClassFormMap(ce, e, sourceType, targetType);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2018.12.27 by L.cm 支持链式 bean
|
||||
// 注意:此处需兼容链式bean 使用了 spring 的方法,比较耗时
|
||||
PropertyDescriptor[] getters = ReflectUtil.getBeanGetters(source);
|
||||
PropertyDescriptor[] setters = ReflectUtil.getBeanSetters(target);
|
||||
Map<String, PropertyDescriptor> names = new HashMap<>(16);
|
||||
for (PropertyDescriptor getter : getters) {
|
||||
names.put(getter.getName(), getter);
|
||||
}
|
||||
|
||||
Local targetLocal = e.make_local();
|
||||
Local sourceLocal = e.make_local();
|
||||
e.load_arg(1);
|
||||
e.checkcast(targetType);
|
||||
e.store_local(targetLocal);
|
||||
e.load_arg(0);
|
||||
e.checkcast(sourceType);
|
||||
e.store_local(sourceLocal);
|
||||
|
||||
for (PropertyDescriptor setter : setters) {
|
||||
String propName = setter.getName();
|
||||
|
||||
CopyProperty targetIgnoreCopy = ReflectUtil.getAnnotation(target, propName, CopyProperty.class);
|
||||
// set 上有忽略的 注解
|
||||
if (targetIgnoreCopy != null) {
|
||||
if (targetIgnoreCopy.ignore()) {
|
||||
continue;
|
||||
}
|
||||
// 注解上的别名,如果别名不为空,使用别名
|
||||
String aliasTargetPropName = targetIgnoreCopy.value();
|
||||
if (StringUtil.isNotBlank(aliasTargetPropName)) {
|
||||
propName = aliasTargetPropName;
|
||||
}
|
||||
}
|
||||
// 找到对应的 get
|
||||
PropertyDescriptor getter = names.get(propName);
|
||||
// 没有 get 跳出
|
||||
if (getter == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MethodInfo read = ReflectUtils.getMethodInfo(getter.getReadMethod());
|
||||
Method writeMethod = setter.getWriteMethod();
|
||||
MethodInfo write = ReflectUtils.getMethodInfo(writeMethod);
|
||||
Type returnType = read.getSignature().getReturnType();
|
||||
Type setterType = write.getSignature().getArgumentTypes()[0];
|
||||
Class<?> getterPropertyType = getter.getPropertyType();
|
||||
Class<?> setterPropertyType = setter.getPropertyType();
|
||||
|
||||
// L.cm 2019.01.12 优化逻辑,先判断类型,类型一致直接 set,不同再判断 是否 类型转换
|
||||
// nonNull Label
|
||||
Label l0 = e.make_label();
|
||||
// 判断类型是否一致,包括 包装类型
|
||||
if (ClassUtil.isAssignable(setterPropertyType, getterPropertyType)) {
|
||||
// 2018.12.27 by L.cm 支持链式 bean
|
||||
e.load_local(targetLocal);
|
||||
e.load_local(sourceLocal);
|
||||
e.invoke(read);
|
||||
boolean getterIsPrimitive = getterPropertyType.isPrimitive();
|
||||
boolean setterIsPrimitive = setterPropertyType.isPrimitive();
|
||||
|
||||
if (nonNull) {
|
||||
// 需要落栈,强制装箱
|
||||
e.box(returnType);
|
||||
Local var = e.make_local();
|
||||
e.store_local(var);
|
||||
e.load_local(var);
|
||||
// nonNull Label
|
||||
e.ifnull(l0);
|
||||
e.load_local(targetLocal);
|
||||
e.load_local(var);
|
||||
// 需要落栈,强制拆箱
|
||||
e.unbox_or_zero(setterType);
|
||||
} else {
|
||||
// 如果 get 为原始类型,需要装箱
|
||||
if (getterIsPrimitive && !setterIsPrimitive) {
|
||||
e.box(returnType);
|
||||
}
|
||||
// 如果 set 为原始类型,需要拆箱
|
||||
if (!getterIsPrimitive && setterIsPrimitive) {
|
||||
e.unbox_or_zero(setterType);
|
||||
}
|
||||
}
|
||||
|
||||
// 构造 set 方法
|
||||
invokeWrite(e, write, writeMethod, nonNull, l0);
|
||||
} else if (useConverter) {
|
||||
e.load_local(targetLocal);
|
||||
e.load_arg(2);
|
||||
e.load_local(sourceLocal);
|
||||
e.invoke(read);
|
||||
e.box(returnType);
|
||||
|
||||
if (nonNull) {
|
||||
Local var = e.make_local();
|
||||
e.store_local(var);
|
||||
e.load_local(var);
|
||||
e.ifnull(l0);
|
||||
e.load_local(targetLocal);
|
||||
e.load_arg(2);
|
||||
e.load_local(var);
|
||||
}
|
||||
|
||||
EmitUtils.load_class(e, setterType);
|
||||
// 更改成了属性名,之前是 set 方法名
|
||||
e.push(propName);
|
||||
e.invoke_interface(CONVERTER, CONVERT);
|
||||
e.unbox_or_zero(setterType);
|
||||
|
||||
// 构造 set 方法
|
||||
invokeWrite(e, write, writeMethod, nonNull, l0);
|
||||
}
|
||||
}
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
ce.end_class();
|
||||
}
|
||||
|
||||
private static void invokeWrite(CodeEmitter e, MethodInfo write, Method writeMethod, boolean nonNull, Label l0) {
|
||||
// 返回值,判断 链式 bean
|
||||
Class<?> returnType = writeMethod.getReturnType();
|
||||
e.invoke(write);
|
||||
// 链式 bean,有返回值需要 pop
|
||||
if (!returnType.equals(Void.TYPE)) {
|
||||
e.pop();
|
||||
}
|
||||
if (nonNull) {
|
||||
e.visitLabel(l0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object firstInstance(Class type) {
|
||||
return BeanUtil.newInstance(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object nextInstance(Object instance) {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class generate(ClassLoaderData data) {
|
||||
// 生成类名
|
||||
data.reserveName(generateClassName(data.getUniqueNamePredicate()));
|
||||
try {
|
||||
return MethodHandles.lookup()
|
||||
.defineClass(DefaultGeneratorStrategy.INSTANCE.generate(this))
|
||||
.asSubclass(BladeBeanCopier.class);
|
||||
} catch (Exception ex) {
|
||||
throw new CodeGenerationException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private String generateClassName(Predicate nameTestPredicate) {
|
||||
this.className = DefaultNamingPolicy.INSTANCE.getClassName(BEAN_NAME_PREFIX, BEAN_NAME_PREFIX, key, nameTestPredicate);
|
||||
return this.className;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 map 的 copy
|
||||
*
|
||||
* @param ce ClassEmitter
|
||||
* @param e CodeEmitter
|
||||
* @param sourceType sourceType
|
||||
* @param targetType targetType
|
||||
*/
|
||||
public void generateClassFormMap(ClassEmitter ce, CodeEmitter e, Type sourceType, Type targetType) {
|
||||
// 2018.12.27 by L.cm 支持链式 bean
|
||||
PropertyDescriptor[] setters = ReflectUtil.getBeanSetters(target);
|
||||
|
||||
// 入口变量
|
||||
Local targetLocal = e.make_local();
|
||||
Local sourceLocal = e.make_local();
|
||||
e.load_arg(1);
|
||||
e.checkcast(targetType);
|
||||
e.store_local(targetLocal);
|
||||
e.load_arg(0);
|
||||
e.checkcast(sourceType);
|
||||
e.store_local(sourceLocal);
|
||||
Type mapBox = Type.getType(Object.class);
|
||||
|
||||
for (PropertyDescriptor setter : setters) {
|
||||
String propName = setter.getName();
|
||||
|
||||
// set 上有忽略的 注解
|
||||
CopyProperty targetIgnoreCopy = ReflectUtil.getAnnotation(target, propName, CopyProperty.class);
|
||||
if (targetIgnoreCopy != null) {
|
||||
if (targetIgnoreCopy.ignore()) {
|
||||
continue;
|
||||
}
|
||||
// 注解上的别名
|
||||
String aliasTargetPropName = targetIgnoreCopy.value();
|
||||
if (StringUtil.isNotBlank(aliasTargetPropName)) {
|
||||
propName = aliasTargetPropName;
|
||||
}
|
||||
}
|
||||
|
||||
Method writeMethod = setter.getWriteMethod();
|
||||
MethodInfo write = ReflectUtils.getMethodInfo(writeMethod);
|
||||
Type setterType = write.getSignature().getArgumentTypes()[0];
|
||||
|
||||
e.load_local(targetLocal);
|
||||
e.load_local(sourceLocal);
|
||||
|
||||
e.push(propName);
|
||||
// 执行 map get
|
||||
e.invoke_interface(BEAN_MAP, BEAN_MAP_GET);
|
||||
// box 装箱,避免 array[] 数组问题
|
||||
e.box(mapBox);
|
||||
|
||||
// 生成变量
|
||||
Local var = e.make_local();
|
||||
e.store_local(var);
|
||||
e.load_local(var);
|
||||
|
||||
// 先判断 不为null,然后做类型判断
|
||||
Label l0 = e.make_label();
|
||||
e.ifnull(l0);
|
||||
EmitUtils.load_class(e, setterType);
|
||||
e.load_local(var);
|
||||
// ClassUtils.isAssignableValue(Integer.class, id)
|
||||
e.invoke_static(CLASS_UTILS, IS_ASSIGNABLE_VALUE);
|
||||
Label l1 = new Label();
|
||||
// 返回值,判断 链式 bean
|
||||
Class<?> returnType = writeMethod.getReturnType();
|
||||
if (useConverter) {
|
||||
e.if_jump(Opcodes.IFEQ, l1);
|
||||
e.load_local(targetLocal);
|
||||
e.load_local(var);
|
||||
e.unbox_or_zero(setterType);
|
||||
e.invoke(write);
|
||||
if (!returnType.equals(Void.TYPE)) {
|
||||
e.pop();
|
||||
}
|
||||
e.goTo(l0);
|
||||
e.visitLabel(l1);
|
||||
e.load_local(targetLocal);
|
||||
e.load_arg(2);
|
||||
e.load_local(var);
|
||||
EmitUtils.load_class(e, setterType);
|
||||
e.push(propName);
|
||||
e.invoke_interface(CONVERTER, CONVERT);
|
||||
e.unbox_or_zero(setterType);
|
||||
e.invoke(write);
|
||||
} else {
|
||||
e.if_jump(Opcodes.IFEQ, l0);
|
||||
e.load_local(targetLocal);
|
||||
e.load_local(var);
|
||||
e.unbox_or_zero(setterType);
|
||||
e.invoke(write);
|
||||
}
|
||||
// 返回值,判断 链式 bean
|
||||
if (!returnType.equals(Void.TYPE)) {
|
||||
e.pop();
|
||||
}
|
||||
e.visitLabel(l0);
|
||||
}
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
ce.end_class();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springblade.core.tool.beans;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* copy key
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Getter
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
public class BladeBeanCopierKey {
|
||||
private final Class<?> source;
|
||||
private final Class<?> target;
|
||||
private final boolean useConverter;
|
||||
private final boolean nonNull;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package org.springblade.core.tool.beans;
|
||||
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
import org.springframework.cglib.core.AbstractClassGenerator;
|
||||
import org.springframework.cglib.core.ReflectUtils;
|
||||
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
/**
|
||||
* 重写 cglib BeanMap,支持链式bean
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public abstract class BladeBeanMap extends BeanMap {
|
||||
protected BladeBeanMap() {
|
||||
}
|
||||
|
||||
protected BladeBeanMap(Object bean) {
|
||||
super(bean);
|
||||
}
|
||||
|
||||
public static BladeBeanMap create(Object bean) {
|
||||
BladeGenerator gen = new BladeGenerator();
|
||||
gen.setBean(bean);
|
||||
return gen.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* newInstance
|
||||
*
|
||||
* @param o Object
|
||||
* @return BladeBeanMap
|
||||
*/
|
||||
@Override
|
||||
public abstract BladeBeanMap newInstance(Object o);
|
||||
|
||||
public static class BladeGenerator extends AbstractClassGenerator {
|
||||
private static final Source SOURCE = new Source(BladeBeanMap.class.getName());
|
||||
|
||||
private Object bean;
|
||||
private Class beanClass;
|
||||
private int require;
|
||||
|
||||
public BladeGenerator() {
|
||||
super(SOURCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the bean that the generated map should reflect. The bean may be swapped
|
||||
* out for another bean of the same type using {@link #setBean}.
|
||||
* Calling this method overrides any value previously set using {@link #setBeanClass}.
|
||||
* You must call either this method or {@link #setBeanClass} before {@link #create}.
|
||||
*
|
||||
* @param bean the initial bean
|
||||
*/
|
||||
public void setBean(Object bean) {
|
||||
this.bean = bean;
|
||||
if (bean != null) {
|
||||
beanClass = bean.getClass();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class of the bean that the generated map should support.
|
||||
* You must call either this method or {@link #setBeanClass} before {@link #create}.
|
||||
*
|
||||
* @param beanClass the class of the bean
|
||||
*/
|
||||
public void setBeanClass(Class beanClass) {
|
||||
this.beanClass = beanClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the properties reflected by the generated map.
|
||||
*
|
||||
* @param require any combination of {@link #REQUIRE_GETTER} and
|
||||
* {@link #REQUIRE_SETTER}; default is zero (any property allowed)
|
||||
*/
|
||||
public void setRequire(int require) {
|
||||
this.require = require;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClassLoader getDefaultClassLoader() {
|
||||
return beanClass.getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProtectionDomain getProtectionDomain() {
|
||||
return ReflectUtils.getProtectionDomain(beanClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>BeanMap</code>. An existing
|
||||
* generated class will be reused if possible.
|
||||
*
|
||||
* @return {BladeBeanMap}
|
||||
*/
|
||||
public BladeBeanMap create() {
|
||||
if (beanClass == null) {
|
||||
throw new IllegalArgumentException("Class of bean unknown");
|
||||
}
|
||||
setNamePrefix(beanClass.getName());
|
||||
BladeBeanMapKey key = new BladeBeanMapKey(beanClass, require);
|
||||
return (BladeBeanMap) super.create(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateClass(ClassVisitor v) throws Exception {
|
||||
new BladeBeanMapEmitter(v, getClassName(), beanClass, require);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object firstInstance(Class type) {
|
||||
return ((BeanMap) ReflectUtils.newInstance(type)).newInstance(bean);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object nextInstance(Object instance) {
|
||||
return ((BeanMap) instance).newInstance(bean);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+192
@@ -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();
|
||||
// 过滤 getClass,Spring 的工具类会拿到该方法
|
||||
if (!"class".equals(propName)) {
|
||||
names.put(propName, prop);
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private String[] getNames(Map<String, PropertyDescriptor> propertyMap) {
|
||||
return propertyMap.keySet().toArray(new String[0]);
|
||||
}
|
||||
|
||||
private void generateConstructor() {
|
||||
CodeEmitter e = begin_method(Constants.ACC_PUBLIC, CSTRUCT_OBJECT, null);
|
||||
e.load_this();
|
||||
e.load_arg(0);
|
||||
e.super_invoke_constructor(CSTRUCT_OBJECT);
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
}
|
||||
|
||||
private void generateGet(Class type, final Map<String, PropertyDescriptor> getters) {
|
||||
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_GET, null);
|
||||
e.load_arg(0);
|
||||
e.checkcast(Type.getType(type));
|
||||
e.load_arg(1);
|
||||
e.checkcast(Constants.TYPE_STRING);
|
||||
EmitUtils.string_switch(e, getNames(getters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
|
||||
@Override
|
||||
public void processCase(Object key, Label end) {
|
||||
PropertyDescriptor pd = getters.get(key);
|
||||
MethodInfo method = ReflectUtils.getMethodInfo(pd.getReadMethod());
|
||||
e.invoke(method);
|
||||
e.box(method.getSignature().getReturnType());
|
||||
e.return_value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processDefault() {
|
||||
e.aconst_null();
|
||||
e.return_value();
|
||||
}
|
||||
});
|
||||
e.end_method();
|
||||
}
|
||||
|
||||
private void generatePut(Class type, final Map<String, PropertyDescriptor> setters) {
|
||||
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, BEAN_MAP_PUT, null);
|
||||
e.load_arg(0);
|
||||
e.checkcast(Type.getType(type));
|
||||
e.load_arg(1);
|
||||
e.checkcast(Constants.TYPE_STRING);
|
||||
EmitUtils.string_switch(e, getNames(setters), Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
|
||||
@Override
|
||||
public void processCase(Object key, Label end) {
|
||||
PropertyDescriptor pd = setters.get(key);
|
||||
if (pd.getReadMethod() == null) {
|
||||
e.aconst_null();
|
||||
} else {
|
||||
MethodInfo read = ReflectUtils.getMethodInfo(pd.getReadMethod());
|
||||
e.dup();
|
||||
e.invoke(read);
|
||||
e.box(read.getSignature().getReturnType());
|
||||
}
|
||||
// move old value behind bean
|
||||
e.swap();
|
||||
// new value
|
||||
e.load_arg(2);
|
||||
MethodInfo write = ReflectUtils.getMethodInfo(pd.getWriteMethod());
|
||||
e.unbox(write.getSignature().getArgumentTypes()[0]);
|
||||
e.invoke(write);
|
||||
e.return_value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processDefault() {
|
||||
// fall-through
|
||||
}
|
||||
});
|
||||
e.aconst_null();
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
}
|
||||
|
||||
private void generateKeySet(String[] allNames) {
|
||||
// static initializer
|
||||
declare_field(Constants.ACC_STATIC | Constants.ACC_PRIVATE, "keys", FIXED_KEY_SET, null);
|
||||
|
||||
CodeEmitter e = begin_static();
|
||||
e.new_instance(FIXED_KEY_SET);
|
||||
e.dup();
|
||||
EmitUtils.push_array(e, allNames);
|
||||
e.invoke_constructor(FIXED_KEY_SET, CSTRUCT_STRING_ARRAY);
|
||||
e.putfield("keys");
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
|
||||
// keySet
|
||||
e = begin_method(Constants.ACC_PUBLIC, KEY_SET, null);
|
||||
e.load_this();
|
||||
e.getfield("keys");
|
||||
e.return_value();
|
||||
e.end_method();
|
||||
}
|
||||
|
||||
private void generateGetPropertyType(final Map allProps, String[] allNames) {
|
||||
final CodeEmitter e = begin_method(Constants.ACC_PUBLIC, GET_PROPERTY_TYPE, null);
|
||||
e.load_arg(0);
|
||||
EmitUtils.string_switch(e, allNames, Constants.SWITCH_STYLE_HASH, new ObjectSwitchCallback() {
|
||||
@Override
|
||||
public void processCase(Object key, Label end) {
|
||||
PropertyDescriptor pd = (PropertyDescriptor) allProps.get(key);
|
||||
EmitUtils.load_class(e, Type.getType(pd.getPropertyType()));
|
||||
e.return_value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processDefault() {
|
||||
e.aconst_null();
|
||||
e.return_value();
|
||||
}
|
||||
});
|
||||
e.end_method();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.springblade.core.tool.beans;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* bean map key,提高性能
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
@AllArgsConstructor
|
||||
public class BladeBeanMapKey {
|
||||
private final Class type;
|
||||
private final int require;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.springblade.core.tool.beans;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* copy 字段 配置
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface CopyProperty {
|
||||
|
||||
/**
|
||||
* 属性名,用于指定别名,默认使用:field name
|
||||
* @return 属性名
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* 忽略:默认为 false
|
||||
* @return 是否忽略
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
}
|
||||
+23
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.config;
|
||||
|
||||
import org.springblade.core.tool.jackson.*;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* BladeView 视图序列化自动装配
|
||||
* <p>
|
||||
* 框架内置常驻加载,无需手动开启。
|
||||
* 当 Controller 方法未标注 {@link BladeView} 时不产生任何额外开销。
|
||||
* 所有 Bean 均支持 {@link ConditionalOnMissingBean},可由用户自定义覆盖。
|
||||
* </p>
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AutoConfiguration(after = JacksonConfiguration.class)
|
||||
public class BladeViewAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 视图解析器
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public BladeViewResolver bladeViewResolver(BladeJacksonProperties properties, ObjectProvider<BladeViewCustomizer> viewCustomizer) {
|
||||
BladeViewResolver resolver = new BladeViewResolver(properties.getView());
|
||||
viewCustomizer.orderedStream().forEach(customizer -> customizer.customize(resolver));
|
||||
return resolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应拦截器
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public BladeViewResponseAdvice bladeViewResponseAdvice(BladeViewResolver viewResolver, ObjectProvider<BladeRoleSupplier> roleNameSupplier) {
|
||||
return new BladeViewResponseAdvice(viewResolver, roleNameSupplier.getIfAvailable());
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springblade.core.tool.jackson.BladeJacksonProperties;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* Jackson配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AutoConfiguration(before = JacksonAutoConfiguration.class)
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
@EnableConfigurationProperties(BladeJacksonProperties.class)
|
||||
public class JacksonConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ObjectMapper objectMapper() {
|
||||
//创建默认的ObjectMapper
|
||||
ObjectMapper objectMapper = JsonUtil.getInstance();
|
||||
//允许空字符串序列化为null对象
|
||||
objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
|
||||
//自定义拓展配置
|
||||
objectMapper.findAndRegisterModules();
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.config;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tool.jackson.BladeJacksonProperties;
|
||||
import org.springblade.core.tool.jackson.MappingApiJackson2HttpMessageConverter;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.format.datetime.DateFormatter;
|
||||
import org.springframework.http.converter.*;
|
||||
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 消息配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@AllArgsConstructor
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class MessageConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final BladeJacksonProperties properties;
|
||||
|
||||
/**
|
||||
* 使用 JACKSON 作为JSON MessageConverter
|
||||
* 消息转换,内置断点续传,下载和字符串
|
||||
*/
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
converters.removeIf(x -> x instanceof StringHttpMessageConverter || x instanceof AbstractJackson2HttpMessageConverter);
|
||||
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
|
||||
converters.add(new ByteArrayHttpMessageConverter());
|
||||
converters.add(new ResourceHttpMessageConverter());
|
||||
converters.add(new ResourceRegionHttpMessageConverter());
|
||||
converters.add(new MappingApiJackson2HttpMessageConverter(objectMapper, properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期格式化
|
||||
*/
|
||||
@Override
|
||||
public void addFormatters(FormatterRegistry registry) {
|
||||
registry.addFormatter(new DateFormatter(DateUtil.PATTERN_DATE));
|
||||
registry.addFormatter(new DateFormatter(DateUtil.PATTERN_DATETIME));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.config;
|
||||
|
||||
|
||||
import org.springblade.core.tool.support.BinderSupplier;
|
||||
import org.springblade.core.tool.utils.SpringUtil;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 工具配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AutoConfiguration
|
||||
public class ToolConfiguration {
|
||||
|
||||
/**
|
||||
* Spring上下文缓存
|
||||
*/
|
||||
@Bean
|
||||
public SpringUtil springUtil() {
|
||||
return new SpringUtil();
|
||||
}
|
||||
|
||||
/**
|
||||
* Binder支持类
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Supplier<Object> binderSupplier() {
|
||||
return new BinderSupplier();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.constant;
|
||||
|
||||
/**
|
||||
* 系统常量
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface BladeConstant {
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
String UTF_8 = "UTF-8";
|
||||
|
||||
/**
|
||||
* contentType
|
||||
*/
|
||||
String CONTENT_TYPE_NAME = "Content-type";
|
||||
|
||||
/**
|
||||
* JSON 资源
|
||||
*/
|
||||
String CONTENT_TYPE = "application/json;charset=utf-8";
|
||||
|
||||
/**
|
||||
* 上下文键值
|
||||
*/
|
||||
String CONTEXT_KEY = "bladeContext";
|
||||
|
||||
/**
|
||||
* 用于 聚合层 向调用层传递请求信息 的请求头
|
||||
*/
|
||||
String CONTEXT_REQUEST_ID = "Blade-RequestId";
|
||||
/**
|
||||
* 用于 聚合层 向调用层传递用户信息 的请求头
|
||||
*/
|
||||
String CONTEXT_ACCOUNT_ID = "Blade-AccountId";
|
||||
/**
|
||||
* 用于 聚合层 向调用层传递租户信息 的请求头
|
||||
*/
|
||||
String CONTEXT_TENANT_ID = "Blade-TenantId";
|
||||
|
||||
/**
|
||||
* mdc request id key
|
||||
*/
|
||||
String MDC_REQUEST_ID_KEY = "requestId";
|
||||
|
||||
/**
|
||||
* mdc account id key
|
||||
*/
|
||||
String MDC_ACCOUNT_ID_KEY = "accountId";
|
||||
|
||||
/**
|
||||
* mdc tenant id key
|
||||
*/
|
||||
String MDC_TENANT_ID_KEY = "tenantId";
|
||||
|
||||
/**
|
||||
* 角色前缀
|
||||
*/
|
||||
String SECURITY_ROLE_PREFIX = "ROLE_";
|
||||
|
||||
/**
|
||||
* 主键字段名
|
||||
*/
|
||||
String DB_PRIMARY_KEY = "id";
|
||||
|
||||
/**
|
||||
* 主键字段get方法
|
||||
*/
|
||||
String DB_PRIMARY_KEY_METHOD = "getId";
|
||||
|
||||
/**
|
||||
* 租户字段名
|
||||
*/
|
||||
String DB_TENANT_KEY = "tenantId";
|
||||
|
||||
/**
|
||||
* 租户字段get方法
|
||||
*/
|
||||
String DB_TENANT_KEY_GET_METHOD = "getTenantId";
|
||||
|
||||
/**
|
||||
* 租户字段set方法
|
||||
*/
|
||||
String DB_TENANT_KEY_SET_METHOD = "setTenantId";
|
||||
|
||||
/**
|
||||
* 业务状态[正常]
|
||||
*/
|
||||
int DB_STATUS_NORMAL = 1;
|
||||
|
||||
/**
|
||||
* 业务状态[0、1、2]
|
||||
*/
|
||||
int DB_STATUS_0 = 0;
|
||||
int DB_STATUS_1 = 1;
|
||||
int DB_STATUS_2 = 2;
|
||||
|
||||
/**
|
||||
* 删除状态[0:正常,1:删除]
|
||||
*/
|
||||
int DB_NOT_DELETED = 0;
|
||||
int DB_IS_DELETED = 1;
|
||||
|
||||
/**
|
||||
* 用户锁定状态
|
||||
*/
|
||||
int DB_ADMIN_NON_LOCKED = 0;
|
||||
int DB_ADMIN_LOCKED = 1;
|
||||
|
||||
/**
|
||||
* 顶级父节点id
|
||||
*/
|
||||
Long TOP_PARENT_ID = 0L;
|
||||
|
||||
/**
|
||||
* 顶级父节点名称
|
||||
*/
|
||||
String TOP_PARENT_NAME = "顶级";
|
||||
|
||||
/**
|
||||
* 管理员对应的租户ID
|
||||
*/
|
||||
String ADMIN_TENANT_ID = "000000";
|
||||
|
||||
/**
|
||||
* 日志默认状态
|
||||
*/
|
||||
String LOG_NORMAL_TYPE = "1";
|
||||
|
||||
/**
|
||||
* 默认为空消息
|
||||
*/
|
||||
String DEFAULT_NULL_MESSAGE = "暂无承载数据";
|
||||
/**
|
||||
* 默认成功消息
|
||||
*/
|
||||
String DEFAULT_SUCCESS_MESSAGE = "操作成功";
|
||||
/**
|
||||
* 默认失败消息
|
||||
*/
|
||||
String DEFAULT_FAILURE_MESSAGE = "操作失败";
|
||||
/**
|
||||
* 默认未授权消息
|
||||
*/
|
||||
String DEFAULT_UNAUTHORIZED_MESSAGE = "签名认证失败";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.constant;
|
||||
|
||||
/**
|
||||
* 系统默认角色
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class RoleConstant {
|
||||
|
||||
public static final String ADMINISTRATOR = "administrator";
|
||||
|
||||
public static final String HAS_ROLE_ADMINISTRATOR = "hasRole('" + ADMINISTRATOR + "')";
|
||||
|
||||
public static final String ADMIN = "admin";
|
||||
|
||||
public static final String HAS_ROLE_ADMIN = "hasAnyRole('" + ADMINISTRATOR + "', '" + ADMIN + "')";
|
||||
|
||||
public static final String USER = "user";
|
||||
|
||||
public static final String HAS_ROLE_USER = "hasRole('" + USER + "')";
|
||||
|
||||
public static final String TEST = "test";
|
||||
|
||||
public static final String HAS_ROLE_TEST = "hasRole('" + TEST + "')";
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package org.springblade.core.tool.convert;
|
||||
|
||||
import org.springframework.boot.convert.ApplicationConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* 类型 转换 服务,添加了 IEnum 转换
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class BladeConversionService extends ApplicationConversionService {
|
||||
@Nullable
|
||||
private static volatile BladeConversionService SHARED_INSTANCE;
|
||||
|
||||
public BladeConversionService() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public BladeConversionService(@Nullable StringValueResolver embeddedValueResolver) {
|
||||
super(embeddedValueResolver);
|
||||
super.addConverter(new EnumToStringConverter());
|
||||
super.addConverter(new StringToEnumConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a shared default application {@code ConversionService} instance, lazily
|
||||
* building it once needed.
|
||||
* <p>
|
||||
* Note: This method actually returns an {@link BladeConversionService}
|
||||
* instance. However, the {@code ConversionService} signature has been preserved for
|
||||
* binary compatibility.
|
||||
* @return the shared {@code BladeConversionService} instance (never{@code null})
|
||||
*/
|
||||
public static GenericConversionService getInstance() {
|
||||
BladeConversionService sharedInstance = BladeConversionService.SHARED_INSTANCE;
|
||||
if (sharedInstance == null) {
|
||||
synchronized (BladeConversionService.class) {
|
||||
sharedInstance = BladeConversionService.SHARED_INSTANCE;
|
||||
if (sharedInstance == null) {
|
||||
sharedInstance = new BladeConversionService();
|
||||
BladeConversionService.SHARED_INSTANCE = sharedInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package org.springblade.core.tool.convert;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.function.CheckedFunction;
|
||||
import org.springblade.core.tool.utils.ClassUtil;
|
||||
import org.springblade.core.tool.utils.ConvertUtil;
|
||||
import org.springblade.core.tool.utils.ReflectUtil;
|
||||
import org.springblade.core.tool.utils.Unchecked;
|
||||
import org.springframework.cglib.core.Converter;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 组合 spring cglib Converter 和 spring ConversionService
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class BladeConverter implements Converter {
|
||||
private static final ConcurrentMap<String, TypeDescriptor> TYPE_CACHE = new ConcurrentHashMap<>();
|
||||
private final Class<?> sourceClazz;
|
||||
private final Class<?> targetClazz;
|
||||
|
||||
/**
|
||||
* cglib convert
|
||||
*
|
||||
* @param value 源对象属性
|
||||
* @param target 目标对象属性类
|
||||
* @param fieldName 目标的field名,原为 set 方法名,BladeBeanCopier 里做了更改
|
||||
* @return {Object}
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public Object convert(Object value, Class target, final Object fieldName) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
// 类型一样,不需要转换
|
||||
if (ClassUtil.isAssignableValue(target, value)) {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
TypeDescriptor targetDescriptor = BladeConverter.getTypeDescriptor(targetClazz, (String) fieldName);
|
||||
// 1. 判断 sourceClazz 为 Map
|
||||
if (Map.class.isAssignableFrom(sourceClazz)) {
|
||||
return ConvertUtil.convert(value, targetDescriptor);
|
||||
} else {
|
||||
TypeDescriptor sourceDescriptor = BladeConverter.getTypeDescriptor(sourceClazz, (String) fieldName);
|
||||
return ConvertUtil.convert(value, sourceDescriptor, targetDescriptor);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.warn("BladeConverter error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static TypeDescriptor getTypeDescriptor(final Class<?> clazz, final String fieldName) {
|
||||
String srcCacheKey = clazz.getName() + fieldName;
|
||||
// 忽略抛出异常的函数,定义完整泛型,避免编译问题
|
||||
CheckedFunction<String, TypeDescriptor> uncheckedFunction = (key) -> {
|
||||
// 这里 property 理论上不会为 null
|
||||
Field field = ReflectUtil.getField(clazz, fieldName);
|
||||
if (field == null) {
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
return new TypeDescriptor(field);
|
||||
};
|
||||
return TYPE_CACHE.computeIfAbsent(srcCacheKey, Unchecked.function(uncheckedFunction));
|
||||
}
|
||||
}
|
||||
+135
@@ -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);
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.convert;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.utils.ConvertUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.converter.ConditionalGenericConverter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* 接收参数 同 jackson String -》 Enum 转换
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Slf4j
|
||||
public class StringToEnumConverter implements ConditionalGenericConverter {
|
||||
/**
|
||||
* 缓存 Enum 类信息,提供性能
|
||||
*/
|
||||
private static final ConcurrentMap<Class<?>, AccessibleObject> ENUM_CACHE_MAP = new ConcurrentHashMap<>(8);
|
||||
|
||||
@Nullable
|
||||
private static AccessibleObject getAnnotation(Class<?> clazz) {
|
||||
Set<AccessibleObject> accessibleObjects = new HashSet<>();
|
||||
// JsonCreator METHOD, CONSTRUCTOR
|
||||
Constructor<?>[] constructors = clazz.getConstructors();
|
||||
Collections.addAll(accessibleObjects, constructors);
|
||||
// methods
|
||||
Method[] methods = clazz.getDeclaredMethods();
|
||||
Collections.addAll(accessibleObjects, methods);
|
||||
for (AccessibleObject accessibleObject : accessibleObjects) {
|
||||
// 复用 jackson 的 JsonCreator注解
|
||||
JsonCreator jsonCreator = accessibleObject.getAnnotation(JsonCreator.class);
|
||||
if (jsonCreator != null && JsonCreator.Mode.DISABLED != jsonCreator.mode()) {
|
||||
accessibleObject.setAccessible(true);
|
||||
return accessibleObject;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ConvertiblePair> getConvertibleTypes() {
|
||||
return Collections.singleton(new ConvertiblePair(String.class, Enum.class));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (StringUtil.isBlank((String) source)) {
|
||||
return null;
|
||||
}
|
||||
Class<?> clazz = targetType.getType();
|
||||
AccessibleObject accessibleObject = ENUM_CACHE_MAP.computeIfAbsent(clazz, StringToEnumConverter::getAnnotation);
|
||||
String value = ((String) source).trim();
|
||||
// 如果为null,走默认的转换
|
||||
if (accessibleObject == null) {
|
||||
return valueOf(clazz, value);
|
||||
}
|
||||
try {
|
||||
return StringToEnumConverter.invoke(clazz, accessibleObject, value);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Enum<T>> T valueOf(Class<?> clazz, String value){
|
||||
return Enum.valueOf((Class<T>) clazz, value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Object invoke(Class<?> clazz, AccessibleObject accessibleObject, String value)
|
||||
throws IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
if (accessibleObject instanceof Constructor) {
|
||||
Constructor constructor = (Constructor) accessibleObject;
|
||||
Class<?> paramType = constructor.getParameterTypes()[0];
|
||||
// 类型转换
|
||||
Object object = ConvertUtil.convert(value, paramType);
|
||||
return constructor.newInstance(object);
|
||||
}
|
||||
if (accessibleObject instanceof Method) {
|
||||
Method method = (Method) accessibleObject;
|
||||
Class<?> paramType = method.getParameterTypes()[0];
|
||||
// 类型转换
|
||||
Object object = ConvertUtil.convert(value, paramType);
|
||||
return method.invoke(clazz, object);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.function;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* 受检的 Callable
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CheckedCallable<T> {
|
||||
|
||||
/**
|
||||
* Run this callable.
|
||||
*
|
||||
* @return result
|
||||
* @throws Throwable CheckedException
|
||||
*/
|
||||
@Nullable
|
||||
T call() throws Throwable;
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.function;
|
||||
|
||||
/**
|
||||
* 受检的 Comparator
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CheckedComparator<T> {
|
||||
|
||||
/**
|
||||
* Compares its two arguments for order.
|
||||
*
|
||||
* @param o1 o1
|
||||
* @param o2 o2
|
||||
* @return int
|
||||
* @throws Throwable CheckedException
|
||||
*/
|
||||
int compare(T o1, T o2) throws Throwable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.function;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* 受检的 Consumer
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CheckedConsumer<T> {
|
||||
|
||||
/**
|
||||
* Run the Consumer
|
||||
*
|
||||
* @param t T
|
||||
* @throws Throwable UncheckedException
|
||||
*/
|
||||
@Nullable
|
||||
void accept(@Nullable T t) throws Throwable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.function;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* 受检的 function
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CheckedFunction<T, R> {
|
||||
|
||||
/**
|
||||
* Run the Function
|
||||
*
|
||||
* @param t T
|
||||
* @return R R
|
||||
* @throws Throwable CheckedException
|
||||
*/
|
||||
@Nullable
|
||||
R apply(@Nullable T t) throws Throwable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.function;
|
||||
|
||||
/**
|
||||
* 受检的 runnable
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CheckedRunnable {
|
||||
|
||||
/**
|
||||
* Run this runnable.
|
||||
*
|
||||
* @throws Throwable CheckedException
|
||||
*/
|
||||
void run() throws Throwable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.function;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* 受检的 Supplier
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CheckedSupplier<T> {
|
||||
|
||||
/**
|
||||
* Run the Supplier
|
||||
*
|
||||
* @return T
|
||||
* @throws Throwable CheckedException
|
||||
*/
|
||||
@Nullable
|
||||
T get() throws Throwable;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.springblade.core.tool.geo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 坐标点
|
||||
*
|
||||
* @author JourWon、hutool、L.cm
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class GeoPoint implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 3584864663880053897L;
|
||||
|
||||
/**
|
||||
* 经度
|
||||
*/
|
||||
private double lon;
|
||||
/**
|
||||
* 纬度
|
||||
*/
|
||||
private double lat;
|
||||
|
||||
/**
|
||||
* 当前坐标偏移指定坐标
|
||||
*
|
||||
* @param offset 偏移量
|
||||
* @return this
|
||||
*/
|
||||
public GeoPoint offset(GeoPoint offset) {
|
||||
this.lon += offset.lon;
|
||||
this.lat += offset.lat;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package org.springblade.core.tool.geo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 各坐标系之间的转换工具类
|
||||
* <p>
|
||||
* 参考:<a href="https://github.com/JourWon/coordinate-transform">coordinate-transform</a>
|
||||
* <p>
|
||||
* WGS84坐标系:即地球坐标系,国际上通用的坐标系。设备一般包含GPS芯片或者北斗芯片获取的经纬度为WGS84地理坐标系。
|
||||
* 谷歌地图采用的是WGS84地理坐标系(中国范围除外,谷歌中国地图采用的是GCJ02地理坐标系。)
|
||||
* <p>
|
||||
* GCJ02坐标系:即火星坐标系,WGS84坐标系经加密后的坐标系。
|
||||
* 出于国家安全考虑,国内所有导航电子地图必须使用国家测绘局制定的加密坐标系统,即将一个真实的经纬度坐标加密成一个不正确的经纬度坐标。
|
||||
* <p>
|
||||
* BD09坐标系:即百度坐标系,GCJ02坐标系经加密后的坐标系。搜狗坐标系、图吧坐标系等,估计也是在GCJ02基础上加密而成的。
|
||||
* <p>
|
||||
* 高德MapABC地图API 火星坐标
|
||||
* 腾讯搜搜地图API 火星坐标
|
||||
* 阿里云地图API 火星坐标
|
||||
* 灵图51ditu地图API 火星坐标
|
||||
* <p>
|
||||
* 百度地图API 百度坐标
|
||||
* 搜狐搜狗地图API 搜狗坐标
|
||||
* 图吧MapBar地图API 图吧坐标
|
||||
*
|
||||
* @author JourWon、hutool、L.cm
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum GeoType {
|
||||
|
||||
/**
|
||||
* WGS84
|
||||
*/
|
||||
WGS84("WGS84", "地球坐标系,国际通用坐标系") {
|
||||
@Override
|
||||
public GeoPoint toWGS84(double lon, double lat) {
|
||||
return new GeoPoint(lon, lat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint toGCJ02(double lon, double lat) {
|
||||
return GeoUtil.wgs84ToGcj02(lon, lat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint toBD09(double lon, double lat) {
|
||||
return GeoUtil.wgs84ToBd09(lon, lat);
|
||||
}
|
||||
},
|
||||
GCJ02("GCJ02", "火星坐标系,高德、腾讯、阿里等使用") {
|
||||
@Override
|
||||
public GeoPoint toWGS84(double lon, double lat) {
|
||||
return GeoUtil.gcj02ToWgs84(lon, lat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint toGCJ02(double lon, double lat) {
|
||||
return new GeoPoint(lon, lat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint toBD09(double lon, double lat) {
|
||||
return GeoUtil.gcj02ToBd09(lon, lat);
|
||||
}
|
||||
},
|
||||
BD09("BD09", "百度坐标系,百度、搜狗等使用") {
|
||||
@Override
|
||||
public GeoPoint toWGS84(double lon, double lat) {
|
||||
return GeoUtil.bd09toWgs84(lon, lat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint toGCJ02(double lon, double lat) {
|
||||
return GeoUtil.bd09ToGcj02(lon, lat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint toBD09(double lon, double lat) {
|
||||
return new GeoPoint(lon, lat);
|
||||
}
|
||||
};
|
||||
|
||||
@JsonValue
|
||||
private final String type;
|
||||
private final String desc;
|
||||
|
||||
/**
|
||||
* 转换成 地球坐标系
|
||||
*
|
||||
* @param lon lon
|
||||
* @param lat lat
|
||||
* @return GeoPoint
|
||||
*/
|
||||
public abstract GeoPoint toWGS84(double lon, double lat);
|
||||
|
||||
/**
|
||||
* 转换成 火星坐标系
|
||||
*
|
||||
* @param lon lon
|
||||
* @param lat lat
|
||||
* @return GeoPoint
|
||||
*/
|
||||
public abstract GeoPoint toGCJ02(double lon, double lat);
|
||||
|
||||
/**
|
||||
* 转换成 百度坐标系
|
||||
*
|
||||
* @param lon lon
|
||||
* @param lat lat
|
||||
* @return GeoPoint
|
||||
*/
|
||||
public abstract GeoPoint toBD09(double lon, double lat);
|
||||
|
||||
/**
|
||||
* 获取坐标系
|
||||
*
|
||||
* @param type type 坐标系类型
|
||||
* @return GeoType
|
||||
*/
|
||||
@JsonCreator
|
||||
public static GeoType getGeoType(String type) {
|
||||
for (GeoType geoType : values()) {
|
||||
if (geoType.type.equalsIgnoreCase(type)) {
|
||||
return geoType;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("未知的坐标系类型" + type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package org.springblade.core.tool.geo;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
* 位置工具类
|
||||
*
|
||||
* @author JourWon、hutool、L.cm
|
||||
*/
|
||||
public class GeoUtil {
|
||||
/**
|
||||
* 地球的半径 (m)
|
||||
*/
|
||||
public static final double EARTH_RADIUS = 6378137;
|
||||
/**
|
||||
* 圆周率π
|
||||
*/
|
||||
private static final double PI = 3.1415926535897932384626D;
|
||||
/**
|
||||
* 火星坐标系与百度坐标系转换的中间量
|
||||
*/
|
||||
private static final double X_PI = 3.14159265358979324 * 3000.0 / 180.0D;
|
||||
/**
|
||||
* 地球半径(Krasovsky 1940)
|
||||
*/
|
||||
public static final double RADIUS = 6378245.0D;
|
||||
/**
|
||||
* 修正参数(偏率ee)
|
||||
*/
|
||||
public static final double CORRECTION_PARAM = 0.00669342162296594323D;
|
||||
/**
|
||||
* 经纬度格式化为字符串
|
||||
*/
|
||||
public static final DecimalFormat FORMAT = new DecimalFormat("#.########");
|
||||
|
||||
/**
|
||||
* 根据经纬度计算两点之间的距离 (m)
|
||||
*
|
||||
* @param lon1 位置 1 的经度
|
||||
* @param lat1 位置 1 的纬度
|
||||
* @param lon2 位置 2 的经度
|
||||
* @param lat2 位置 2 的纬度
|
||||
* @return 返回距离
|
||||
*/
|
||||
public static double getDistance(double lon1, double lat1, double lon2, double lat2) {
|
||||
double radLat1 = radian(lat1);
|
||||
double radLat2 = radian(lat2);
|
||||
double a = radLat1 - radLat2;
|
||||
double b = radian(lon1) - radian(lon2);
|
||||
return (2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
|
||||
+ Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))))
|
||||
* EARTH_RADIUS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将整数形式的经纬度转换为十进制度格式,因为部分设备采集上来的为十进制形式的坐标
|
||||
*
|
||||
* @param coordinate 整数形式的经度或纬度
|
||||
* @return 十进制度格式的经纬度
|
||||
*/
|
||||
public static double getGpsValue(int coordinate) {
|
||||
int degrees = coordinate / (3600 * 100);
|
||||
int remainder = coordinate % (3600 * 100);
|
||||
int minutes = remainder / (60 * 100);
|
||||
remainder = remainder % (60 * 100);
|
||||
double seconds = remainder / 100.0;
|
||||
// 将分和秒转换为度的小数部分
|
||||
return degrees + (minutes / 60.0) + (seconds / 3600.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化经纬度
|
||||
*
|
||||
* @param value value
|
||||
* @return 格式化经纬度
|
||||
*/
|
||||
public static String formatGeo(double value) {
|
||||
return FORMAT.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断坐标是否在国外<br>
|
||||
* 火星坐标系 (GCJ-02)只对国内有效,国外无需转换
|
||||
*
|
||||
* @param lng 经度
|
||||
* @param lat 纬度
|
||||
* @return 坐标是否在国外
|
||||
*/
|
||||
public static boolean isOutOfChina(double lng, double lat) {
|
||||
return (lng < 72.004 || lng > 137.8347) || (lat < 0.8293 || lat > 55.8271);
|
||||
}
|
||||
|
||||
/**
|
||||
* WGS84 转换为 火星坐标系 (GCJ-02)
|
||||
*
|
||||
* @param lon 经度值
|
||||
* @param lat 纬度值
|
||||
* @return 火星坐标 (GCJ-02)
|
||||
*/
|
||||
public static GeoPoint wgs84ToGcj02(double lon, double lat) {
|
||||
return new GeoPoint(lon, lat).offset(offset(lon, lat, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* WGS84 坐标转为 百度坐标系 (BD-09) 坐标
|
||||
*
|
||||
* @param lon 经度值
|
||||
* @param lat 纬度值
|
||||
* @return bd09 坐标
|
||||
*/
|
||||
public static GeoPoint wgs84ToBd09(double lon, double lat) {
|
||||
final GeoPoint gcj02 = wgs84ToGcj02(lon, lat);
|
||||
return gcj02ToBd09(gcj02.getLon(), gcj02.getLat());
|
||||
}
|
||||
|
||||
/**
|
||||
* 火星坐标系 (GCJ-02) 转换为 WGS84
|
||||
*
|
||||
* @param lon 经度坐标
|
||||
* @param lat 纬度坐标
|
||||
* @return WGS84 坐标
|
||||
*/
|
||||
public static GeoPoint gcj02ToWgs84(double lon, double lat) {
|
||||
return new GeoPoint(lon, lat).offset(offset(lon, lat, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
|
||||
*
|
||||
* @param lon 经度值
|
||||
* @param lat 纬度值
|
||||
* @return BD-09 坐标
|
||||
*/
|
||||
public static GeoPoint gcj02ToBd09(double lon, double lat) {
|
||||
double z = Math.sqrt(lon * lon + lat * lat) + 0.00002 * Math.sin(lat * X_PI);
|
||||
double theta = Math.atan2(lat, lon) + 0.000003 * Math.cos(lon * X_PI);
|
||||
double bd_lng = z * Math.cos(theta) + 0.0065;
|
||||
double bd_lat = z * Math.sin(theta) + 0.006;
|
||||
return new GeoPoint(bd_lng, bd_lat);
|
||||
}
|
||||
|
||||
/**
|
||||
* 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
|
||||
* 即 百度 转 谷歌、高德
|
||||
*
|
||||
* @param lon 经度值
|
||||
* @param lat 纬度值
|
||||
* @return GCJ-02 坐标
|
||||
*/
|
||||
public static GeoPoint bd09ToGcj02(double lon, double lat) {
|
||||
double x = lon - 0.0065;
|
||||
double y = lat - 0.006;
|
||||
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);
|
||||
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
|
||||
double gg_lon = z * Math.cos(theta);
|
||||
double gg_lat = z * Math.sin(theta);
|
||||
return new GeoPoint(gg_lon, gg_lat);
|
||||
}
|
||||
|
||||
/**
|
||||
* 百度坐标系 (BD-09) 与 WGS84 的转换
|
||||
*
|
||||
* @param lon 经度值
|
||||
* @param lat 纬度值
|
||||
* @return WGS84坐标
|
||||
*/
|
||||
public static GeoPoint bd09toWgs84(double lon, double lat) {
|
||||
final GeoPoint gcj02 = bd09ToGcj02(lon, lat);
|
||||
return gcj02ToWgs84(gcj02.getLon(), gcj02.getLat());
|
||||
}
|
||||
|
||||
/**
|
||||
* WGS84 坐标转为 墨卡托投影
|
||||
*
|
||||
* @param lon 经度值
|
||||
* @param lat 纬度值
|
||||
* @return 墨卡托投影
|
||||
*/
|
||||
public static GeoPoint wgs84ToMercator(double lon, double lat) {
|
||||
double x = lon * 20037508.342789244 / 180;
|
||||
double y = Math.log(Math.tan((90 + lat) * PI / 360)) / (PI / 180);
|
||||
y = y * 20037508.342789244 / 180;
|
||||
return new GeoPoint(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 墨卡托投影 转为 WGS84 坐标
|
||||
*
|
||||
* @param mercatorX 墨卡托X坐标
|
||||
* @param mercatorY 墨卡托Y坐标
|
||||
* @return WGS84 坐标
|
||||
*/
|
||||
public static GeoPoint mercatorToWgs84(double mercatorX, double mercatorY) {
|
||||
double x = mercatorX / 20037508.342789244 * 180;
|
||||
double y = mercatorY / 20037508.342789244 * 180;
|
||||
y = 180 / PI * (2 * Math.atan(Math.exp(y * PI / 180)) - PI / 2);
|
||||
return new GeoPoint(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* WGS84 与 火星坐标系 (GCJ-02)转换的偏移算法(非精确)
|
||||
*
|
||||
* @param lon 经度值
|
||||
* @param lat 纬度值
|
||||
* @param isPlus 是否正向偏移:WGS84转GCJ-02使用正向,否则使用反向
|
||||
* @return 偏移坐标
|
||||
*/
|
||||
private static GeoPoint offset(double lon, double lat, boolean isPlus) {
|
||||
double dlon = transLon(lon - 105.0, lat - 35.0);
|
||||
double dlat = transLat(lon - 105.0, lat - 35.0);
|
||||
|
||||
double magic = Math.sin(lat / 180.0 * PI);
|
||||
magic = 1 - CORRECTION_PARAM * magic * magic;
|
||||
final double sqrtMagic = Math.sqrt(magic);
|
||||
|
||||
dlon = (dlon * 180.0) / (RADIUS / sqrtMagic * Math.cos(lat / 180.0 * PI) * PI);
|
||||
dlat = (dlat * 180.0) / ((RADIUS * (1 - CORRECTION_PARAM)) / (magic * sqrtMagic) * PI);
|
||||
|
||||
if (isPlus) {
|
||||
return new GeoPoint(dlon, dlat);
|
||||
} else {
|
||||
return new GeoPoint(-dlon, -dlat);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算经度坐标
|
||||
*
|
||||
* @param lon 经度坐标
|
||||
* @param lat 纬度坐标
|
||||
* @return ret 计算完成后的
|
||||
*/
|
||||
private static double transLon(double lon, double lat) {
|
||||
double ret = 300.0 + lon + 2.0 * lat + 0.1 * lon * lon + 0.1 * lon * lat + 0.1 * Math.sqrt(Math.abs(lon));
|
||||
ret += (20.0 * Math.sin(6.0 * lon * PI) + 20.0 * Math.sin(2.0 * lon * PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(lon * PI) + 40.0 * Math.sin(lon / 3.0 * PI)) * 2.0 / 3.0;
|
||||
ret += (150.0 * Math.sin(lon / 12.0 * PI) + 300.0 * Math.sin(lon / 30.0 * PI)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算纬度坐标
|
||||
*
|
||||
* @param lon 经度
|
||||
* @param lat 纬度
|
||||
* @return ret 计算完成后的
|
||||
*/
|
||||
private static double transLat(double lon, double lat) {
|
||||
double ret = -100.0 + 2.0 * lon + 3.0 * lat + 0.2 * lat * lat + 0.1 * lon * lat
|
||||
+ 0.2 * Math.sqrt(Math.abs(lon));
|
||||
ret += (20.0 * Math.sin(6.0 * lon * PI) + 20.0 * Math.sin(2.0 * lon * PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
|
||||
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static double radian(double d) {
|
||||
return d * PI / 180.0;
|
||||
}
|
||||
|
||||
}
|
||||
+152
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
|
||||
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import static org.springblade.core.tool.constant.BladeConstant.DB_PRIMARY_KEY;
|
||||
|
||||
/**
|
||||
* jackson 默认值为 null 时的处理
|
||||
* <p>
|
||||
* 主要是为了避免 app 端出现null导致闪退
|
||||
* <p>
|
||||
* 规则:
|
||||
* number -1
|
||||
* string ""
|
||||
* date ""
|
||||
* boolean false
|
||||
* array []
|
||||
* Object {}
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class BladeBeanSerializerModifier extends BeanSerializerModifier {
|
||||
@Override
|
||||
public List<BeanPropertyWriter> changeProperties(
|
||||
SerializationConfig config, BeanDescription beanDesc,
|
||||
List<BeanPropertyWriter> beanProperties) {
|
||||
// 循环所有的beanPropertyWriter
|
||||
beanProperties.forEach(writer -> {
|
||||
// 如果已经有 null 序列化处理如注解:@JsonSerialize(nullsUsing = xxx) 跳过
|
||||
if (writer.hasNullSerializer()) {
|
||||
return;
|
||||
}
|
||||
JavaType type = writer.getType();
|
||||
Class<?> clazz = type.getRawClass();
|
||||
if (type.isTypeOrSubTypeOf(Number.class)) {
|
||||
// 如果字段名为 id,则跳过 null 序列化处理
|
||||
if (StringUtil.equalsIgnoreCase(DB_PRIMARY_KEY, writer.getName())) {
|
||||
return;
|
||||
}
|
||||
writer.assignNullSerializer(NullJsonSerializers.NUMBER_JSON_SERIALIZER);
|
||||
} else if (type.isTypeOrSubTypeOf(Boolean.class)) {
|
||||
writer.assignNullSerializer(NullJsonSerializers.BOOLEAN_JSON_SERIALIZER);
|
||||
} else if (type.isTypeOrSubTypeOf(Character.class)) {
|
||||
writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
|
||||
} else if (type.isTypeOrSubTypeOf(String.class)) {
|
||||
writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
|
||||
} else if (type.isArrayType() || clazz.isArray() || type.isTypeOrSubTypeOf(Collection.class)) {
|
||||
writer.assignNullSerializer(NullJsonSerializers.ARRAY_JSON_SERIALIZER);
|
||||
} else if (type.isTypeOrSubTypeOf(OffsetDateTime.class)) {
|
||||
writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
|
||||
} else if (type.isTypeOrSubTypeOf(Date.class) || type.isTypeOrSubTypeOf(TemporalAccessor.class)) {
|
||||
writer.assignNullSerializer(NullJsonSerializers.STRING_JSON_SERIALIZER);
|
||||
} else {
|
||||
writer.assignNullSerializer(NullJsonSerializers.OBJECT_JSON_SERIALIZER);
|
||||
}
|
||||
});
|
||||
return super.changeProperties(config, beanDesc, beanProperties);
|
||||
}
|
||||
|
||||
public interface NullJsonSerializers {
|
||||
|
||||
JsonSerializer<Object> STRING_JSON_SERIALIZER = new JsonSerializer<Object>() {
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeString(StringPool.EMPTY);
|
||||
}
|
||||
};
|
||||
|
||||
JsonSerializer<Object> NUMBER_JSON_SERIALIZER = new JsonSerializer<Object>() {
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeNumber(StringUtil.INDEX_NOT_FOUND);
|
||||
}
|
||||
};
|
||||
|
||||
JsonSerializer<Object> BOOLEAN_JSON_SERIALIZER = new JsonSerializer<Object>() {
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeObject(Boolean.FALSE);
|
||||
}
|
||||
};
|
||||
|
||||
JsonSerializer<Object> ARRAY_JSON_SERIALIZER = new JsonSerializer<Object>() {
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeStartArray();
|
||||
gen.writeEndArray();
|
||||
}
|
||||
};
|
||||
|
||||
JsonSerializer<Object> OBJECT_JSON_SERIALIZER = new JsonSerializer<Object>() {
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
gen.writeStartObject();
|
||||
gen.writeEndObject();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* jackson 配置
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties("blade.jackson")
|
||||
public class BladeJacksonProperties {
|
||||
|
||||
/**
|
||||
* null 转为 空,字符串转成"",数组转为[],对象转为{},数字转为-1
|
||||
*/
|
||||
private Boolean nullToEmpty = Boolean.TRUE;
|
||||
/**
|
||||
* 响应到前端,大数值自动写出为 String,避免精度丢失
|
||||
*/
|
||||
private Boolean bigNumToString = Boolean.TRUE;
|
||||
/**
|
||||
* 支持 MediaType text/plain,用于和 blade-api-crypto 一起使用
|
||||
*/
|
||||
private Boolean supportTextPlain = Boolean.FALSE;
|
||||
|
||||
/**
|
||||
* 视图配置
|
||||
*/
|
||||
private View view = new View();
|
||||
|
||||
/**
|
||||
* Jackson Views 视图配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public static class View {
|
||||
/**
|
||||
* 动态模式的默认视图(角色获取不到时降级)
|
||||
* <p>可选: summary / detail / admin / administrator</p>
|
||||
*/
|
||||
private String defaultView = "summary";
|
||||
/**
|
||||
* 角色 → 视图映射
|
||||
*/
|
||||
private Map<String, String> roleMapping = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
+57
@@ -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);
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* BladeView 角色名提供者
|
||||
* <p>
|
||||
* 用于 {@link BladeViewResponseAdvice} 动态模式下按角色解析视图。
|
||||
* 自定义时请实现本接口而非通用的 {@code Supplier<String>},
|
||||
* 以避免与业务工程中其它 {@code Supplier<String>} Bean 产生歧义。
|
||||
* </p>
|
||||
*
|
||||
* <h3>示例</h3>
|
||||
* <pre>
|
||||
* @Bean
|
||||
* public BladeRoleSupplier myRoleSupplier() {
|
||||
* return () -> currentUserHolder.getRole();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface BladeRoleSupplier extends Supplier<String> {
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
|
||||
/**
|
||||
* java 8 时间默认序列化
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class BladeSensitiveModule extends SimpleModule {
|
||||
public static final BladeSensitiveModule INSTANCE = new BladeSensitiveModule();
|
||||
|
||||
public BladeSensitiveModule() {
|
||||
super(BladeSensitiveModule.class.getName());
|
||||
this.addSerializer(String.class, SensitiveSerializer.instance);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* BladeX 视图控制注解(全场景统一)
|
||||
* <p>
|
||||
* 用在字段上:标记该字段的可见视图层级(替代 {@code @JsonView})
|
||||
* 用在方法/类上:控制接口的视图过滤策略
|
||||
* </p>
|
||||
*
|
||||
* <h3>字段标注</h3>
|
||||
* <pre>
|
||||
* @BladeView(Views.Detail.class)
|
||||
* private String tenantName;
|
||||
* </pre>
|
||||
*
|
||||
* <h3>Controller 标注</h3>
|
||||
* <pre>
|
||||
* // 静态:明确指定视图
|
||||
* @BladeView(Views.Summary.class)
|
||||
* public R<List<UserVO>> list() { ... }
|
||||
*
|
||||
* // 动态:根据用户角色自动解析
|
||||
* @BladeView
|
||||
* public R<UserVO> detail() { ... }
|
||||
*
|
||||
* // 类级别默认(方法级可覆盖)
|
||||
* @BladeView(Views.Admin.class)
|
||||
* public class AdminController { ... }
|
||||
* </pre>
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface BladeView {
|
||||
|
||||
/**
|
||||
* 视图类型
|
||||
* <p>
|
||||
* 字段上:指定 Views.Summary / Detail / Admin / Administrator
|
||||
* 方法上:指定具体视图 = 静态模式;默认 Auto.class = 动态模式
|
||||
* </p>
|
||||
*/
|
||||
Class<?> value() default Auto.class;
|
||||
|
||||
/**
|
||||
* 动态解析标记(仅用于 Controller 方法/类)
|
||||
*/
|
||||
interface Auto {
|
||||
}
|
||||
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import com.fasterxml.jackson.databind.introspect.Annotated;
|
||||
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* 自定义 Jackson 注解内省器
|
||||
* <p>
|
||||
* 让 Jackson 序列化引擎识别字段上的 {@link BladeView} 注解,
|
||||
* 将其等同于标准的 {@code @JsonView} 处理。
|
||||
* </p>
|
||||
* <p>
|
||||
* 继承 {@link JacksonAnnotationIntrospector},所有标准 Jackson 注解
|
||||
* ({@code @JsonIgnore}、{@code @JsonSerialize}、{@code @JsonFormat} 等)
|
||||
* 通过 super 正常工作。
|
||||
* </p>
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class BladeViewAnnotationIntrospector extends JacksonAnnotationIntrospector {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Class<?>[] findViews(Annotated a) {
|
||||
// 优先识别 @BladeView(仅处理非 Auto 的静态视图标注)
|
||||
BladeView bladeView = _findAnnotation(a, BladeView.class);
|
||||
if (bladeView != null && bladeView.value() != BladeView.Auto.class) {
|
||||
return new Class<?>[]{bladeView.value()};
|
||||
}
|
||||
// 兼容:仍然识别标准 @JsonView
|
||||
return super.findViews(a);
|
||||
}
|
||||
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
/**
|
||||
* 视图自定义扩展接口
|
||||
* <p>
|
||||
* 用户可通过实现此接口来注册自定义视图层级,
|
||||
* 注册为 Spring Bean 即可生效。
|
||||
* </p>
|
||||
*
|
||||
* <h3>示例:新增超管以上的超级视图</h3>
|
||||
* <pre>
|
||||
* // 1. 定义自定义视图接口
|
||||
* public interface SuperView extends Views.Administrator {}
|
||||
*
|
||||
* // 2. 注册到解析器
|
||||
* @Bean
|
||||
* public BladeViewCustomizer myViewCustomizer() {
|
||||
* return resolver -> resolver.registerView("super", SuperView.class, 4);
|
||||
* }
|
||||
*
|
||||
* // 3. 在 YAML 中映射角色
|
||||
* // blade.jackson.view.role-mapping.superadmin: super
|
||||
* </pre>
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface BladeViewCustomizer {
|
||||
|
||||
/**
|
||||
* 自定义视图注册
|
||||
*
|
||||
* @param resolver 视图解析器
|
||||
*/
|
||||
void customize(BladeViewResolver resolver);
|
||||
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 视图解析器 — 视图名/角色名 → 视图 Class
|
||||
* <p>
|
||||
* 内置四级视图:summary(0) → detail(1) → admin(2) → administrator(3)
|
||||
* 可通过 {@link BladeViewCustomizer} 注册自定义视图层级
|
||||
* </p>
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class BladeViewResolver {
|
||||
|
||||
private final BladeJacksonProperties.View viewProperties;
|
||||
|
||||
/**
|
||||
* 视图名称 → 视图 Class 映射(线程安全)
|
||||
*/
|
||||
private final Map<String, Class<?>> viewMapping = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 视图名称 → 优先级映射(数字越大优先级越高,线程安全)
|
||||
*/
|
||||
private final Map<String, Integer> priorityMapping = new ConcurrentHashMap<>();
|
||||
|
||||
public BladeViewResolver(BladeJacksonProperties.View viewProperties) {
|
||||
this.viewProperties = viewProperties;
|
||||
// 注册内置四级视图
|
||||
registerView("summary", Views.Summary.class, 0);
|
||||
registerView("list", Views.Summary.class, 0);
|
||||
registerView("detail", Views.Detail.class, 1);
|
||||
registerView("admin", Views.Admin.class, 2);
|
||||
registerView("administrator", Views.Administrator.class, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册自定义视图层级
|
||||
*
|
||||
* @param name 视图名称(不区分大小写)
|
||||
* @param viewClass 视图 Class(建议继承 {@link Views} 中的接口)
|
||||
* @param priority 优先级(数字越大权限越高,内置:summary=0, detail=1, admin=2, administrator=3)
|
||||
*/
|
||||
public void registerView(String name, Class<?> viewClass, int priority) {
|
||||
viewMapping.put(name.toLowerCase(), viewClass);
|
||||
priorityMapping.put(name.toLowerCase(), priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* 视图名称 → Class(未知名称降级到 Summary — 最小权限原则)
|
||||
*
|
||||
* @param viewName 视图名称
|
||||
* @return 视图 Class
|
||||
*/
|
||||
public Class<?> resolve(String viewName) {
|
||||
return viewMapping.getOrDefault(viewName.toLowerCase(), Views.Summary.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据角色名解析视图(支持逗号分隔多角色,取最高权限)
|
||||
*
|
||||
* @param roleName 角色名(可逗号分隔)
|
||||
* @return 视图 Class
|
||||
*/
|
||||
public Class<?> resolveByRole(String roleName) {
|
||||
if (roleName == null || roleName.isEmpty()) {
|
||||
return resolve(viewProperties.getDefaultView());
|
||||
}
|
||||
|
||||
Map<String, String> mapping = viewProperties.getRoleMapping();
|
||||
String bestView = null;
|
||||
int bestPriority = -1;
|
||||
|
||||
for (String role : roleName.split(",")) {
|
||||
String viewName = mapping.get(role.trim().toLowerCase());
|
||||
if (viewName != null) {
|
||||
int priority = priorityMapping.getOrDefault(viewName.toLowerCase(), -1);
|
||||
if (priority > bestPriority) {
|
||||
bestPriority = priority;
|
||||
bestView = viewName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resolve(bestView != null ? bestView : viewProperties.getDefaultView());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认视图
|
||||
*
|
||||
* @return 默认视图 Class
|
||||
*/
|
||||
public Class<?> getDefaultView() {
|
||||
return resolve(viewProperties.getDefaultView());
|
||||
}
|
||||
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.auto.annotation.AutoIgnore;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJacksonValue;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
/**
|
||||
* {@link BladeView} 响应拦截器
|
||||
* <p>
|
||||
* 拦截 Controller 方法/类上的 {@link BladeView} 注解,
|
||||
* 解析视图 Class 后包装为 {@link MappingJacksonValue} 交给 Jackson 处理。
|
||||
* </p>
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AutoIgnore
|
||||
@ControllerAdvice
|
||||
@RequiredArgsConstructor
|
||||
public class BladeViewResponseAdvice implements ResponseBodyAdvice<Object> {
|
||||
|
||||
private final BladeViewResolver viewResolver;
|
||||
|
||||
@Nullable
|
||||
private final BladeRoleSupplier roleNameSupplier;
|
||||
|
||||
@Override
|
||||
public boolean supports(@NonNull MethodParameter returnType,
|
||||
@NonNull Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return returnType.hasMethodAnnotation(BladeView.class)
|
||||
|| returnType.getDeclaringClass().isAnnotationPresent(BladeView.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object beforeBodyWrite(@Nullable Object body,
|
||||
@NonNull MethodParameter returnType,
|
||||
@NonNull MediaType selectedContentType,
|
||||
@NonNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
|
||||
@NonNull ServerHttpRequest request,
|
||||
@NonNull ServerHttpResponse response) {
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 方法级优先于类级
|
||||
BladeView annotation = returnType.getMethodAnnotation(BladeView.class);
|
||||
if (annotation == null) {
|
||||
annotation = returnType.getDeclaringClass().getAnnotation(BladeView.class);
|
||||
}
|
||||
if (annotation == null) {
|
||||
return body;
|
||||
}
|
||||
|
||||
Class<?> viewClass = resolveViewClass(annotation);
|
||||
|
||||
MappingJacksonValue container;
|
||||
if (body instanceof MappingJacksonValue) {
|
||||
container = (MappingJacksonValue) body;
|
||||
} else {
|
||||
container = new MappingJacksonValue(body);
|
||||
}
|
||||
container.setSerializationView(viewClass);
|
||||
return container;
|
||||
}
|
||||
|
||||
private Class<?> resolveViewClass(BladeView annotation) {
|
||||
if (annotation.value() != BladeView.Auto.class) {
|
||||
return annotation.value();
|
||||
}
|
||||
if (roleNameSupplier != null) {
|
||||
return viewResolver.resolveByRole(roleNameSupplier.get());
|
||||
}
|
||||
return viewResolver.getDefaultView();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,997 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.TreeNode;
|
||||
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.*;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.fasterxml.jackson.databind.type.CollectionLikeType;
|
||||
import com.fasterxml.jackson.databind.type.MapType;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.utils.*;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Serial;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Jackson工具类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
public class JsonUtil {
|
||||
|
||||
/**
|
||||
* 将对象序列化成json字符串
|
||||
*
|
||||
* @param value javaBean
|
||||
* @return jsonString json字符串
|
||||
*/
|
||||
public static <T> String toJson(T value) {
|
||||
try {
|
||||
return getInstance().writeValueAsString(value);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象序列化成 json byte 数组
|
||||
*
|
||||
* @param object javaBean
|
||||
* @return jsonString json字符串
|
||||
*/
|
||||
public static byte[] toJsonAsBytes(Object object) {
|
||||
try {
|
||||
return getInstance().writeValueAsBytes(object);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param content content
|
||||
* @param valueType class
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(String content, Class<T> valueType) {
|
||||
try {
|
||||
return getInstance().readValue(content, valueType);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param content content
|
||||
* @param typeReference 泛型类型
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(String content, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return getInstance().readValue(content, typeReference);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json byte 数组反序列化成对象
|
||||
*
|
||||
* @param bytes json bytes
|
||||
* @param valueType class
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(byte[] bytes, Class<T> valueType) {
|
||||
try {
|
||||
return getInstance().readValue(bytes, valueType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param bytes bytes
|
||||
* @param typeReference 泛型类型
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(byte[] bytes, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return getInstance().readValue(bytes, typeReference);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param in InputStream
|
||||
* @param valueType class
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(InputStream in, Class<T> valueType) {
|
||||
try {
|
||||
return getInstance().readValue(in, valueType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param in InputStream
|
||||
* @param typeReference 泛型类型
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(InputStream in, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return getInstance().readValue(in, typeReference);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成List对象
|
||||
*
|
||||
* @param content content
|
||||
* @param valueTypeRef class
|
||||
* @param <T> T 泛型标记
|
||||
* @return List<T>
|
||||
*/
|
||||
public static <T> List<T> parseArray(String content, Class<T> valueTypeRef) {
|
||||
try {
|
||||
|
||||
if (!StringUtil.startsWithIgnoreCase(content, StringPool.LEFT_SQ_BRACKET)) {
|
||||
content = StringPool.LEFT_SQ_BRACKET + content + StringPool.RIGHT_SQ_BRACKET;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> list = getInstance().readValue(content, new TypeReference<List<Map<String, Object>>>() {
|
||||
});
|
||||
|
||||
List<T> result = new ArrayList<>();
|
||||
for (Map<String, Object> map : list) {
|
||||
result.add(toPojo(map, valueTypeRef));
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json字符串转成 JsonNode
|
||||
*
|
||||
* @param jsonString jsonString
|
||||
* @return jsonString json字符串
|
||||
*/
|
||||
public static JsonNode readTree(String jsonString) {
|
||||
Objects.requireNonNull(jsonString, "jsonString is null");
|
||||
try {
|
||||
return getInstance().readTree(jsonString);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json字符串转成 JsonNode
|
||||
*
|
||||
* @param in InputStream
|
||||
* @return jsonString json字符串
|
||||
*/
|
||||
public static JsonNode readTree(InputStream in) {
|
||||
Objects.requireNonNull(in, "InputStream in is null");
|
||||
try {
|
||||
return getInstance().readTree(in);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json字符串转成 JsonNode
|
||||
*
|
||||
* @param content content
|
||||
* @return jsonString json字符串
|
||||
*/
|
||||
public static JsonNode readTree(byte[] content) {
|
||||
Objects.requireNonNull(content, "byte[] content is null");
|
||||
try {
|
||||
return getInstance().readTree(content);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json字符串转成 JsonNode
|
||||
*
|
||||
* @param jsonParser JsonParser
|
||||
* @return jsonString json字符串
|
||||
*/
|
||||
public static JsonNode readTree(JsonParser jsonParser) {
|
||||
Objects.requireNonNull(jsonParser, "jsonParser is null");
|
||||
try {
|
||||
return getInstance().readTree(jsonParser);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将json byte 数组反序列化成对象
|
||||
*
|
||||
* @param content json bytes
|
||||
* @param valueType class
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable byte[] content, Class<T> valueType) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, valueType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param jsonString jsonString
|
||||
* @param valueType class
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable String jsonString, Class<T> valueType) {
|
||||
if (StringUtil.isBlank(jsonString)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(jsonString, valueType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param in InputStream
|
||||
* @param valueType class
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable InputStream in, Class<T> valueType) {
|
||||
if (in == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(in, valueType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param content bytes
|
||||
* @param typeReference 泛型类型
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable byte[] content, TypeReference<T> typeReference) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, typeReference);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param jsonString jsonString
|
||||
* @param typeReference 泛型类型
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable String jsonString, TypeReference<T> typeReference) {
|
||||
if (StringUtil.isBlank(jsonString)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(jsonString, typeReference);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param in InputStream
|
||||
* @param typeReference 泛型类型
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable InputStream in, TypeReference<T> typeReference) {
|
||||
if (in == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(in, typeReference);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param content bytes
|
||||
* @param javaType JavaType
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable byte[] content, JavaType javaType) {
|
||||
if (content == null || content.length == 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, javaType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param jsonString jsonString
|
||||
* @param javaType JavaType
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable String jsonString, JavaType javaType) {
|
||||
if (StringUtil.isBlank(jsonString)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(jsonString, javaType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将json反序列化成对象
|
||||
*
|
||||
* @param in InputStream
|
||||
* @param javaType JavaType
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable InputStream in, JavaType javaType) {
|
||||
if (in == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(in, javaType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将java.io.Reader反序列化成对象
|
||||
*
|
||||
* @param reader java.io.Reader
|
||||
* @param javaType JavaType
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T readValue(@Nullable Reader reader, JavaType javaType) {
|
||||
if (reader == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(reader, javaType);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clazz 获取 JavaType
|
||||
*
|
||||
* @param clazz Class
|
||||
* @return MapType
|
||||
*/
|
||||
public static JavaType getType(Class<?> clazz) {
|
||||
return getInstance().getTypeFactory().constructType(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装 map type,keyClass String
|
||||
*
|
||||
* @param valueClass value 类型
|
||||
* @return MapType
|
||||
*/
|
||||
public static MapType getMapType(Class<?> valueClass) {
|
||||
return getMapType(String.class, valueClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装 map type
|
||||
*
|
||||
* @param keyClass key 类型
|
||||
* @param valueClass value 类型
|
||||
* @return MapType
|
||||
*/
|
||||
public static MapType getMapType(Class<?> keyClass, Class<?> valueClass) {
|
||||
return getInstance().getTypeFactory().constructMapType(Map.class, keyClass, valueClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装 map type
|
||||
*
|
||||
* @param elementClass 集合值类型
|
||||
* @return CollectionLikeType
|
||||
*/
|
||||
public static CollectionLikeType getListType(Class<?> elementClass) {
|
||||
return getInstance().getTypeFactory().constructCollectionLikeType(List.class, elementClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content bytes
|
||||
* @param elementClass elementClass
|
||||
* @param <T> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <T> List<T> readList(@Nullable byte[] content, Class<T> elementClass) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, getListType(elementClass));
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content InputStream
|
||||
* @param elementClass elementClass
|
||||
* @param <T> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <T> List<T> readList(@Nullable InputStream content, Class<T> elementClass) {
|
||||
if (content == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, getListType(elementClass));
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content bytes
|
||||
* @param elementClass elementClass
|
||||
* @param <T> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <T> List<T> readList(@Nullable String content, Class<T> elementClass) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, getListType(elementClass));
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content InputStream
|
||||
* @param valueClass 值类型
|
||||
* @param <V> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <V> Map<String, V> readMap(@Nullable InputStream content, Class<?> valueClass) {
|
||||
return readMap(content, String.class, valueClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param reader java.io.Reader
|
||||
* @param valueClass 值类型
|
||||
* @param <V> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <V> Map<String, V> readMap(@Nullable Reader reader, Class<?> valueClass) {
|
||||
return readMap(reader, String.class, valueClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content bytes
|
||||
* @param valueClass 值类型
|
||||
* @param <V> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <V> Map<String, V> readMap(@Nullable String content, Class<?> valueClass) {
|
||||
return readMap(content, String.class, valueClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content bytes
|
||||
* @param keyClass key类型
|
||||
* @param valueClass 值类型
|
||||
* @param <K> 泛型
|
||||
* @param <V> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <K, V> Map<K, V> readMap(@Nullable byte[] content, Class<?> keyClass, Class<?> valueClass) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, getMapType(keyClass, valueClass));
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content InputStream
|
||||
* @param keyClass key类型
|
||||
* @param valueClass 值类型
|
||||
* @param <K> 泛型
|
||||
* @param <V> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <K, V> Map<K, V> readMap(@Nullable InputStream content, Class<?> keyClass, Class<?> valueClass) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, getMapType(keyClass, valueClass));
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param reader java.io.Reader
|
||||
* @param keyClass key类型
|
||||
* @param valueClass 值类型
|
||||
* @param <K> 泛型
|
||||
* @param <V> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <K, V> Map<K, V> readMap(@Nullable Reader reader, Class<?> keyClass, Class<?> valueClass) {
|
||||
if (reader == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(reader, getMapType(keyClass, valueClass));
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content bytes
|
||||
* @param keyClass key类型
|
||||
* @param valueClass 值类型
|
||||
* @param <K> 泛型
|
||||
* @param <V> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <K, V> Map<K, V> readMap(@Nullable String content, Class<?> keyClass, Class<?> valueClass) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, getMapType(keyClass, valueClass));
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content bytes
|
||||
* @return 集合
|
||||
*/
|
||||
public static Map<String, Object> readMap(@Nullable byte[] content) {
|
||||
return readMap(content, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content bytes
|
||||
* @return 集合
|
||||
*/
|
||||
public static Map<String, Object> readMap(@Nullable String content) {
|
||||
return readMap(content, String.class, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content InputStream
|
||||
* @return 集合
|
||||
*/
|
||||
public static Map<String, Object> readMap(@Nullable InputStream content) {
|
||||
return readMap(content, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param reader java.io.Reader
|
||||
* @return 集合
|
||||
*/
|
||||
public static Map<String, Object> readMap(@Nullable Reader reader) {
|
||||
return readMap(reader, Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content bytes
|
||||
* @param valueClass 值类型
|
||||
* @param <V> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <V> Map<String, V> readMap(@Nullable byte[] content, Class<?> valueClass) {
|
||||
return readMap(content, String.class, valueClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取集合
|
||||
*
|
||||
* @param content bytes
|
||||
* @return 集合
|
||||
*/
|
||||
public static List<Map<String, Object>> readListMap(@Nullable String content) {
|
||||
if (ObjectUtil.isEmpty(content)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return getInstance().readValue(content, getListType(Map.class));
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装参数化类型
|
||||
*
|
||||
* <p>
|
||||
* 例如: Map.class, String.class, String.class 对应 Map[String, String]
|
||||
* </p>
|
||||
*
|
||||
* @param parametrized 泛型参数化
|
||||
* @param parameterClasses 泛型参数类型
|
||||
* @return JavaType
|
||||
*/
|
||||
public static JavaType getParametricType(Class<?> parametrized, Class<?>... parameterClasses) {
|
||||
return getInstance().getTypeFactory().constructParametricType(parametrized, parameterClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 封装参数化类型,用来构造复杂的泛型
|
||||
*
|
||||
* <p>
|
||||
* 例如: Map.class, String.class, String.class 对应 Map[String, String]
|
||||
* </p>
|
||||
*
|
||||
* @param parametrized 泛型参数化
|
||||
* @param parameterTypes 泛型参数类型
|
||||
* @return JavaType
|
||||
*/
|
||||
public static JavaType getParametricType(Class<?> parametrized, JavaType... parameterTypes) {
|
||||
return getInstance().getTypeFactory().constructParametricType(parametrized, parameterTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* jackson 的类型转换
|
||||
*
|
||||
* @param fromValue 来源对象
|
||||
* @param toValueType 转换的类型
|
||||
* @param <T> 泛型标记
|
||||
* @return 转换结果
|
||||
*/
|
||||
public static <T> T convertValue(Object fromValue, Class<T> toValueType) {
|
||||
return getInstance().convertValue(fromValue, toValueType);
|
||||
}
|
||||
|
||||
/**
|
||||
* jackson 的类型转换
|
||||
*
|
||||
* @param fromValue 来源对象
|
||||
* @param toValueType 转换的类型
|
||||
* @param <T> 泛型标记
|
||||
* @return 转换结果
|
||||
*/
|
||||
public static <T> T convertValue(Object fromValue, JavaType toValueType) {
|
||||
return getInstance().convertValue(fromValue, toValueType);
|
||||
}
|
||||
|
||||
/**
|
||||
* jackson 的类型转换
|
||||
*
|
||||
* @param fromValue 来源对象
|
||||
* @param toValueTypeRef 泛型类型
|
||||
* @param <T> 泛型标记
|
||||
* @return 转换结果
|
||||
*/
|
||||
public static <T> T convertValue(Object fromValue, TypeReference<T> toValueTypeRef) {
|
||||
return getInstance().convertValue(fromValue, toValueTypeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* tree 转对象
|
||||
*
|
||||
* @param treeNode TreeNode
|
||||
* @param valueType valueType
|
||||
* @param <T> 泛型标记
|
||||
* @return 转换结果
|
||||
*/
|
||||
public static <T> T treeToValue(TreeNode treeNode, Class<T> valueType) {
|
||||
try {
|
||||
return getInstance().treeToValue(treeNode, valueType);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象转为 json node
|
||||
*
|
||||
* @param value 对象
|
||||
* @return JsonNode
|
||||
*/
|
||||
public static JsonNode valueToTree(@Nullable Object value) {
|
||||
return getInstance().valueToTree(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否可以序列化
|
||||
*
|
||||
* @param value 对象
|
||||
* @return 是否可以序列化
|
||||
*/
|
||||
public static boolean canSerialize(@Nullable Object value) {
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
return getInstance().canSerialize(value.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转成map形式
|
||||
*
|
||||
* @param content json字符串
|
||||
* @return {Map}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, Object> toMap(String content) {
|
||||
try {
|
||||
return getInstance().readValue(content, Map.class);
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转成map形式
|
||||
*
|
||||
* @param bean 源对象
|
||||
* @return {Map}
|
||||
*/
|
||||
public static Map<String, Object> toMap(Object bean) {
|
||||
return JsonUtil.convertValue(bean, JsonUtil.getMapType(Object.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转成map形式
|
||||
*
|
||||
* @param content json字符串
|
||||
* @param valueTypeRef 泛型类型
|
||||
* @param <T> 泛型
|
||||
* @return {Map}
|
||||
*/
|
||||
public static <T> Map<String, T> toMap(String content, Class<T> valueTypeRef) {
|
||||
try {
|
||||
Map<String, Map<String, Object>> map = getInstance().readValue(content, new TypeReference<Map<String, Map<String, Object>>>() {
|
||||
});
|
||||
Map<String, T> result = new HashMap<>(16);
|
||||
for (Map.Entry<String, Map<String, Object>> entry : map.entrySet()) {
|
||||
result.put(entry.getKey(), toPojo(entry.getValue(), valueTypeRef));
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T toPojo(Map fromValue, Class<T> toValueType) {
|
||||
return getInstance().convertValue(fromValue, toValueType);
|
||||
}
|
||||
|
||||
// --------------------------------- objectMapper method start -----------------------------------------
|
||||
public static ObjectMapper getInstance() {
|
||||
return JacksonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
public static ObjectMapper getInstance(BladeJacksonProperties properties) {
|
||||
return JacksonPropertyHolder.INSTANCE(properties);
|
||||
}
|
||||
|
||||
private static class JacksonHolder {
|
||||
private static final ObjectMapper INSTANCE = new JacksonObjectMapper();
|
||||
}
|
||||
|
||||
private static class JacksonPropertyHolder {
|
||||
private static ObjectMapper INSTANCE(BladeJacksonProperties properties) {
|
||||
ObjectMapper objectMapper = new JacksonObjectMapper();
|
||||
if (properties != null) {
|
||||
//大数字 转 字符串
|
||||
if (Boolean.TRUE.equals(properties.getBigNumToString())) {
|
||||
objectMapper.registerModules(BladeNumberModule.INSTANCE);
|
||||
}
|
||||
//null 处理
|
||||
if (Boolean.TRUE.equals(properties.getNullToEmpty())) {
|
||||
objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new BladeBeanSerializerModifier()));
|
||||
objectMapper.getSerializerProvider().setNullValueSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
|
||||
}
|
||||
// 敏感词处理
|
||||
objectMapper.registerModule(BladeSensitiveModule.INSTANCE);
|
||||
}
|
||||
objectMapper.findAndRegisterModules();
|
||||
return objectMapper;
|
||||
}
|
||||
}
|
||||
|
||||
private static class JacksonObjectMapper extends ObjectMapper {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 4288193147502386170L;
|
||||
|
||||
private static final Locale CHINA = Locale.CHINA;
|
||||
|
||||
public JacksonObjectMapper(ObjectMapper src) {
|
||||
super(src);
|
||||
}
|
||||
|
||||
public JacksonObjectMapper() {
|
||||
// 通过 Builder 设置 Feature
|
||||
super(JsonMapper.builder()
|
||||
.enable(MapperFeature.DEFAULT_VIEW_INCLUSION) //默认视图包含
|
||||
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS) //允许未转义的控制字符
|
||||
.enable(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER) //允许反斜杠转义任意字符
|
||||
.enable(JsonReadFeature.ALLOW_SINGLE_QUOTES) //允许单引号
|
||||
.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT) //允许空字符串反序列化为null对象
|
||||
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) //日期不序列化为时间戳
|
||||
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) //序列化空对象不报错
|
||||
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) //反序列化遇到未知属性不报错
|
||||
.build());
|
||||
|
||||
//设置地点为中国
|
||||
super.setLocale(CHINA);
|
||||
//设置为中国上海时区
|
||||
super.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
|
||||
//序列化时,日期的统一格式
|
||||
super.setDateFormat(new SimpleDateFormat(DateUtil.PATTERN_DATETIME, Locale.CHINA));
|
||||
//日期格式化
|
||||
super.registerModule(BladeJavaTimeModule.INSTANCE);
|
||||
//视图注解内省器,让 Jackson 识别 @BladeView
|
||||
super.setAnnotationIntrospector(new BladeViewAnnotationIntrospector());
|
||||
//注册模块
|
||||
super.findAndRegisterModules();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectMapper copy() {
|
||||
return new JacksonObjectMapper(this);
|
||||
}
|
||||
}
|
||||
// --------------------------------- objectMapper method end -----------------------------------------
|
||||
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springblade.core.tool.utils.Charsets;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 针对 api 服务对 pc、android、ios 处理的 分读写的 jackson 处理
|
||||
*
|
||||
* <p>
|
||||
* 1. app 端上报数据是 使用 readObjectMapper
|
||||
* 2. 返回给 app 端的数据使用 writeObjectMapper
|
||||
* 3. 如果是返回字符串,直接响应,不做 json 处理
|
||||
* </p>
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class MappingApiJackson2HttpMessageConverter extends AbstractReadWriteJackson2HttpMessageConverter {
|
||||
|
||||
@Nullable
|
||||
private String jsonPrefix;
|
||||
|
||||
public MappingApiJackson2HttpMessageConverter(ObjectMapper objectMapper, BladeJacksonProperties properties) {
|
||||
super(objectMapper, initWriteObjectMapper(objectMapper, properties), initMediaType(properties));
|
||||
}
|
||||
|
||||
private static List<MediaType> initMediaType(BladeJacksonProperties properties) {
|
||||
List<MediaType> supportedMediaTypes = new ArrayList<>();
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
|
||||
supportedMediaTypes.add(new MediaType("application", "*+json"));
|
||||
// 支持 text 文本,用于报文签名
|
||||
if (Boolean.TRUE.equals(properties.getSupportTextPlain())) {
|
||||
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
|
||||
}
|
||||
return supportedMediaTypes;
|
||||
}
|
||||
|
||||
private static ObjectMapper initWriteObjectMapper(ObjectMapper readObjectMapper, BladeJacksonProperties properties) {
|
||||
// 拷贝 readObjectMapper
|
||||
ObjectMapper writeObjectMapper = readObjectMapper.copy();
|
||||
// 敏感词处理
|
||||
writeObjectMapper.registerModule(BladeSensitiveModule.INSTANCE);
|
||||
// 大数字 转 字符串
|
||||
if (Boolean.TRUE.equals(properties.getBigNumToString())) {
|
||||
writeObjectMapper.registerModules(BladeNumberModule.INSTANCE);
|
||||
}
|
||||
// null 处理
|
||||
if (Boolean.TRUE.equals(properties.getNullToEmpty())) {
|
||||
writeObjectMapper.setSerializerFactory(writeObjectMapper.getSerializerFactory().withSerializerModifier(new BladeBeanSerializerModifier()));
|
||||
writeObjectMapper.getSerializerProvider().setNullValueSerializer(BladeBeanSerializerModifier.NullJsonSerializers.STRING_JSON_SERIALIZER);
|
||||
}
|
||||
return writeObjectMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(@NonNull Object object, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
|
||||
// 如果是字符串,直接写出
|
||||
if (object instanceof String) {
|
||||
Charset defaultCharset = this.getDefaultCharset();
|
||||
Charset charset = defaultCharset == null ? Charsets.UTF_8 : defaultCharset;
|
||||
StreamUtils.copy((String) object, charset, outputMessage.getBody());
|
||||
} else {
|
||||
super.writeInternal(object, type, outputMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a custom prefix to use for this view's JSON output.
|
||||
* Default is none.
|
||||
*
|
||||
* @param jsonPrefix jsonPrefix
|
||||
* @see #setPrefixJson
|
||||
*/
|
||||
public void setJsonPrefix(@Nullable String jsonPrefix) {
|
||||
this.jsonPrefix = jsonPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the JSON output by this view should be prefixed with ")]}', ". Default is false.
|
||||
* <p>Prefixing the JSON string in this manner is used to help prevent JSON Hijacking.
|
||||
* The prefix renders the string syntactically invalid as a script so that it cannot be hijacked.
|
||||
* This prefix should be stripped before parsing the string as JSON.
|
||||
*
|
||||
* @param prefixJson prefixJson
|
||||
* @see #setJsonPrefix
|
||||
*/
|
||||
public void setPrefixJson(boolean prefixJson) {
|
||||
this.jsonPrefix = (prefixJson ? ")]}', " : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writePrefix(@NonNull JsonGenerator generator, @NonNull Object object) throws IOException {
|
||||
if (this.jsonPrefix != null) {
|
||||
generator.writeRaw(this.jsonPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotation;
|
||||
import org.springblade.core.tool.sensitive.SensitiveType;
|
||||
import org.springblade.core.tool.sensitive.SensitiveUtil;
|
||||
import org.springblade.core.tool.sensitive.SensitiveWord;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 敏感信息处理注解类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@JacksonAnnotation
|
||||
public @interface Sensitive {
|
||||
// 敏感词类型处理
|
||||
SensitiveType type() default SensitiveType.NONE;
|
||||
|
||||
// 敏感词匹配处理
|
||||
SensitiveWord word() default SensitiveWord.NONE;
|
||||
|
||||
// 自定义敏感词组
|
||||
String[] words() default {};
|
||||
|
||||
// 自定义正则匹配
|
||||
String regex() default StringPool.EMPTY;
|
||||
|
||||
// 自定义替换符
|
||||
String replacement() default SensitiveUtil.DEFAULT_REPLACEMENT;
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
|
||||
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
import org.springblade.core.tool.sensitive.SensitiveType;
|
||||
import org.springblade.core.tool.sensitive.SensitiveUtil;
|
||||
import org.springblade.core.tool.sensitive.SensitiveWord;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 敏感信息序列化
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@JacksonStdImpl
|
||||
public class SensitiveSerializer extends JsonSerializer<String> implements ContextualSerializer {
|
||||
public final static SensitiveSerializer instance = new SensitiveSerializer();
|
||||
|
||||
private Sensitive sensitive;
|
||||
|
||||
@Override
|
||||
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
|
||||
if (StringUtil.isBlank(value)) {
|
||||
gen.writeString(value);
|
||||
return;
|
||||
}
|
||||
if (sensitive == null) {
|
||||
gen.writeString(value);
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据注解配置处理脱敏
|
||||
if (sensitive.type() != SensitiveType.NONE) {
|
||||
// 类型脱敏
|
||||
gen.writeString(SensitiveUtil.process(value, sensitive.type()));
|
||||
} else if (sensitive.word() != SensitiveWord.NONE) {
|
||||
// 敏感词脱敏
|
||||
gen.writeString(SensitiveUtil.processWithWords(value, sensitive.word().getWords(), sensitive.replacement(), true));
|
||||
} else if (sensitive.words().length > 0) {
|
||||
// 自定义敏感词脱敏
|
||||
gen.writeString(SensitiveUtil.processWithWords(value, Arrays.asList(sensitive.words()), sensitive.replacement(), true));
|
||||
} else if (StringUtil.isNotBlank(sensitive.regex())) {
|
||||
// 正则脱敏
|
||||
gen.writeString(SensitiveUtil.processWithRegex(value, sensitive.regex(), sensitive.replacement()));
|
||||
} else {
|
||||
// 默认值
|
||||
gen.writeString(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
|
||||
JsonSerializer<?> valueSerializer = prov.findValueSerializer(String.class);
|
||||
if (property == null) {
|
||||
return valueSerializer;
|
||||
}
|
||||
Sensitive sensitive = property.getAnnotation(Sensitive.class);
|
||||
if (sensitive != null) {
|
||||
SensitiveSerializer serializer = new SensitiveSerializer();
|
||||
serializer.sensitive = sensitive;
|
||||
return serializer;
|
||||
}
|
||||
return valueSerializer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.jackson;
|
||||
|
||||
/**
|
||||
* BladeX 全局视图层级定义
|
||||
* <p>
|
||||
* 四级视图:Summary → Detail → Admin → Administrator
|
||||
* 继承即包含:高级视图自动包含所有低级视图的字段
|
||||
* 未标注 {@link BladeView} 的字段在任何视图下始终输出
|
||||
* </p>
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public final class Views {
|
||||
|
||||
private Views() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 摘要视图 — 列表、下拉、搜索
|
||||
*/
|
||||
public interface Summary {
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情视图 — 详情页、个人中心
|
||||
*/
|
||||
public interface Detail extends Summary {
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理视图 — 普通管理员(admin)
|
||||
*/
|
||||
public interface Admin extends Detail {
|
||||
}
|
||||
|
||||
/**
|
||||
* 超管视图 — 超级管理员(administrator)
|
||||
*/
|
||||
public interface Administrator extends Admin {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 节点基类
|
||||
*
|
||||
* @author smallchill
|
||||
*/
|
||||
@Data
|
||||
public class BaseNode<T> implements INode<T> {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
protected Long id;
|
||||
|
||||
/**
|
||||
* 父节点ID
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
protected Long parentId;
|
||||
|
||||
/**
|
||||
* 子孙节点
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
protected List<T> children = new ArrayList<T>();
|
||||
|
||||
/**
|
||||
* 是否有子孙节点
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private Boolean hasChildren;
|
||||
|
||||
/**
|
||||
* 是否有子孙节点
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
@Override
|
||||
public Boolean getHasChildren() {
|
||||
if (children.size() > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return this.hasChildren;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.io.Serial;
|
||||
|
||||
|
||||
/**
|
||||
* 森林节点类
|
||||
*
|
||||
* @author smallchill
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ForestNode extends BaseNode<ForestNode> {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 节点内容
|
||||
*/
|
||||
private Object content;
|
||||
|
||||
public ForestNode(Long id, Long parentId, Object content) {
|
||||
this.id = id;
|
||||
this.parentId = parentId;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 森林管理类
|
||||
*
|
||||
* @author smallchill
|
||||
*/
|
||||
public class ForestNodeManager<T extends INode<T>> {
|
||||
|
||||
/**
|
||||
* 森林的所有节点
|
||||
*/
|
||||
private final ImmutableMap<Long, T> nodeMap;
|
||||
|
||||
/**
|
||||
* 森林的父节点ID
|
||||
*/
|
||||
private final Map<Long, Object> parentIdMap = Maps.newHashMap();
|
||||
|
||||
public ForestNodeManager(List<T> nodes) {
|
||||
nodeMap = Maps.uniqueIndex(nodes, INode::getId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据节点ID获取一个节点
|
||||
*
|
||||
* @param id 节点ID
|
||||
* @return 对应的节点对象
|
||||
*/
|
||||
public INode<T> getTreeNodeAt(Long id) {
|
||||
if (nodeMap.containsKey(id)) {
|
||||
return nodeMap.get(id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加父节点ID
|
||||
*
|
||||
* @param parentId 父节点ID
|
||||
*/
|
||||
public void addParentId(Long parentId) {
|
||||
parentIdMap.put(parentId, StringPool.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树的根节点(一个森林对应多颗树)
|
||||
*
|
||||
* @return 树的根节点集合
|
||||
*/
|
||||
public List<T> getRoot() {
|
||||
List<T> roots = new ArrayList<>();
|
||||
nodeMap.forEach((key, node) -> {
|
||||
if (node.getParentId() == 0 || parentIdMap.containsKey(node.getId())) {
|
||||
roots.add(node);
|
||||
}
|
||||
});
|
||||
return roots;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 森林节点归并类
|
||||
*
|
||||
* @author smallchill
|
||||
*/
|
||||
public class ForestNodeMerger {
|
||||
|
||||
/**
|
||||
* 将节点数组归并为一个森林(多棵树)(填充节点的children域)
|
||||
* 时间复杂度为O(n^2)
|
||||
*
|
||||
* @param items 节点域
|
||||
* @return 多棵树的根节点集合
|
||||
*/
|
||||
public static <T extends INode<T>> List<T> merge(List<T> items) {
|
||||
ForestNodeManager<T> forestNodeManager = new ForestNodeManager<>(items);
|
||||
items.forEach(forestNode -> {
|
||||
if (forestNode.getParentId() != 0) {
|
||||
INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());
|
||||
if (node != null) {
|
||||
node.getChildren().add(forestNode);
|
||||
} else {
|
||||
forestNodeManager.addParentId(forestNode.getId());
|
||||
}
|
||||
}
|
||||
});
|
||||
return forestNodeManager.getRoot();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Blade.
|
||||
*
|
||||
* @author smallchill
|
||||
*/
|
||||
public interface INode<T> extends Serializable {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*
|
||||
* @return Long
|
||||
*/
|
||||
Long getId();
|
||||
|
||||
/**
|
||||
* 父主键
|
||||
*
|
||||
* @return Long
|
||||
*/
|
||||
Long getParentId();
|
||||
|
||||
/**
|
||||
* 子孙节点
|
||||
*
|
||||
* @return List<T>
|
||||
*/
|
||||
List<T> getChildren();
|
||||
|
||||
/**
|
||||
* 是否有子孙节点
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
default Boolean getHasChildren() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by Blade.
|
||||
*
|
||||
* @author smallchill
|
||||
*/
|
||||
public class NodeTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<ForestNode> list = new ArrayList<>();
|
||||
list.add(new ForestNode(1L, 0L, "1"));
|
||||
list.add(new ForestNode(2L, 0L, "2"));
|
||||
list.add(new ForestNode(3L, 1L, "3"));
|
||||
list.add(new ForestNode(4L, 2L, "4"));
|
||||
list.add(new ForestNode(5L, 3L, "5"));
|
||||
list.add(new ForestNode(6L, 4L, "6"));
|
||||
list.add(new ForestNode(7L, 3L, "7"));
|
||||
list.add(new ForestNode(8L, 5L, "8"));
|
||||
list.add(new ForestNode(9L, 6L, "9"));
|
||||
list.add(new ForestNode(10L, 9L, "10"));
|
||||
List<ForestNode> tns = ForestNodeMerger.merge(list);
|
||||
tns.forEach(node ->
|
||||
System.out.println(JsonUtil.toJson(node))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 树型节点类
|
||||
*
|
||||
* @author smallchill
|
||||
*/
|
||||
@Data
|
||||
public class TreeNode extends BaseNode<TreeNode> {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String title;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long key;
|
||||
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long value;
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
TreeNode other = (TreeNode) obj;
|
||||
return Func.equals(this.getId(), other.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, parentId);
|
||||
}
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.sensitive;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 敏感信息处理配置类
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Builder
|
||||
@Data
|
||||
public class SensitiveConfig {
|
||||
// 启用的内置正则脱敏类型
|
||||
private Set<SensitiveType> sensitiveTypes;
|
||||
|
||||
// 启用的内置敏感词分组
|
||||
private Set<SensitiveWord> sensitiveWords;
|
||||
|
||||
// 自定义敏感词列表
|
||||
private List<String> customSensitiveWords;
|
||||
|
||||
// 自定义正则表达式脱敏规则
|
||||
private Map<String, Pattern> customPatterns;
|
||||
|
||||
// 自定义替换文本(可选,有默认值)
|
||||
private String replacement;
|
||||
|
||||
// 是否按行处理
|
||||
private boolean processLineByLine;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.sensitive;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 脱敏类型枚举.
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public enum SensitiveType {
|
||||
|
||||
// 空敏感类型
|
||||
NONE("无", "", ""),
|
||||
|
||||
// 通用信息类
|
||||
GLOBAL("全局", "(.{2}).*(.{2})", "$1****$2"),
|
||||
KEYS("密钥", "(.{3}).*(.{3})", "$1************$2"),
|
||||
|
||||
// 身份信息类
|
||||
MOBILE("手机号", "(\\d{3})\\d{4}(\\d{4})", "$1****$2"),
|
||||
EMAIL("电子邮箱", "(\\w{2})\\w+(@\\w+\\.\\w+)", "$1****$2"),
|
||||
ID_CARD("身份证号", "(\\d{4})\\d{10}(\\w{4})", "$1**********$2"),
|
||||
PASSPORT("护照号", "([A-Z]{1})\\d{7}", "$1*******"),
|
||||
|
||||
// 金融信息类
|
||||
BANK_CARD("银行卡号", "(\\d{4})\\d+(\\d{4})", "$1****$2"),
|
||||
CREDIT_CARD("信用卡号", "(\\d{4})\\d+(\\d{4})", "$1****$2"),
|
||||
|
||||
// 账户信息类
|
||||
USERNAME("用户名", "(\\w{1})\\w+(\\w{1})", "$1****$2"),
|
||||
IP_ADDRESS("IP地址", "(\\d{1,3}\\.\\d{1,3})\\.\\d{1,3}\\.\\d{1,3}", "$1.***.***"),
|
||||
MAC_ADDRESS("MAC地址", "([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}):[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}", "$1:****"),
|
||||
|
||||
// 地址信息类
|
||||
ADDRESS("详细地址", "(.{3}).*(.{3})", "$1****$2"),
|
||||
GPS("GPS坐标", "(\\d+\\.\\d{2})\\d+,(\\d+\\.\\d{2})\\d+", "$1***,$2***"),
|
||||
;
|
||||
|
||||
private final String replacement;
|
||||
private final Pattern pattern;
|
||||
|
||||
SensitiveType(String ignore, String regex, String replacement) {
|
||||
this.replacement = replacement;
|
||||
this.pattern = Pattern.compile(regex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换文本
|
||||
*
|
||||
* @param content content
|
||||
* @return 替换后的内容
|
||||
*/
|
||||
public String replaceAll(String content) {
|
||||
return this.pattern.matcher(content).replaceAll(this.replacement);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.sensitive;
|
||||
|
||||
import org.springblade.core.tool.utils.CollectionUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 敏感信息处理工具类
|
||||
* <p>
|
||||
* 支持以下功能:
|
||||
* 1. 内置敏感类型处理(手机号、邮箱、身份证等)
|
||||
* 2. 自定义正则表达式处理
|
||||
* 3. 敏感词处理
|
||||
* 4. 支持按行处理或整体处理
|
||||
* 5. 支持自定义替换符
|
||||
* 6. 支持泛型返回值
|
||||
* </p>
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
public class SensitiveUtil {
|
||||
// 默认替换符
|
||||
public static final String DEFAULT_REPLACEMENT = "******";
|
||||
// 换行符
|
||||
private static final String LINE_SEPARATOR = System.lineSeparator();
|
||||
|
||||
// 预编译的默认配置
|
||||
private static final SensitiveConfig DEFAULT_CONFIG = SensitiveConfig.builder()
|
||||
.sensitiveTypes(EnumSet.of(
|
||||
SensitiveType.MOBILE,
|
||||
SensitiveType.ID_CARD,
|
||||
SensitiveType.EMAIL,
|
||||
SensitiveType.BANK_CARD,
|
||||
SensitiveType.CREDIT_CARD,
|
||||
SensitiveType.IP_ADDRESS,
|
||||
SensitiveType.MAC_ADDRESS
|
||||
))
|
||||
.sensitiveWords(EnumSet.of(
|
||||
SensitiveWord.SECURE,
|
||||
SensitiveWord.PAYMENT,
|
||||
SensitiveWord.AUTHENTICATION,
|
||||
SensitiveWord.SESSION
|
||||
))
|
||||
.processLineByLine(true)
|
||||
.replacement(DEFAULT_REPLACEMENT)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 使用默认配置处理敏感信息
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static String process(String content) {
|
||||
return process(content, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用默认配置处理敏感信息
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param supplier 结果转换函数
|
||||
* @param <T> 返回值类型
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static <T> T process(String content, Function<String, T> supplier) {
|
||||
return process(content, DEFAULT_CONFIG, supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自定义配置处理敏感信息
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param config 自定义配置
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static String process(String content, SensitiveConfig config) {
|
||||
return process(content, config, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自定义配置处理敏感信息
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param config 自定义配置
|
||||
* @param supplier 结果转换函数
|
||||
* @param <T> 返回值类型
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static <T> T process(String content, SensitiveConfig config, Function<String, T> supplier) {
|
||||
if (StringUtil.isBlank(content)) {
|
||||
return supplier.apply(content);
|
||||
}
|
||||
|
||||
String processedContent = content;
|
||||
String replacement = StringUtil.isBlank(config.getReplacement()) ?
|
||||
DEFAULT_REPLACEMENT : config.getReplacement();
|
||||
|
||||
// 1. 处理内置敏感类型
|
||||
if (!CollectionUtil.isEmpty(config.getSensitiveTypes())) {
|
||||
processedContent = processRegexPatterns(processedContent, config.getSensitiveTypes());
|
||||
}
|
||||
|
||||
// 2. 处理自定义正则
|
||||
if (!CollectionUtil.isEmpty(config.getCustomPatterns())) {
|
||||
processedContent = processCustomPatterns(processedContent,
|
||||
config.getCustomPatterns(),
|
||||
replacement
|
||||
);
|
||||
}
|
||||
|
||||
// 3. 处理敏感词
|
||||
if (!CollectionUtil.isEmpty(config.getSensitiveWords())) {
|
||||
List<String> words = getSensitiveWords(config.getSensitiveWords());
|
||||
processedContent = processSensitiveWords(processedContent,
|
||||
words,
|
||||
replacement,
|
||||
config.isProcessLineByLine()
|
||||
);
|
||||
}
|
||||
|
||||
return supplier.apply(processedContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个敏感类型
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param type 敏感类型
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static String process(String content, SensitiveType type) {
|
||||
return process(content, type, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个敏感类型
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param type 敏感类型
|
||||
* @param supplier 结果转换函数
|
||||
* @param <T> 返回值类型
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static <T> T process(String content, SensitiveType type, Function<String, T> supplier) {
|
||||
if (StringUtil.isBlank(content) || type == null) {
|
||||
return supplier.apply(content);
|
||||
}
|
||||
String processed = processRegexPatterns(content, Collections.singleton(type));
|
||||
return supplier.apply(processed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理多个敏感类型
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param types 敏感类型集合
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static String process(String content, Set<SensitiveType> types) {
|
||||
return process(content, types, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理多个敏感类型
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param types 敏感类型集合
|
||||
* @param supplier 结果转换函数
|
||||
* @param <T> 返回值类型
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static <T> T process(String content, Set<SensitiveType> types, Function<String, T> supplier) {
|
||||
if (StringUtil.isBlank(content) || CollectionUtil.isEmpty(types)) {
|
||||
return supplier.apply(content);
|
||||
}
|
||||
String processed = processRegexPatterns(content, types);
|
||||
return supplier.apply(processed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自定义正则处理(使用默认替换符)
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param regex 正则表达式
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static String processWithRegex(String content, String regex) {
|
||||
return processWithRegex(content, regex, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自定义正则处理(使用默认替换符)
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param regex 正则表达式
|
||||
* @param supplier 结果转换函数
|
||||
* @param <T> 返回值类型
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static <T> T processWithRegex(String content, String regex, Function<String, T> supplier) {
|
||||
return processWithRegex(content, regex, DEFAULT_REPLACEMENT, supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自定义正则处理(使用自定义替换符)
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param regex 正则表达式
|
||||
* @param replacement 替换内容
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static String processWithRegex(String content, String regex, String replacement) {
|
||||
return processWithRegex(content, regex, replacement, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自定义正则处理(使用自定义替换符)
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param regex 正则表达式
|
||||
* @param replacement 替换内容
|
||||
* @param supplier 结果转换函数
|
||||
* @param <T> 返回值类型
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static <T> T processWithRegex(String content, String regex, String replacement, Function<String, T> supplier) {
|
||||
if (StringUtil.isBlank(content) || StringUtil.isBlank(regex)) {
|
||||
return supplier.apply(content);
|
||||
}
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
String processed = pattern.matcher(content).replaceAll(replacement);
|
||||
return supplier.apply(processed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理敏感词(使用默认配置)
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param words 敏感词列表
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static String processWithWords(String content, List<String> words) {
|
||||
return processWithWords(content, words, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理敏感词(使用默认配置)
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param words 敏感词列表
|
||||
* @param supplier 结果转换函数
|
||||
* @param <T> 返回值类型
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static <T> T processWithWords(String content, List<String> words, Function<String, T> supplier) {
|
||||
return processWithWords(content, words, DEFAULT_REPLACEMENT, true, supplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理敏感词(使用完整参数)
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param words 敏感词列表
|
||||
* @param replacement 替换符
|
||||
* @param processLineByLine 是否按行处理
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static String processWithWords(String content,
|
||||
List<String> words,
|
||||
String replacement,
|
||||
boolean processLineByLine) {
|
||||
return processWithWords(content, words, replacement, processLineByLine, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理敏感词(使用完整参数)
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param words 敏感词列表
|
||||
* @param replacement 替换符
|
||||
* @param processLineByLine 是否按行处理
|
||||
* @param supplier 结果转换函数
|
||||
* @param <T> 返回值类型
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
public static <T> T processWithWords(String content,
|
||||
List<String> words,
|
||||
String replacement,
|
||||
boolean processLineByLine,
|
||||
Function<String, T> supplier) {
|
||||
if (StringUtil.isBlank(content) || CollectionUtil.isEmpty(words)) {
|
||||
return supplier.apply(content);
|
||||
}
|
||||
String processed = processSensitiveWords(content, words, replacement, processLineByLine);
|
||||
return supplier.apply(processed);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理正则表达式
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param types 敏感类型集合
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
private static String processRegexPatterns(String content, Set<SensitiveType> types) {
|
||||
String result = content;
|
||||
for (SensitiveType type : types) {
|
||||
result = type.replaceAll(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自定义正则表达式
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param patterns 自定义正则表达式
|
||||
* @param replacement 替换符
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
private static String processCustomPatterns(String content,
|
||||
Map<String, Pattern> patterns,
|
||||
String replacement) {
|
||||
String result = content;
|
||||
for (Pattern pattern : patterns.values()) {
|
||||
result = pattern.matcher(result).replaceAll(replacement);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取敏感词列表
|
||||
*
|
||||
* @param groups 敏感词分组
|
||||
* @return 敏感词列表
|
||||
*/
|
||||
private static List<String> getSensitiveWords(Set<SensitiveWord> groups) {
|
||||
List<String> words = new ArrayList<>();
|
||||
for (SensitiveWord group : groups) {
|
||||
words.addAll(group.getWords());
|
||||
}
|
||||
return words;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理敏感词
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param words 敏感词列表
|
||||
* @param replacement 替换符
|
||||
* @param processLineByLine 是否按行处理
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
private static String processSensitiveWords(String content,
|
||||
List<String> words,
|
||||
String replacement,
|
||||
boolean processLineByLine) {
|
||||
return processLineByLine ?
|
||||
maskSensitiveLines(content, words, replacement) :
|
||||
maskSensitiveContent(content, words, replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按行处理敏感词
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param words 敏感词列表
|
||||
* @param replacement 替换符
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
private static String maskSensitiveLines(String content,
|
||||
List<String> words,
|
||||
String replacement) {
|
||||
String[] lines = content.split(LINE_SEPARATOR);
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
String line = lines[i];
|
||||
boolean containsSensitive = words.stream()
|
||||
.anyMatch(word -> line.toLowerCase().contains(word.toLowerCase()));
|
||||
|
||||
result.append(containsSensitive ? replacement : line);
|
||||
|
||||
if (i < lines.length - 1) {
|
||||
result.append(LINE_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理整体内容中的敏感词
|
||||
*
|
||||
* @param content 待处理内容
|
||||
* @param words 敏感词列表
|
||||
* @param replacement 替换符
|
||||
* @return 处理后的结果
|
||||
*/
|
||||
private static String maskSensitiveContent(String content,
|
||||
List<String> words,
|
||||
String replacement) {
|
||||
boolean containsSensitive = words.stream()
|
||||
.anyMatch(word -> content.toLowerCase().contains(word.toLowerCase()));
|
||||
|
||||
return containsSensitive ? replacement : content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.sensitive;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 敏感词分组枚举.
|
||||
*
|
||||
* @author BladeX
|
||||
*/
|
||||
@Getter
|
||||
public enum SensitiveWord {
|
||||
|
||||
// 空敏感词组
|
||||
NONE(List.of()),
|
||||
|
||||
// 安全敏感词组
|
||||
SECURE(Arrays.asList(
|
||||
// 认证信息类
|
||||
"password", "pwd", "token", "secret", "bearer", "key",
|
||||
// API相关
|
||||
"api_key", "access_token", "refresh_token", "auth_token",
|
||||
// 加密相关
|
||||
"private_key", "public_key", "salt", "hash",
|
||||
// 安全相关
|
||||
"security", "certificate", "credentials",
|
||||
// 数据库相关
|
||||
"connection_string", "jdbc", "sql", "database_url"
|
||||
)),
|
||||
|
||||
// 支付相关敏感词
|
||||
PAYMENT(Arrays.asList(
|
||||
"cvv", "card_number", "expiry", "pin", "payment_token"
|
||||
)),
|
||||
|
||||
// 身份验证相关敏感词
|
||||
AUTHENTICATION(Arrays.asList(
|
||||
"otp", "verification_code", "auth_code", "mfa_token"
|
||||
)),
|
||||
|
||||
// 会话相关敏感词
|
||||
SESSION(Arrays.asList(
|
||||
"session_id", "cookie", "jwt_token", "bearer_token"
|
||||
));
|
||||
|
||||
private final List<String> words;
|
||||
|
||||
SensitiveWord(List<String> words) {
|
||||
this.words = Collections.unmodifiableList(words);
|
||||
}
|
||||
}
|
||||
+120
@@ -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();
|
||||
}
|
||||
}
|
||||
+51
@@ -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;
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.ssl;
|
||||
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* 不进行证书校验
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DisableValidationTrustManager implements X509TrustManager {
|
||||
|
||||
public static final X509TrustManager INSTANCE = new DisableValidationTrustManager();
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.ssl;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
/**
|
||||
* 信任所有 host name
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class TrustAllHostNames implements HostnameVerifier {
|
||||
public static final TrustAllHostNames INSTANCE = new TrustAllHostNames();
|
||||
|
||||
@Override
|
||||
public boolean verify(String s, SSLSession sslSession) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springblade.core.tool.support;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 跟踪类变动比较
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@Getter
|
||||
@ToString
|
||||
public class BeanDiff {
|
||||
/**
|
||||
* 变更字段
|
||||
*/
|
||||
private final Set<String> fields = new HashSet<>();
|
||||
/**
|
||||
* 旧值
|
||||
*/
|
||||
private final Map<String, Object> oldValues = new HashMap<>();
|
||||
/**
|
||||
* 新值
|
||||
*/
|
||||
private final Map<String, Object> newValues = new HashMap<>();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.support;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 解决 no binder available 问题
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class BinderSupplier implements Supplier<Object> {
|
||||
|
||||
@Override
|
||||
public Object get() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.support;
|
||||
|
||||
/**
|
||||
* Created by Blade.
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class CoreMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("init core module");
|
||||
}
|
||||
|
||||
}
|
||||
+264
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.support;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* A factory for creating MultiOutputStream objects.
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IMultiOutputStream {
|
||||
|
||||
/**
|
||||
* Builds the output stream.
|
||||
*
|
||||
* @param params the params
|
||||
* @return the output stream
|
||||
*/
|
||||
OutputStream buildOutputStream(Integer... params);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.support;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.Enumeration;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* 轻量级雪花ID生成器
|
||||
* <p>
|
||||
* 64位结构: 1位符号(0) | 41位时间差值 | 10位机器标识 | 12位毫秒内序列
|
||||
* <p>
|
||||
* 设计要点:
|
||||
* <ul>
|
||||
* <li>时间纪元 2018-01-01 UTC,可使用至 2087 年</li>
|
||||
* <li>机器标识基于网卡MAC地址与进程PID混合哈希,容器/集群环境下碰撞率极低</li>
|
||||
* <li>内置时钟回拨容忍机制:2秒内自旋等待,超出则抛出异常拒绝生成</li>
|
||||
* <li>适用于 JdbcTemplate 直接入库场景,无需引入外部 IdWorker 依赖</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public final class IdGenerator {
|
||||
|
||||
// ======================== 位分配常量 ========================
|
||||
|
||||
/** 机器标识占位 */
|
||||
private static final int MACHINE_BITS = 10;
|
||||
|
||||
/** 毫秒内序列占位 */
|
||||
private static final int SEQUENCE_BITS = 12;
|
||||
|
||||
/** 机器标识左移位数 */
|
||||
private static final int MACHINE_SHIFT = SEQUENCE_BITS;
|
||||
|
||||
/** 时间戳左移位数 */
|
||||
private static final int TIMESTAMP_SHIFT = MACHINE_BITS + SEQUENCE_BITS;
|
||||
|
||||
/** 机器标识上限掩码(1023) */
|
||||
private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_BITS);
|
||||
|
||||
/** 序列号上限掩码(4095) */
|
||||
private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
|
||||
|
||||
// ======================== 时钟常量 ========================
|
||||
|
||||
/** 起始纪元:2018-01-01 00:00:00 UTC */
|
||||
private static final long EPOCH = 1514764800000L;
|
||||
|
||||
/** 时钟回拨容忍上限(毫秒) */
|
||||
private static final long MAX_BACKWARD_MS = 2000L;
|
||||
|
||||
// ======================== 运行时状态 ========================
|
||||
|
||||
/** 当前机器标识(启动时初始化,不可变) */
|
||||
private static final long MACHINE_ID = resolveMachineId();
|
||||
|
||||
/** 上一次生成ID的时间戳 */
|
||||
private static long lastTimestamp = -1L;
|
||||
|
||||
/** 当前毫秒内序列号 */
|
||||
private static long sequence = 0L;
|
||||
|
||||
private IdGenerator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成全局唯一的雪花ID
|
||||
*
|
||||
* @return 64位 long 型 ID
|
||||
* @throws IllegalStateException 时钟回拨超出容忍阈值时抛出
|
||||
*/
|
||||
public static synchronized long getId() {
|
||||
long now = System.currentTimeMillis();
|
||||
// 时钟回拨保护
|
||||
if (now < lastTimestamp) {
|
||||
long offset = lastTimestamp - now;
|
||||
if (offset <= MAX_BACKWARD_MS) {
|
||||
// 回拨幅度在容忍范围内,自旋等待追上
|
||||
now = awaitNextMillis(lastTimestamp);
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"时钟回拨超出容忍阈值: 回拨=" + offset + "ms, 上限=" + MAX_BACKWARD_MS + "ms"
|
||||
);
|
||||
}
|
||||
}
|
||||
// 同一毫秒内递增序列号
|
||||
if (now == lastTimestamp) {
|
||||
sequence = (sequence + 1) & MAX_SEQUENCE;
|
||||
if (sequence == 0) {
|
||||
// 当前毫秒序列号耗尽,等待下一毫秒
|
||||
now = awaitNextMillis(lastTimestamp);
|
||||
}
|
||||
} else {
|
||||
// 新毫秒:随机起始序列,避免低位连续为0造成分库分表不均匀
|
||||
sequence = ThreadLocalRandom.current().nextLong(4);
|
||||
}
|
||||
lastTimestamp = now;
|
||||
return ((now - EPOCH) << TIMESTAMP_SHIFT)
|
||||
| (MACHINE_ID << MACHINE_SHIFT)
|
||||
| sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自旋等待直到时间戳超过给定值
|
||||
*
|
||||
* @param target 目标时间戳
|
||||
* @return 超过目标的当前时间戳
|
||||
*/
|
||||
private static long awaitNextMillis(long target) {
|
||||
long now;
|
||||
do {
|
||||
Thread.onSpinWait();
|
||||
now = System.currentTimeMillis();
|
||||
} while (now <= target);
|
||||
return now;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析机器标识
|
||||
* <p>
|
||||
* 优先使用网卡MAC地址与进程PID混合哈希,确保在容器/集群环境下具有良好的分散性。
|
||||
* 若MAC地址不可用,降级为主机名与PID的混合哈希;若仍失败,使用安全随机数兜底。
|
||||
*
|
||||
* @return 10位机器标识(0~1023)
|
||||
*/
|
||||
private static long resolveMachineId() {
|
||||
try {
|
||||
long pid = ProcessHandle.current().pid();
|
||||
byte[] mac = findFirstMacAddress();
|
||||
if (mac != null) {
|
||||
// MAC(6字节) + PID 混合哈希
|
||||
long hash = 0L;
|
||||
for (byte macByte : mac) {
|
||||
hash = hash * 31 + (macByte & 0xFF);
|
||||
}
|
||||
return (hash ^ pid) & MAX_MACHINE_ID;
|
||||
}
|
||||
// MAC不可用,降级为主机名 + PID
|
||||
String hostName = InetAddress.getLocalHost().getHostName();
|
||||
return (hostName.hashCode() ^ pid) & MAX_MACHINE_ID;
|
||||
} catch (Exception ignored) {
|
||||
return ThreadLocalRandom.current().nextLong(MAX_MACHINE_ID + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找首个可用的物理网卡MAC地址
|
||||
*
|
||||
* @return MAC地址字节数组,不可用时返回 null
|
||||
*/
|
||||
private static byte[] findFirstMacAddress() {
|
||||
try {
|
||||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||
while (interfaces.hasMoreElements()) {
|
||||
NetworkInterface ni = interfaces.nextElement();
|
||||
if (ni.isLoopback() || ni.isVirtual() || !ni.isUp()) {
|
||||
continue;
|
||||
}
|
||||
byte[] mac = ni.getHardwareAddress();
|
||||
if (mac != null && mac.length == 6) {
|
||||
return mac;
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.support;
|
||||
|
||||
/**
|
||||
* 图片操作类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class ImagePosition {
|
||||
|
||||
/**
|
||||
* 图片顶部.
|
||||
*/
|
||||
public static final int TOP = 32;
|
||||
|
||||
/**
|
||||
* 图片中部.
|
||||
*/
|
||||
public static final int MIDDLE = 16;
|
||||
|
||||
/**
|
||||
* 图片底部.
|
||||
*/
|
||||
public static final int BOTTOM = 8;
|
||||
|
||||
/**
|
||||
* 图片左侧.
|
||||
*/
|
||||
public static final int LEFT = 4;
|
||||
|
||||
/**
|
||||
* 图片居中.
|
||||
*/
|
||||
public static final int CENTER = 2;
|
||||
|
||||
/**
|
||||
* 图片右侧.
|
||||
*/
|
||||
public static final int RIGHT = 1;
|
||||
|
||||
/**
|
||||
* 横向边距,靠左或靠右时和边界的距离.
|
||||
*/
|
||||
private static final int PADDING_HORI = 6;
|
||||
|
||||
/**
|
||||
* 纵向边距,靠上或靠底时和边界的距离.
|
||||
*/
|
||||
private static final int PADDING_VERT = 6;
|
||||
|
||||
|
||||
/**
|
||||
* 图片中盒[左上角]的x坐标.
|
||||
*/
|
||||
private int boxPosX;
|
||||
|
||||
/**
|
||||
* 图片中盒[左上角]的y坐标.
|
||||
*/
|
||||
private int boxPosY;
|
||||
|
||||
/**
|
||||
* Instantiates a new image position.
|
||||
*
|
||||
* @param width the width
|
||||
* @param height the height
|
||||
* @param boxWidth the box width
|
||||
* @param boxHeight the box height
|
||||
* @param style the style
|
||||
*/
|
||||
public ImagePosition(int width, int height, int boxWidth, int boxHeight, int style) {
|
||||
switch (style & 7) {
|
||||
case LEFT:
|
||||
boxPosX = PADDING_HORI;
|
||||
break;
|
||||
case RIGHT:
|
||||
boxPosX = width - boxWidth - PADDING_HORI;
|
||||
break;
|
||||
case CENTER:
|
||||
default:
|
||||
boxPosX = (width - boxWidth) / 2;
|
||||
}
|
||||
switch (style >> 3 << 3) {
|
||||
case TOP:
|
||||
boxPosY = PADDING_VERT;
|
||||
break;
|
||||
case MIDDLE:
|
||||
boxPosY = (height - boxHeight) / 2;
|
||||
break;
|
||||
case BOTTOM:
|
||||
default:
|
||||
boxPosY = height - boxHeight - PADDING_VERT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the x.
|
||||
*
|
||||
* @return the x
|
||||
*/
|
||||
public int getX() {
|
||||
return getX(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the x.
|
||||
*
|
||||
* @param x 横向偏移
|
||||
* @return the x
|
||||
*/
|
||||
public int getX(int x) {
|
||||
return this.boxPosX + x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y.
|
||||
*
|
||||
* @return the y
|
||||
*/
|
||||
public int getY() {
|
||||
return getY(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the y.
|
||||
*
|
||||
* @param y 纵向偏移
|
||||
* @return the y
|
||||
*/
|
||||
public int getY(int y) {
|
||||
return this.boxPosY + y;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.support;
|
||||
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
|
||||
import java.sql.Time;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 链式map
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class Kv extends LinkedCaseInsensitiveMap<Object> {
|
||||
|
||||
private Kv() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Kv
|
||||
*
|
||||
* @return Kv
|
||||
*/
|
||||
public static Kv init() {
|
||||
return new Kv();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Kv
|
||||
*
|
||||
* @return Kv
|
||||
*/
|
||||
public static Kv create() {
|
||||
return new Kv();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Kv
|
||||
*
|
||||
* @param value 初始化数据
|
||||
* @return Kv
|
||||
*/
|
||||
public static Kv create(Kv value) {
|
||||
Kv kv = new Kv();
|
||||
kv.putAll(value);
|
||||
return kv;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Map创建Kv
|
||||
*
|
||||
* @param map 初始化数据
|
||||
* @return Kv
|
||||
*/
|
||||
public static Kv create(Map<?, ?> map) {
|
||||
Kv kv = new Kv();
|
||||
if (map != null) {
|
||||
map.forEach((key, value) -> kv.set(String.valueOf(key), value));
|
||||
}
|
||||
return kv;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从对象创建Kv
|
||||
*
|
||||
* @param value 源对象
|
||||
* @return Kv
|
||||
*/
|
||||
public static Kv create(Object value) {
|
||||
Kv kv = new Kv();
|
||||
if (value != null) {
|
||||
if (value instanceof Map) {
|
||||
return create((Map<?, ?>) value);
|
||||
} else {
|
||||
kv.set("object", value);
|
||||
}
|
||||
}
|
||||
return kv;
|
||||
}
|
||||
|
||||
public static <K, V> HashMap<K, V> newMap() {
|
||||
return new HashMap<>(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置列
|
||||
*
|
||||
* @param attr 属性
|
||||
* @param value 值
|
||||
* @return 本身
|
||||
*/
|
||||
public Kv set(String attr, Object value) {
|
||||
this.put(attr, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置全部
|
||||
*
|
||||
* @param map 属性
|
||||
* @return 本身
|
||||
*/
|
||||
public Kv setAll(Map<? extends String, ?> map) {
|
||||
if (map != null) {
|
||||
this.putAll(map);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置列,当键或值为null时忽略
|
||||
*
|
||||
* @param attr 属性
|
||||
* @param value 值
|
||||
* @return 本身
|
||||
*/
|
||||
public Kv setIgnoreNull(String attr, Object value) {
|
||||
if (attr != null && value != null) {
|
||||
set(attr, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object getObj(String key) {
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param <T> 值类型
|
||||
* @param attr 字段名
|
||||
* @param defaultValue 默认值
|
||||
* @return 字段值
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String attr, T defaultValue) {
|
||||
final Object result = get(attr);
|
||||
return (T) (result != null ? result : defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public String getStr(String attr) {
|
||||
return Func.toStr(get(attr), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public Integer getInt(String attr) {
|
||||
return Func.toInt(get(attr), -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public Long getLong(String attr) {
|
||||
return Func.toLong(get(attr), -1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public Float getFloat(String attr) {
|
||||
return Func.toFloat(get(attr), null);
|
||||
}
|
||||
|
||||
public Double getDouble(String attr) {
|
||||
return Func.toDouble(get(attr), null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public Boolean getBool(String attr) {
|
||||
return Func.toBoolean(get(attr), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public byte[] getBytes(String attr) {
|
||||
return get(attr, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public Date getDate(String attr) {
|
||||
return get(attr, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public Time getTime(String attr) {
|
||||
return get(attr, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public Timestamp getTimestamp(String attr) {
|
||||
return get(attr, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得特定类型值
|
||||
*
|
||||
* @param attr 字段名
|
||||
* @return 字段值
|
||||
*/
|
||||
public Number getNumber(String attr) {
|
||||
return get(attr, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Kv clone() {
|
||||
Kv clone = new Kv();
|
||||
clone.putAll(this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,501 @@
|
||||
package org.springblade.core.tool.support;
|
||||
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 字符串切分器
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class StrSpliter {
|
||||
|
||||
//---------------------------------------------------------------------------------------------- Split by char
|
||||
|
||||
/**
|
||||
* 切分字符串路径,仅支持Unix分界符:/
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static List<String> splitPath(String str) {
|
||||
return splitPath(str, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串路径,仅支持Unix分界符:/
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static String[] splitPathToArray(String str) {
|
||||
return toArray(splitPath(str));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串路径,仅支持Unix分界符:/
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param limit 限制分片数
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static List<String> splitPath(String str, int limit) {
|
||||
return split(str, StringPool.SLASH, limit, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串路径,仅支持Unix分界符:/
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param limit 限制分片数
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static String[] splitPathToArray(String str, int limit) {
|
||||
return toArray(splitPath(str, limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static List<String> splitTrim(String str, char separator, boolean ignoreEmpty) {
|
||||
return split(str, separator, 0, true, ignoreEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static List<String> split(String str, char separator, boolean isTrim, boolean ignoreEmpty) {
|
||||
return split(str, separator, 0, isTrim, ignoreEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串,大小写敏感,去除每个元素两边空白符
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符
|
||||
* @param limit 限制分片数,-1不限制
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static List<String> splitTrim(String str, char separator, int limit, boolean ignoreEmpty) {
|
||||
return split(str, separator, limit, true, ignoreEmpty, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串,大小写敏感
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符
|
||||
* @param limit 限制分片数,-1不限制
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static List<String> split(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) {
|
||||
return split(str, separator, limit, isTrim, ignoreEmpty, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串,忽略大小写
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符
|
||||
* @param limit 限制分片数,-1不限制
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static List<String> splitIgnoreCase(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) {
|
||||
return split(str, separator, limit, isTrim, ignoreEmpty, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符
|
||||
* @param limit 限制分片数,-1不限制
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @param ignoreCase 是否忽略大小写
|
||||
* @return 切分后的集合
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static List<String> split(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase) {
|
||||
if (StringUtil.isEmpty(str)) {
|
||||
return new ArrayList<String>(0);
|
||||
}
|
||||
if (limit == 1) {
|
||||
return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty);
|
||||
}
|
||||
|
||||
final ArrayList<String> list = new ArrayList<>(limit > 0 ? limit : 16);
|
||||
int len = str.length();
|
||||
int start = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (Func.equals(separator, str.charAt(i))) {
|
||||
addToList(list, str.substring(start, i), isTrim, ignoreEmpty);
|
||||
start = i + 1;
|
||||
|
||||
//检查是否超出范围(最大允许limit-1个,剩下一个留给末尾字符串)
|
||||
if (limit > 0 && list.size() > limit - 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return addToList(list, str.substring(start, len), isTrim, ignoreEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串为字符串数组
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符
|
||||
* @param limit 限制分片数
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static String[] splitToArray(String str, char separator, int limit, boolean isTrim, boolean ignoreEmpty) {
|
||||
return toArray(split(str, separator, limit, isTrim, ignoreEmpty));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------- Split by String
|
||||
|
||||
/**
|
||||
* 切分字符串,不忽略大小写
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符串
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static List<String> split(String str, String separator, boolean isTrim, boolean ignoreEmpty) {
|
||||
return split(str, separator, -1, isTrim, ignoreEmpty, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串,去除每个元素两边空格,忽略大小写
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符串
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static List<String> splitTrim(String str, String separator, boolean ignoreEmpty) {
|
||||
return split(str, separator, true, ignoreEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串,不忽略大小写
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符串
|
||||
* @param limit 限制分片数
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static List<String> split(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) {
|
||||
return split(str, separator, limit, isTrim, ignoreEmpty, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串,去除每个元素两边空格,忽略大小写
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符串
|
||||
* @param limit 限制分片数
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static List<String> splitTrim(String str, String separator, int limit, boolean ignoreEmpty) {
|
||||
return split(str, separator, limit, true, ignoreEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串,忽略大小写
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符串
|
||||
* @param limit 限制分片数
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static List<String> splitIgnoreCase(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) {
|
||||
return split(str, separator, limit, isTrim, ignoreEmpty, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串,去除每个元素两边空格,忽略大小写
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符串
|
||||
* @param limit 限制分片数
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static List<String> splitTrimIgnoreCase(String str, String separator, int limit, boolean ignoreEmpty) {
|
||||
return split(str, separator, limit, true, ignoreEmpty, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符串
|
||||
* @param limit 限制分片数
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @param ignoreCase 是否忽略大小写
|
||||
* @return 切分后的集合
|
||||
* @since 3.2.1
|
||||
*/
|
||||
public static List<String> split(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty, boolean ignoreCase) {
|
||||
if (StringUtil.isEmpty(str)) {
|
||||
return new ArrayList<String>(0);
|
||||
}
|
||||
if (limit == 1) {
|
||||
return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty);
|
||||
}
|
||||
|
||||
if (StringUtil.isEmpty(separator)) {
|
||||
return split(str, limit);
|
||||
} else if (separator.length() == 1) {
|
||||
return split(str, separator.charAt(0), limit, isTrim, ignoreEmpty, ignoreCase);
|
||||
}
|
||||
|
||||
final ArrayList<String> list = new ArrayList<>();
|
||||
int len = str.length();
|
||||
int separatorLen = separator.length();
|
||||
int start = 0;
|
||||
int i = 0;
|
||||
while (i < len) {
|
||||
i = StringUtil.indexOf(str, separator, start, ignoreCase);
|
||||
if (i > -1) {
|
||||
addToList(list, str.substring(start, i), isTrim, ignoreEmpty);
|
||||
start = i + separatorLen;
|
||||
|
||||
//检查是否超出范围(最大允许limit-1个,剩下一个留给末尾字符串)
|
||||
if (limit > 0 && list.size() > limit - 2) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return addToList(list, str.substring(start, len), isTrim, ignoreEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串为字符串数组
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符字符
|
||||
* @param limit 限制分片数
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static String[] splitToArray(String str, String separator, int limit, boolean isTrim, boolean ignoreEmpty) {
|
||||
return toArray(split(str, separator, limit, isTrim, ignoreEmpty));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------- Split by Whitespace
|
||||
|
||||
/**
|
||||
* 使用空白符切分字符串<br>
|
||||
* 切分后的字符串两边不包含空白符,空串或空白符串并不做为元素之一
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param limit 限制分片数
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static List<String> split(String str, int limit) {
|
||||
if (StringUtil.isEmpty(str)) {
|
||||
return new ArrayList<String>(0);
|
||||
}
|
||||
if (limit == 1) {
|
||||
return addToList(new ArrayList<String>(1), str, true, true);
|
||||
}
|
||||
|
||||
final ArrayList<String> list = new ArrayList<>();
|
||||
int len = str.length();
|
||||
int start = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (Func.isEmpty(str.charAt(i))) {
|
||||
addToList(list, str.substring(start, i), true, true);
|
||||
start = i + 1;
|
||||
if (limit > 0 && list.size() > limit - 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return addToList(list, str.substring(start, len), true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串为字符串数组
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param limit 限制分片数
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static String[] splitToArray(String str, int limit) {
|
||||
return toArray(split(str, limit));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------- Split by regex
|
||||
|
||||
/**
|
||||
* 通过正则切分字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param separatorPattern 分隔符正则{@link Pattern}
|
||||
* @param limit 限制分片数
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static List<String> split(String str, Pattern separatorPattern, int limit, boolean isTrim, boolean ignoreEmpty) {
|
||||
if (StringUtil.isEmpty(str)) {
|
||||
return new ArrayList<String>(0);
|
||||
}
|
||||
if (limit == 1) {
|
||||
return addToList(new ArrayList<String>(1), str, isTrim, ignoreEmpty);
|
||||
}
|
||||
|
||||
if (null == separatorPattern) {
|
||||
return split(str, limit);
|
||||
}
|
||||
|
||||
final Matcher matcher = separatorPattern.matcher(str);
|
||||
final ArrayList<String> list = new ArrayList<>();
|
||||
int len = str.length();
|
||||
int start = 0;
|
||||
while (matcher.find()) {
|
||||
addToList(list, str.substring(start, matcher.start()), isTrim, ignoreEmpty);
|
||||
start = matcher.end();
|
||||
|
||||
if (limit > 0 && list.size() > limit - 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return addToList(list, str.substring(start, len), isTrim, ignoreEmpty);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过正则切分字符串为字符串数组
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separatorPattern 分隔符正则{@link Pattern}
|
||||
* @param limit 限制分片数
|
||||
* @param isTrim 是否去除切分字符串后每个元素两边的空格
|
||||
* @param ignoreEmpty 是否忽略空串
|
||||
* @return 切分后的集合
|
||||
* @since 3.0.8
|
||||
*/
|
||||
public static String[] splitToArray(String str, Pattern separatorPattern, int limit, boolean isTrim, boolean ignoreEmpty) {
|
||||
return toArray(split(str, separatorPattern, limit, isTrim, ignoreEmpty));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------- Split by length
|
||||
|
||||
/**
|
||||
* 根据给定长度,将给定字符串截取为多个部分
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param len 每一个小节的长度
|
||||
* @return 截取后的字符串数组
|
||||
*/
|
||||
public static String[] splitByLength(String str, int len) {
|
||||
int partCount = str.length() / len;
|
||||
int lastPartCount = str.length() % len;
|
||||
int fixPart = 0;
|
||||
if (lastPartCount != 0) {
|
||||
fixPart = 1;
|
||||
}
|
||||
|
||||
final String[] strs = new String[partCount + fixPart];
|
||||
for (int i = 0; i < partCount + fixPart; i++) {
|
||||
if (i == partCount + fixPart - 1 && lastPartCount != 0) {
|
||||
strs[i] = str.substring(i * len, i * len + lastPartCount);
|
||||
} else {
|
||||
strs[i] = str.substring(i * len, i * len + len);
|
||||
}
|
||||
}
|
||||
return strs;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------------- Private method start
|
||||
|
||||
/**
|
||||
* 将字符串加入List中
|
||||
*
|
||||
* @param list 列表
|
||||
* @param part 被加入的部分
|
||||
* @param isTrim 是否去除两端空白符
|
||||
* @param ignoreEmpty 是否略过空字符串(空字符串不做为一个元素)
|
||||
* @return 列表
|
||||
*/
|
||||
private static List<String> addToList(List<String> list, String part, boolean isTrim, boolean ignoreEmpty) {
|
||||
part = part.toString();
|
||||
if (isTrim) {
|
||||
part = part.trim();
|
||||
}
|
||||
if (false == ignoreEmpty || false == part.isEmpty()) {
|
||||
list.add(part);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* List转Array
|
||||
*
|
||||
* @param list List
|
||||
* @return Array
|
||||
*/
|
||||
private static String[] toArray(List<String> list) {
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
//---------------------------------------------------------------------------------------------------------- Private method end
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package org.springblade.core.tool.support;
|
||||
|
||||
import org.springblade.core.tool.utils.Exceptions;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Lambda 受检异常处理
|
||||
* https://segmentfault.com/a/1190000007832130
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class Try {
|
||||
|
||||
public static <T, R> Function<T, R> of(UncheckedFunction<T, R> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return t -> {
|
||||
try {
|
||||
return mapper.apply(t);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Consumer<T> of(UncheckedConsumer<T> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return t -> {
|
||||
try {
|
||||
mapper.accept(t);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> Supplier<T> of(UncheckedSupplier<T> mapper) {
|
||||
Objects.requireNonNull(mapper);
|
||||
return () -> {
|
||||
try {
|
||||
return mapper.get();
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface UncheckedFunction<T, R> {
|
||||
/**
|
||||
* apply
|
||||
*
|
||||
* @param t
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Nullable
|
||||
R apply(@Nullable T t) throws Exception;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface UncheckedConsumer<T> {
|
||||
/**
|
||||
* accept
|
||||
*
|
||||
* @param t
|
||||
* @throws Exception
|
||||
*/
|
||||
@Nullable
|
||||
void accept(@Nullable T t) throws Exception;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface UncheckedSupplier<T> {
|
||||
/**
|
||||
* get
|
||||
*
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Nullable
|
||||
T get() throws Exception;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.tuple;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.tool.utils.RsaUtil;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* rsa 的 key pair 封装
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class KeyPair {
|
||||
private final java.security.KeyPair keyPair;
|
||||
|
||||
public PublicKey getPublic() {
|
||||
return keyPair.getPublic();
|
||||
}
|
||||
|
||||
public PrivateKey getPrivate() {
|
||||
return keyPair.getPrivate();
|
||||
}
|
||||
|
||||
public byte[] getPublicBytes() {
|
||||
return this.getPublic().getEncoded();
|
||||
}
|
||||
|
||||
public byte[] getPrivateBytes() {
|
||||
return this.getPrivate().getEncoded();
|
||||
}
|
||||
|
||||
public String getPublicBase64() {
|
||||
return RsaUtil.getKeyString(this.getPublic());
|
||||
}
|
||||
|
||||
public String getPrivateBase64() {
|
||||
return RsaUtil.getKeyString(this.getPrivate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PublicKey=" + this.getPublicBase64() + '\n' + "PrivateKey=" + this.getPrivateBase64();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.tuple;
|
||||
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* tuple Pair
|
||||
*
|
||||
* @author L.cm
|
||||
**/
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class Pair<L, R> {
|
||||
private static final Pair<Object, Object> EMPTY = new Pair<>(null, null);
|
||||
|
||||
private final L left;
|
||||
private final R right;
|
||||
|
||||
/**
|
||||
* Returns an empty pair.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <L, R> Pair<L, R> empty() {
|
||||
return (Pair<L, R>) EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a pair with its left value being {@code left}, or returns an empty pair if
|
||||
* {@code left} is null.
|
||||
*
|
||||
* @return the constructed pair or an empty pair if {@code left} is null.
|
||||
*/
|
||||
public static <L, R> Pair<L, R> createLeft(L left) {
|
||||
if (left == null) {
|
||||
return empty();
|
||||
} else {
|
||||
return new Pair<>(left, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a pair with its right value being {@code right}, or returns an empty pair if
|
||||
* {@code right} is null.
|
||||
*
|
||||
* @return the constructed pair or an empty pair if {@code right} is null.
|
||||
*/
|
||||
public static <L, R> Pair<L, R> createRight(R right) {
|
||||
if (right == null) {
|
||||
return empty();
|
||||
} else {
|
||||
return new Pair<>(null, right);
|
||||
}
|
||||
}
|
||||
|
||||
public static <L, R> Pair<L, R> create(L left, R right) {
|
||||
if (right == null && left == null) {
|
||||
return empty();
|
||||
} else {
|
||||
return new Pair<>(left, right);
|
||||
}
|
||||
}
|
||||
|
||||
private Pair(L left, R right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 完全兼容微信所使用的AES加密工具类
|
||||
* aes的key必须是256byte长(比如32个字符),可以使用AesKit.genAesKey()来生成一组key
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class AesUtil {
|
||||
public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
|
||||
|
||||
public static String genAesKey() {
|
||||
return StringUtil.random(32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成mysql aes
|
||||
*
|
||||
* @param key key
|
||||
* @return SecretKeySpec
|
||||
*/
|
||||
public static SecretKeySpec genMySqlAesKey(final byte[] key) {
|
||||
final byte[] finalKey = new byte[16];
|
||||
int i = 0;
|
||||
for (byte b : key) {
|
||||
finalKey[i++ % 16] ^= b;
|
||||
}
|
||||
return new SecretKeySpec(finalKey, "AES");
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成mysql aes
|
||||
*
|
||||
* @param key key
|
||||
* @return SecretKeySpec
|
||||
*/
|
||||
public static SecretKeySpec genMySqlAesKey(final String key) {
|
||||
return genMySqlAesKey(key.getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
public static String encryptToHex(String content, String aesTextKey) {
|
||||
return HexUtil.encodeToString(encrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
public static String encryptToHex(byte[] content, String aesTextKey) {
|
||||
return HexUtil.encodeToString(encrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
public static String encryptToBase64(String content, String aesTextKey) {
|
||||
return Base64Util.encodeToString(encrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
public static String encryptToBase64(byte[] content, String aesTextKey) {
|
||||
return Base64Util.encodeToString(encrypt(content, aesTextKey));
|
||||
}
|
||||
|
||||
public static byte[] encrypt(String content, String aesTextKey) {
|
||||
return encrypt(content.getBytes(DEFAULT_CHARSET), aesTextKey);
|
||||
}
|
||||
|
||||
public static byte[] encrypt(String content, Charset charset, String aesTextKey) {
|
||||
return encrypt(content.getBytes(charset), aesTextKey);
|
||||
}
|
||||
|
||||
public static byte[] encrypt(byte[] content, String aesTextKey) {
|
||||
return encrypt(content, Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String decryptFormHexToString(@Nullable String content, String aesTextKey) {
|
||||
byte[] hexBytes = decryptFormHex(content, aesTextKey);
|
||||
if (hexBytes == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(hexBytes, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static byte[] decryptFormHex(@Nullable String content, String aesTextKey) {
|
||||
if (StringUtil.isBlank(content)) {
|
||||
return null;
|
||||
}
|
||||
return decryptFormHex(content.getBytes(DEFAULT_CHARSET), aesTextKey);
|
||||
}
|
||||
|
||||
public static byte[] decryptFormHex(byte[] content, String aesTextKey) {
|
||||
return decrypt(HexUtil.decode(content), aesTextKey);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String decryptFormBase64ToString(@Nullable String content, String aesTextKey) {
|
||||
byte[] hexBytes = decryptFormBase64(content, aesTextKey);
|
||||
if (hexBytes == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(hexBytes, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static byte[] decryptFormBase64(@Nullable String content, String aesTextKey) {
|
||||
if (StringUtil.isBlank(content)) {
|
||||
return null;
|
||||
}
|
||||
return decryptFormBase64(content.getBytes(DEFAULT_CHARSET), aesTextKey);
|
||||
}
|
||||
|
||||
public static byte[] decryptFormBase64(byte[] content, String aesTextKey) {
|
||||
return decrypt(Base64Util.decode(content), aesTextKey);
|
||||
}
|
||||
|
||||
public static String decryptToString(byte[] content, String aesTextKey) {
|
||||
return new String(decrypt(content, aesTextKey), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
public static byte[] decrypt(byte[] content, String aesTextKey) {
|
||||
return decrypt(content, Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
public static byte[] encrypt(byte[] content, byte[] aesKey) {
|
||||
return aes(Pkcs7Encoder.encode(content), aesKey, Cipher.ENCRYPT_MODE);
|
||||
}
|
||||
|
||||
public static byte[] decrypt(byte[] encrypted, byte[] aesKey) {
|
||||
return Pkcs7Encoder.decode(aes(encrypted, aesKey, Cipher.DECRYPT_MODE));
|
||||
}
|
||||
|
||||
private static byte[] aes(byte[] encrypted, byte[] aesKey, int mode) {
|
||||
Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32");
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
|
||||
IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
|
||||
cipher.init(mode, keySpec, iv);
|
||||
return cipher.doFinal(encrypted);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 兼容 mysql 的 aes 加密
|
||||
*
|
||||
* @param input input
|
||||
* @param aesKey aesKey
|
||||
* @return byte array
|
||||
*/
|
||||
public static byte[] encryptMysql(String input, String aesKey) {
|
||||
return encryptMysql(input, aesKey, Function.identity());
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容 mysql 的 aes 加密
|
||||
*
|
||||
* @param input input
|
||||
* @param aesKey aesKey
|
||||
* @param <T> 泛型标记
|
||||
* @return T 泛型对象
|
||||
*/
|
||||
public static <T> T encryptMysql(String input, String aesKey, Function<byte[], T> mapper) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, genMySqlAesKey(aesKey));
|
||||
byte[] bytes = cipher.doFinal(input.getBytes(StandardCharsets.UTF_8));
|
||||
return mapper.apply(bytes);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容 mysql 的 aes 加密
|
||||
*
|
||||
* @param input input
|
||||
* @param aesKey aesKey
|
||||
* @return byte 数组
|
||||
*/
|
||||
public static byte[] decryptMysql(String input, String aesKey) {
|
||||
return decryptMysql(input, txt -> txt.getBytes(DEFAULT_CHARSET), aesKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容 mysql 的 aes 加密
|
||||
*
|
||||
* @param input input
|
||||
* @param aesKey aesKey
|
||||
* @return byte 数组
|
||||
*/
|
||||
public static byte[] decryptMysql(String input, Function<String, byte[]> inputMapper, String aesKey) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, genMySqlAesKey(aesKey));
|
||||
return cipher.doFinal(inputMapper.apply(input));
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容 mysql 的 aes 加密
|
||||
*
|
||||
* @param input input
|
||||
* @param inputMapper Function
|
||||
* @param aesKey aesKey
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String decryptMysqlToString(String input, Function<String, byte[]> inputMapper, String aesKey) {
|
||||
return new String(decryptMysql(input, inputMapper, aesKey), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容 mysql 的 aes 加密
|
||||
*
|
||||
* @param input input
|
||||
* @param aesKey aesKey
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String decryptMysqlToString(String input, String aesKey) {
|
||||
return decryptMysqlToString(input, txt -> txt.getBytes(DEFAULT_CHARSET), aesKey);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Spring AntPath 规则文件过滤
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class AntPathFilter implements FileFilter, Serializable {
|
||||
private static final long serialVersionUID = 812598009067554612L;
|
||||
private static final PathMatcher PATH_MATCHER = new AntPathMatcher();
|
||||
|
||||
private final String pattern;
|
||||
|
||||
/**
|
||||
* 过滤规则
|
||||
*
|
||||
* @param pathname 路径
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
String filePath = pathname.getAbsolutePath();
|
||||
return PATH_MATCHER.match(pattern, filePath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* Base64工具
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class Base64Util {
|
||||
public static final Base64.Encoder ENCODER = Base64.getEncoder();
|
||||
public static final Base64.Encoder URL_ENCODER = Base64.getUrlEncoder();
|
||||
public static final Base64.Decoder DECODER = Base64.getDecoder();
|
||||
public static final Base64.Decoder URL_DECODER = Base64.getUrlDecoder();
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @param value 字符串
|
||||
* @return {String}
|
||||
*/
|
||||
public static String encode(String value) {
|
||||
return encode(value, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @param value 字符串
|
||||
* @param charset 字符集
|
||||
* @return {String}
|
||||
*/
|
||||
public static String encode(String value, Charset charset) {
|
||||
byte[] val = value.getBytes(charset);
|
||||
return new String(encode(val), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码URL安全
|
||||
*
|
||||
* @param value 字符串
|
||||
* @return {String}
|
||||
*/
|
||||
public static String encodeUrlSafe(String value) {
|
||||
return encodeUrlSafe(value, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码URL安全
|
||||
*
|
||||
* @param value 字符串
|
||||
* @param charset 字符集
|
||||
* @return {String}
|
||||
*/
|
||||
public static String encodeUrlSafe(String value, Charset charset) {
|
||||
byte[] val = value.getBytes(charset);
|
||||
return new String(encodeUrlSafe(val), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码
|
||||
*
|
||||
* @param value 字符串
|
||||
* @return {String}
|
||||
*/
|
||||
public static String decode(String value) {
|
||||
return decode(value, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码
|
||||
*
|
||||
* @param value 字符串
|
||||
* @param charset 字符集
|
||||
* @return {String}
|
||||
*/
|
||||
public static String decode(String value, Charset charset) {
|
||||
byte[] val = value.getBytes(charset);
|
||||
byte[] decodedValue = decode(val);
|
||||
return new String(decodedValue, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码URL安全
|
||||
*
|
||||
* @param value 字符串
|
||||
* @return {String}
|
||||
*/
|
||||
public static String decodeUrlSafe(String value) {
|
||||
return decodeUrlSafe(value, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码URL安全
|
||||
*
|
||||
* @param value 字符串
|
||||
* @param charset 字符集
|
||||
* @return {String}
|
||||
*/
|
||||
public static String decodeUrlSafe(String value, Charset charset) {
|
||||
byte[] val = value.getBytes(charset);
|
||||
byte[] decodedValue = decodeUrlSafe(val);
|
||||
return new String(decodedValue, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-encode the given byte array.
|
||||
*
|
||||
* @param src the original byte array
|
||||
* @return the encoded byte array
|
||||
*/
|
||||
public static byte[] encode(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return src;
|
||||
}
|
||||
return ENCODER.encode(src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-decode the given byte array.
|
||||
*
|
||||
* @param src the encoded byte array
|
||||
* @return the original byte array
|
||||
*/
|
||||
public static byte[] decode(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return src;
|
||||
}
|
||||
return DECODER.decode(src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-encode the given byte array using the RFC 4648
|
||||
* "URL and Filename Safe Alphabet".
|
||||
*
|
||||
* @param src the original byte array
|
||||
* @return the encoded byte array
|
||||
*/
|
||||
public static byte[] encodeUrlSafe(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return src;
|
||||
}
|
||||
return URL_ENCODER.encode(src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-decode the given byte array using the RFC 4648
|
||||
* "URL and Filename Safe Alphabet".
|
||||
*
|
||||
* @param src the encoded byte array
|
||||
* @return the original byte array
|
||||
* @since 4.2.4
|
||||
*/
|
||||
public static byte[] decodeUrlSafe(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return src;
|
||||
}
|
||||
return URL_DECODER.decode(src);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-encode the given byte array to a String.
|
||||
*
|
||||
* @param src the original byte array
|
||||
* @return the encoded byte array as a UTF-8 String
|
||||
*/
|
||||
public static String encodeToString(byte[] src) {
|
||||
if (src.length == 0) {
|
||||
return "";
|
||||
}
|
||||
return new String(encode(src), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-decode the given byte array from a UTF-8 String.
|
||||
*
|
||||
* @param src the encoded UTF-8 String
|
||||
* @return the original byte array
|
||||
*/
|
||||
public static byte[] decodeFromString(String src) {
|
||||
if (src.isEmpty()) {
|
||||
return new byte[0];
|
||||
}
|
||||
return decode(src.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-encode the given byte array to a String using the RFC 4648
|
||||
* "URL and Filename Safe Alphabet".
|
||||
*
|
||||
* @param src the original byte array
|
||||
* @return the encoded byte array as a UTF-8 String
|
||||
*/
|
||||
public static String encodeToUrlSafeString(byte[] src) {
|
||||
return new String(encodeUrlSafe(src), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Base64-decode the given byte array from a UTF-8 String using the RFC 4648
|
||||
* "URL and Filename Safe Alphabet".
|
||||
*
|
||||
* @param src the encoded UTF-8 String
|
||||
* @return the original byte array
|
||||
*/
|
||||
public static byte[] decodeFromUrlSafeString(String src) {
|
||||
return decodeUrlSafe(src.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
|
||||
import org.springblade.core.tool.beans.BeanProperty;
|
||||
import org.springblade.core.tool.beans.BladeBeanCopier;
|
||||
import org.springblade.core.tool.beans.BladeBeanMap;
|
||||
import org.springblade.core.tool.convert.BladeConverter;
|
||||
import org.springframework.beans.BeanWrapper;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyAccessorFactory;
|
||||
import org.springframework.cglib.beans.BeanGenerator;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 实体工具类
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class BeanUtil extends org.springframework.beans.BeanUtils {
|
||||
|
||||
/**
|
||||
* 实例化对象
|
||||
*
|
||||
* @param clazz 类
|
||||
* @param <T> 泛型标记
|
||||
* @return 对象
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T newInstance(Class<?> clazz) {
|
||||
return (T) instantiateClass(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 实例化对象
|
||||
*
|
||||
* @param clazzStr 类名
|
||||
* @param <T> 泛型标记
|
||||
* @return 对象
|
||||
*/
|
||||
public static <T> T newInstance(String clazzStr) {
|
||||
try {
|
||||
Class<?> clazz = ClassUtil.forName(clazzStr, null);
|
||||
return newInstance(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Bean的属性, 支持 propertyName 多级 :test.user.name
|
||||
*
|
||||
* @param bean bean
|
||||
* @param propertyName 属性名
|
||||
* @return 属性值
|
||||
*/
|
||||
@Nullable
|
||||
public static Object getProperty(@Nullable Object bean, String propertyName) {
|
||||
if (bean == null) {
|
||||
return null;
|
||||
}
|
||||
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean);
|
||||
return beanWrapper.getPropertyValue(propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Bean属性, 支持 propertyName 多级 :test.user.name
|
||||
*
|
||||
* @param bean bean
|
||||
* @param propertyName 属性名
|
||||
* @param value 属性值
|
||||
*/
|
||||
public static void setProperty(Object bean, String propertyName, Object value) {
|
||||
Objects.requireNonNull(bean, "bean Could not null");
|
||||
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(bean);
|
||||
beanWrapper.setPropertyValue(propertyName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 深复制
|
||||
*
|
||||
* <p>
|
||||
* 支持 map bean
|
||||
* </p>
|
||||
*
|
||||
* @param source 源对象
|
||||
* @param <T> 泛型标记
|
||||
* @return T
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public static <T> T clone(@Nullable T source) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
return (T) BeanUtil.copy(source, source.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* copy 对象属性,默认不使用Convert
|
||||
*
|
||||
* <p>
|
||||
* 支持 map bean copy
|
||||
* </p>
|
||||
*
|
||||
* @param source 源对象
|
||||
* @param clazz 类名
|
||||
* @param <T> 泛型标记
|
||||
* @return T
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T copy(@Nullable Object source, Class<T> clazz) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
return BeanUtil.copy(source, source.getClass(), clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* copy 对象属性,默认不使用Convert
|
||||
*
|
||||
* <p>
|
||||
* 支持 map bean copy
|
||||
* </p>
|
||||
*
|
||||
* @param source 源对象
|
||||
* @param sourceClazz 源类型
|
||||
* @param targetClazz 转换成的类型
|
||||
* @param <T> 泛型标记
|
||||
* @return T
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T copy(@Nullable Object source, Class sourceClazz, Class<T> targetClazz) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
BladeBeanCopier copier = BladeBeanCopier.create(sourceClazz, targetClazz, false);
|
||||
T to = newInstance(targetClazz);
|
||||
copier.copy(source, to, null);
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* copy 列表对象,默认不使用Convert
|
||||
*
|
||||
* <p>
|
||||
* 支持 map bean copy
|
||||
* </p>
|
||||
*
|
||||
* @param sourceList 源列表
|
||||
* @param targetClazz 转换成的类型
|
||||
* @param <T> 泛型标记
|
||||
* @return T
|
||||
*/
|
||||
public static <T> List<T> copy(@Nullable Collection<?> sourceList, Class<T> targetClazz) {
|
||||
if (sourceList == null || sourceList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<T> outList = new ArrayList<>(sourceList.size());
|
||||
Class<?> sourceClazz = null;
|
||||
for (Object source : sourceList) {
|
||||
if (source == null) {
|
||||
continue;
|
||||
}
|
||||
if (sourceClazz == null) {
|
||||
sourceClazz = source.getClass();
|
||||
}
|
||||
T bean = BeanUtil.copy(source, sourceClazz, targetClazz);
|
||||
outList.add(bean);
|
||||
}
|
||||
return outList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝对象
|
||||
*
|
||||
* <p>
|
||||
* 支持 map bean copy
|
||||
* </p>
|
||||
*
|
||||
* @param source 源对象
|
||||
* @param targetBean 需要赋值的对象
|
||||
*/
|
||||
public static void copy(@Nullable Object source, @Nullable Object targetBean) {
|
||||
if (source == null || targetBean == null) {
|
||||
return;
|
||||
}
|
||||
BladeBeanCopier copier = BladeBeanCopier
|
||||
.create(source.getClass(), targetBean.getClass(), false);
|
||||
|
||||
copier.copy(source, targetBean, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝对象,source 属性做 null 判断,Map 不支持,map 会做 instanceof 判断,不会
|
||||
*
|
||||
* <p>
|
||||
* 支持 bean copy
|
||||
* </p>
|
||||
*
|
||||
* @param source 源对象
|
||||
* @param targetBean 需要赋值的对象
|
||||
*/
|
||||
public static void copyNonNull(@Nullable Object source, @Nullable Object targetBean) {
|
||||
if (source == null || targetBean == null) {
|
||||
return;
|
||||
}
|
||||
BladeBeanCopier copier = BladeBeanCopier
|
||||
.create(source.getClass(), targetBean.getClass(), false, true);
|
||||
|
||||
copier.copy(source, targetBean, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝对象并对不同类型属性进行转换
|
||||
*
|
||||
* <p>
|
||||
* 支持 map bean copy
|
||||
* </p>
|
||||
*
|
||||
* @param source 源对象
|
||||
* @param targetClazz 转换成的类
|
||||
* @param <T> 泛型标记
|
||||
* @return T
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T copyWithConvert(@Nullable Object source, Class<T> targetClazz) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
return BeanUtil.copyWithConvert(source, source.getClass(), targetClazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝对象并对不同类型属性进行转换
|
||||
*
|
||||
* <p>
|
||||
* 支持 map bean copy
|
||||
* </p>
|
||||
*
|
||||
* @param source 源对象
|
||||
* @param sourceClazz 源类
|
||||
* @param targetClazz 转换成的类
|
||||
* @param <T> 泛型标记
|
||||
* @return T
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T copyWithConvert(@Nullable Object source, Class<?> sourceClazz, Class<T> targetClazz) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
BladeBeanCopier copier = BladeBeanCopier.create(sourceClazz, targetClazz, true);
|
||||
T to = newInstance(targetClazz);
|
||||
copier.copy(source, to, new BladeConverter(sourceClazz, targetClazz));
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝列表并对不同类型属性进行转换
|
||||
*
|
||||
* <p>
|
||||
* 支持 map bean copy
|
||||
* </p>
|
||||
*
|
||||
* @param sourceList 源对象列表
|
||||
* @param targetClazz 转换成的类
|
||||
* @param <T> 泛型标记
|
||||
* @return List
|
||||
*/
|
||||
public static <T> List<T> copyWithConvert(@Nullable Collection<?> sourceList, Class<T> targetClazz) {
|
||||
if (sourceList == null || sourceList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<T> outList = new ArrayList<>(sourceList.size());
|
||||
Class<?> sourceClazz = null;
|
||||
for (Object source : sourceList) {
|
||||
if (source == null) {
|
||||
continue;
|
||||
}
|
||||
if (sourceClazz == null) {
|
||||
sourceClazz = source.getClass();
|
||||
}
|
||||
T bean = BeanUtil.copyWithConvert(source, sourceClazz, targetClazz);
|
||||
outList.add(bean);
|
||||
}
|
||||
return outList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the property values of the given source bean into the target class.
|
||||
* <p>Note: The source and target classes do not have to match or even be derived
|
||||
* from each other, as long as the properties match. Any bean properties that the
|
||||
* source bean exposes but the target bean does not will silently be ignored.
|
||||
* <p>This is just a convenience method. For more complex transfer needs,
|
||||
*
|
||||
* @param source the source bean
|
||||
* @param targetClazz the target bean class
|
||||
* @param <T> 泛型标记
|
||||
* @return T
|
||||
* @throws BeansException if the copying failed
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T copyProperties(@Nullable Object source, Class<T> targetClazz) throws BeansException {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
T to = newInstance(targetClazz);
|
||||
BeanUtil.copyProperties(source, to);
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the property values of the given source bean into the target class.
|
||||
* <p>Note: The source and target classes do not have to match or even be derived
|
||||
* from each other, as long as the properties match. Any bean properties that the
|
||||
* source bean exposes but the target bean does not will silently be ignored.
|
||||
* <p>This is just a convenience method. For more complex transfer needs,
|
||||
*
|
||||
* @param sourceList the source list bean
|
||||
* @param targetClazz the target bean class
|
||||
* @param <T> 泛型标记
|
||||
* @return List
|
||||
* @throws BeansException if the copying failed
|
||||
*/
|
||||
public static <T> List<T> copyProperties(@Nullable Collection<?> sourceList, Class<T> targetClazz) throws BeansException {
|
||||
if (sourceList == null || sourceList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<T> outList = new ArrayList<>(sourceList.size());
|
||||
for (Object source : sourceList) {
|
||||
if (source == null) {
|
||||
continue;
|
||||
}
|
||||
T bean = BeanUtil.copyProperties(source, targetClazz);
|
||||
outList.add(bean);
|
||||
}
|
||||
return outList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象装成map形式
|
||||
*
|
||||
* @param bean 源对象
|
||||
* @return {Map}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, Object> toMap(@Nullable Object bean) {
|
||||
if (bean == null) {
|
||||
return new HashMap<>(0);
|
||||
}
|
||||
return BladeBeanMap.create(bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将map 转为 bean
|
||||
*
|
||||
* @param beanMap map
|
||||
* @param valueType 对象类型
|
||||
* @param <T> 泛型标记
|
||||
* @return {T}
|
||||
*/
|
||||
public static <T> T toBean(Map<String, Object> beanMap, Class<T> valueType) {
|
||||
Objects.requireNonNull(beanMap, "beanMap Could not null");
|
||||
T to = newInstance(valueType);
|
||||
if (beanMap.isEmpty()) {
|
||||
return to;
|
||||
}
|
||||
BeanUtil.copy(beanMap, to);
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给一个Bean添加字段
|
||||
*
|
||||
* @param superBean 父级Bean
|
||||
* @param props 新增属性
|
||||
* @return {Object}
|
||||
*/
|
||||
@Nullable
|
||||
public static Object generator(@Nullable Object superBean, BeanProperty... props) {
|
||||
if (superBean == null) {
|
||||
return null;
|
||||
}
|
||||
Class<?> superclass = superBean.getClass();
|
||||
Object genBean = generator(superclass, props);
|
||||
BeanUtil.copy(superBean, genBean);
|
||||
return genBean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给一个class添加字段
|
||||
*
|
||||
* @param superclass 父级
|
||||
* @param props 新增属性
|
||||
* @return {Object}
|
||||
*/
|
||||
public static Object generator(Class<?> superclass, BeanProperty... props) {
|
||||
BeanGenerator generator = new BeanGenerator();
|
||||
generator.setSuperclass(superclass);
|
||||
generator.setUseCache(true);
|
||||
for (BeanProperty prop : props) {
|
||||
generator.addProperty(prop.getName(), prop.getType());
|
||||
}
|
||||
return generator.create();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
/**
|
||||
* char 常量池
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public interface CharPool {
|
||||
|
||||
// @formatter:off
|
||||
char UPPER_A = 'A';
|
||||
char LOWER_A = 'a';
|
||||
char UPPER_Z = 'Z';
|
||||
char LOWER_Z = 'z';
|
||||
char DOT = '.';
|
||||
char AT = '@';
|
||||
char LEFT_BRACE = '{';
|
||||
char RIGHT_BRACE = '}';
|
||||
char LEFT_BRACKET = '(';
|
||||
char RIGHT_BRACKET = ')';
|
||||
char DASH = '-';
|
||||
char PERCENT = '%';
|
||||
char PIPE = '|';
|
||||
char PLUS = '+';
|
||||
char QUESTION_MARK = '?';
|
||||
char EXCLAMATION_MARK = '!';
|
||||
char EQUALS = '=';
|
||||
char AMPERSAND = '&';
|
||||
char ASTERISK = '*';
|
||||
char STAR = ASTERISK;
|
||||
char BACK_SLASH = '\\';
|
||||
char COLON = ':';
|
||||
char COMMA = ',';
|
||||
char DOLLAR = '$';
|
||||
char SLASH = '/';
|
||||
char HASH = '#';
|
||||
char HAT = '^';
|
||||
char LEFT_CHEV = '<';
|
||||
char NEWLINE = '\n';
|
||||
char N = 'n';
|
||||
char Y = 'y';
|
||||
char QUOTE = '\"';
|
||||
char RETURN = '\r';
|
||||
char TAB = '\t';
|
||||
char RIGHT_CHEV = '>';
|
||||
char SEMICOLON = ';';
|
||||
char SINGLE_QUOTE = '\'';
|
||||
char BACKTICK = '`';
|
||||
char SPACE = ' ';
|
||||
char TILDA = '~';
|
||||
char LEFT_SQ_BRACKET = '[';
|
||||
char RIGHT_SQ_BRACKET = ']';
|
||||
char UNDERSCORE = '_';
|
||||
char ONE = '1';
|
||||
char ZERO = '0';
|
||||
// @formatter:on
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
|
||||
/**
|
||||
* 字符集工具类
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class Charsets {
|
||||
|
||||
/**
|
||||
* 字符集ISO-8859-1
|
||||
*/
|
||||
public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
|
||||
public static final String ISO_8859_1_NAME = ISO_8859_1.name();
|
||||
|
||||
/**
|
||||
* 字符集GBK
|
||||
*/
|
||||
public static final Charset GBK = Charset.forName(StringPool.GBK);
|
||||
public static final String GBK_NAME = GBK.name();
|
||||
|
||||
/**
|
||||
* 字符集utf-8
|
||||
*/
|
||||
public static final Charset UTF_8 = StandardCharsets.UTF_8;
|
||||
public static final String UTF_8_NAME = UTF_8.name();
|
||||
|
||||
/**
|
||||
* 转换为Charset对象
|
||||
*
|
||||
* @param charsetName 字符集,为空则返回默认字符集
|
||||
* @return Charsets
|
||||
* @throws UnsupportedCharsetException 编码不支持
|
||||
*/
|
||||
public static Charset charset(String charsetName) throws UnsupportedCharsetException {
|
||||
return StringUtil.isBlank(charsetName) ? Charset.defaultCharset() : Charset.forName(charsetName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 类操作工具
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class ClassUtil extends org.springframework.util.ClassUtils {
|
||||
|
||||
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||
|
||||
/**
|
||||
* 获取方法参数信息
|
||||
*
|
||||
* @param constructor 构造器
|
||||
* @param parameterIndex 参数序号
|
||||
* @return {MethodParameter}
|
||||
*/
|
||||
public static MethodParameter getMethodParameter(Constructor<?> constructor, int parameterIndex) {
|
||||
MethodParameter methodParameter = new SynthesizingMethodParameter(constructor, parameterIndex);
|
||||
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
|
||||
return methodParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取方法参数信息
|
||||
*
|
||||
* @param method 方法
|
||||
* @param parameterIndex 参数序号
|
||||
* @return {MethodParameter}
|
||||
*/
|
||||
public static MethodParameter getMethodParameter(Method method, int parameterIndex) {
|
||||
MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex);
|
||||
methodParameter.initParameterNameDiscovery(PARAMETER_NAME_DISCOVERER);
|
||||
return methodParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Annotation
|
||||
*
|
||||
* @param method Method
|
||||
* @param annotationType 注解类
|
||||
* @param <A> 泛型标记
|
||||
* @return {Annotation}
|
||||
*/
|
||||
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
|
||||
Class<?> targetClass = method.getDeclaringClass();
|
||||
// The method may be on an interface, but we need attributes from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = ClassUtil.getMostSpecificMethod(method, targetClass);
|
||||
// If we are dealing with method with generic parameters, find the original method.
|
||||
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
// 先找方法,再找方法上的类
|
||||
A annotation = AnnotatedElementUtils.findMergedAnnotation(specificMethod, annotationType);
|
||||
;
|
||||
if (null != annotation) {
|
||||
return annotation;
|
||||
}
|
||||
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
|
||||
return AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Annotation,先找方法,再找方法上的类
|
||||
*
|
||||
* @param point AOP切点
|
||||
* @param annotationType 注解类
|
||||
* @param <A> 泛型标记
|
||||
* @return {Annotation}
|
||||
*/
|
||||
public static <A extends Annotation> A getAnnotation(ProceedingJoinPoint point, Class<A> annotationType) {
|
||||
MethodSignature methodSignature = (MethodSignature) point.getSignature();
|
||||
return getAnnotation(methodSignature.getMethod(), annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Annotation
|
||||
*
|
||||
* @param handlerMethod HandlerMethod
|
||||
* @param annotationType 注解类
|
||||
* @param <A> 泛型标记
|
||||
* @return {Annotation}
|
||||
*/
|
||||
public static <A extends Annotation> A getAnnotation(HandlerMethod handlerMethod, Class<A> annotationType) {
|
||||
// 先找方法,再找方法上的类
|
||||
A annotation = handlerMethod.getMethodAnnotation(annotationType);
|
||||
if (null != annotation) {
|
||||
return annotation;
|
||||
}
|
||||
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
|
||||
Class<?> beanType = handlerMethod.getBeanType();
|
||||
return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否有注解 Annotation
|
||||
*
|
||||
* @param method Method
|
||||
* @param annotationType 注解类
|
||||
* @param <A> 泛型标记
|
||||
* @return {boolean}
|
||||
*/
|
||||
public static <A extends Annotation> boolean isAnnotated(Method method, Class<A> annotationType) {
|
||||
// 先找方法,再找方法上的类
|
||||
boolean isMethodAnnotated = AnnotatedElementUtils.isAnnotated(method, annotationType);
|
||||
if (isMethodAnnotated) {
|
||||
return true;
|
||||
}
|
||||
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
|
||||
Class<?> targetClass = method.getDeclaringClass();
|
||||
return AnnotatedElementUtils.isAnnotated(targetClass, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类的字段名合集
|
||||
*
|
||||
* @param clazz 类
|
||||
* @param <T> 泛型标记
|
||||
* @return 字段名合集
|
||||
*/
|
||||
public static <T> Set<String> getClassFieldNames(Class<T> clazz) {
|
||||
Set<String> fieldNames = new HashSet<>();
|
||||
// 循环遍历 clazz 及其父类,获取字段
|
||||
while (clazz != null && clazz != Object.class) {
|
||||
Field[] fields = clazz.getDeclaredFields(); // 获取当前类的所有字段
|
||||
fieldNames.addAll(
|
||||
Set.of(fields) // 使用 Set.of() 创建不可变集合
|
||||
.stream()
|
||||
.map(Field::getName) // 提取字段名
|
||||
.collect(Collectors.toSet()) // 收集成 Set
|
||||
);
|
||||
clazz = (Class<T>) clazz.getSuperclass(); // 获取父类
|
||||
}
|
||||
return fieldNames;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 集合工具类
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class CollectionUtil extends CollectionUtils {
|
||||
|
||||
/**
|
||||
* Return {@code true} if the supplied Collection is not {@code null} or empty.
|
||||
* Otherwise, return {@code false}.
|
||||
*
|
||||
* @param collection the Collection to check
|
||||
* @return whether the given Collection is not empty
|
||||
*/
|
||||
public static boolean isNotEmpty(@Nullable Collection<?> collection) {
|
||||
return !CollectionUtil.isEmpty(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the supplied Map is not {@code null} or empty.
|
||||
* Otherwise, return {@code false}.
|
||||
*
|
||||
* @param map the Map to check
|
||||
* @return whether the given Map is not empty
|
||||
*/
|
||||
public static boolean isNotEmpty(@Nullable Map<?, ?> map) {
|
||||
return !CollectionUtil.isEmpty(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given Array contains the given element.
|
||||
*
|
||||
* @param array the Array to check
|
||||
* @param element the element to look for
|
||||
* @param <T> The generic tag
|
||||
* @return {@code true} if found, {@code false} else
|
||||
*/
|
||||
public static <T> boolean contains(@Nullable T[] array, final T element) {
|
||||
if (array == null) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.stream(array).anyMatch(x -> ObjectUtil.nullSafeEquals(x, element));
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates 2 arrays
|
||||
*
|
||||
* @param one 数组1
|
||||
* @param other 数组2
|
||||
* @return 新数组
|
||||
*/
|
||||
public static String[] concat(String[] one, String[] other) {
|
||||
return concat(one, other, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenates 2 arrays
|
||||
*
|
||||
* @param one 数组1
|
||||
* @param other 数组2
|
||||
* @param clazz 数组类
|
||||
* @return 新数组
|
||||
*/
|
||||
public static <T> T[] concat(T[] one, T[] other, Class<T> clazz) {
|
||||
T[] target = (T[]) Array.newInstance(clazz, one.length + other.length);
|
||||
System.arraycopy(one, 0, target, 0, one.length);
|
||||
System.arraycopy(other, 0, target, one.length, other.length);
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象是否为数组对象
|
||||
*
|
||||
* @param obj 对象
|
||||
* @return 是否为数组对象,如果为{@code null} 返回false
|
||||
*/
|
||||
public static boolean isArray(Object obj) {
|
||||
if (null == obj) {
|
||||
return false;
|
||||
}
|
||||
return obj.getClass().isArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 不可变 Set
|
||||
*
|
||||
* @param es 对象
|
||||
* @param <E> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <E> Set<E> ofImmutableSet(E... es) {
|
||||
Objects.requireNonNull(es, "args es is null.");
|
||||
return Arrays.stream(es).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 不可变 List
|
||||
*
|
||||
* @param es 对象
|
||||
* @param <E> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <E> List<E> ofImmutableList(E... es) {
|
||||
Objects.requireNonNull(es, "args es is null.");
|
||||
return Arrays.stream(es).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable 转换为List集合
|
||||
*
|
||||
* @param elements Iterable
|
||||
* @param <E> 泛型
|
||||
* @return 集合
|
||||
*/
|
||||
public static <E> List<E> toList(Iterable<E> elements) {
|
||||
Objects.requireNonNull(elements, "elements es is null.");
|
||||
if (elements instanceof Collection) {
|
||||
return new ArrayList((Collection) elements);
|
||||
}
|
||||
Iterator<E> iterator = elements.iterator();
|
||||
List<E> list = new ArrayList<>();
|
||||
while (iterator.hasNext()) {
|
||||
list.add(iterator.next());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将key value 数组转为 map
|
||||
*
|
||||
* @param keysValues key value 数组
|
||||
* @param <K> key
|
||||
* @param <V> value
|
||||
* @return map 集合
|
||||
*/
|
||||
public static <K, V> Map<K, V> toMap(Object... keysValues) {
|
||||
int kvLength = keysValues.length;
|
||||
if (kvLength % 2 != 0) {
|
||||
throw new IllegalArgumentException("wrong number of arguments for met, keysValues length can not be odd");
|
||||
}
|
||||
Map<K, V> keyValueMap = new HashMap<>(kvLength);
|
||||
for (int i = kvLength - 2; i >= 0; i -= 2) {
|
||||
Object key = keysValues[i];
|
||||
Object value = keysValues[i + 1];
|
||||
keyValueMap.put((K) key, (V) value);
|
||||
}
|
||||
return keyValueMap;
|
||||
}
|
||||
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* 参考tomcat8中的并发DateFormat
|
||||
* <p>
|
||||
* {@link SimpleDateFormat}的线程安全包装器。
|
||||
* 不使用ThreadLocal,创建足够的SimpleDateFormat对象来满足并发性要求。
|
||||
* </p>
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class ConcurrentDateFormat {
|
||||
private final String format;
|
||||
private final Locale locale;
|
||||
private final TimeZone timezone;
|
||||
private final Queue<SimpleDateFormat> queue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private ConcurrentDateFormat(String format, Locale locale, TimeZone timezone) {
|
||||
this.format = format;
|
||||
this.locale = locale;
|
||||
this.timezone = timezone;
|
||||
SimpleDateFormat initial = createInstance();
|
||||
queue.add(initial);
|
||||
}
|
||||
|
||||
public static ConcurrentDateFormat of(String format) {
|
||||
return new ConcurrentDateFormat(format, Locale.getDefault(), TimeZone.getDefault());
|
||||
}
|
||||
|
||||
public static ConcurrentDateFormat of(String format, TimeZone timezone) {
|
||||
return new ConcurrentDateFormat(format, Locale.getDefault(), timezone);
|
||||
}
|
||||
|
||||
public static ConcurrentDateFormat of(String format, Locale locale, TimeZone timezone) {
|
||||
return new ConcurrentDateFormat(format, locale, timezone);
|
||||
}
|
||||
|
||||
public String format(Date date) {
|
||||
SimpleDateFormat sdf = queue.poll();
|
||||
if (sdf == null) {
|
||||
sdf = createInstance();
|
||||
}
|
||||
String result = sdf.format(date);
|
||||
queue.add(sdf);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Date parse(String source) throws ParseException {
|
||||
SimpleDateFormat sdf = queue.poll();
|
||||
if (sdf == null) {
|
||||
sdf = createInstance();
|
||||
}
|
||||
Date result = sdf.parse(source);
|
||||
queue.add(sdf);
|
||||
return result;
|
||||
}
|
||||
|
||||
private SimpleDateFormat createInstance() {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(format, locale);
|
||||
sdf.setTimeZone(timezone);
|
||||
return sdf;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springblade.core.tool.convert.BladeConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* 基于 spring ConversionService 类型转换
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class ConvertUtil {
|
||||
|
||||
/**
|
||||
* Convenience operation for converting a source object to the specified targetType.
|
||||
* {@link TypeDescriptor#forObject(Object)}.
|
||||
* @param source the source object
|
||||
* @param targetType the target type
|
||||
* @param <T> 泛型标记
|
||||
* @return the converted value
|
||||
* @throws IllegalArgumentException if targetType is {@code null},
|
||||
* or sourceType is {@code null} but source is not {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T convert(@Nullable Object source, Class<T> targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
if (ClassUtil.isAssignableValue(targetType, source)) {
|
||||
return (T) source;
|
||||
}
|
||||
GenericConversionService conversionService = BladeConversionService.getInstance();
|
||||
return conversionService.convert(source, targetType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience operation for converting a source object to the specified targetType,
|
||||
* where the target type is a descriptor that provides additional conversion context.
|
||||
* {@link TypeDescriptor#forObject(Object)}.
|
||||
* @param source the source object
|
||||
* @param sourceType the source type
|
||||
* @param targetType the target type
|
||||
* @param <T> 泛型标记
|
||||
* @return the converted value
|
||||
* @throws IllegalArgumentException if targetType is {@code null},
|
||||
* or sourceType is {@code null} but source is not {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T convert(@Nullable Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
GenericConversionService conversionService = BladeConversionService.getInstance();
|
||||
return (T) conversionService.convert(source, sourceType, targetType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience operation for converting a source object to the specified targetType,
|
||||
* where the target type is a descriptor that provides additional conversion context.
|
||||
* Simply delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)} and
|
||||
* encapsulates the construction of the source type descriptor using
|
||||
* {@link TypeDescriptor#forObject(Object)}.
|
||||
* @param source the source object
|
||||
* @param targetType the target type
|
||||
* @param <T> 泛型标记
|
||||
* @return the converted value
|
||||
* @throws IllegalArgumentException if targetType is {@code null},
|
||||
* or sourceType is {@code null} but source is not {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public static <T> T convert(@Nullable Object source, TypeDescriptor targetType) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
GenericConversionService conversionService = BladeConversionService.getInstance();
|
||||
return (T) conversionService.convert(source, targetType);
|
||||
}
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
/**
|
||||
* 数据类型转换工具类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class DatatypeConverterUtil {
|
||||
|
||||
/**
|
||||
* hex文本转换为二进制
|
||||
*
|
||||
* @param hexStr hex文本
|
||||
* @return byte[]
|
||||
*/
|
||||
public static byte[] parseHexBinary(String hexStr) {
|
||||
final int len = hexStr.length();
|
||||
|
||||
if (len % 2 != 0) {
|
||||
throw new IllegalArgumentException("hexBinary needs to be even-length: " + hexStr);
|
||||
}
|
||||
|
||||
byte[] out = new byte[len / 2];
|
||||
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
int h = hexToBin(hexStr.charAt(i));
|
||||
int l = hexToBin(hexStr.charAt(i + 1));
|
||||
if (h == -1 || l == -1) {
|
||||
throw new IllegalArgumentException("contains illegal character for hexBinary: " + hexStr);
|
||||
}
|
||||
|
||||
out[i / 2] = (byte) (h * 16 + l);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* hex文本转换为int
|
||||
*
|
||||
* @param ch hex文本
|
||||
* @return int
|
||||
*/
|
||||
private static int hexToBin(char ch) {
|
||||
if ('0' <= ch && ch <= '9') {
|
||||
return ch - '0';
|
||||
}
|
||||
if ('A' <= ch && ch <= 'F') {
|
||||
return ch - 'A' + 10;
|
||||
}
|
||||
if ('a' <= ch && ch <= 'f') {
|
||||
return ch - 'a' + 10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* DateTime 工具类
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DateTimeUtil {
|
||||
public static final DateTimeFormatter DATETIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATETIME);
|
||||
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE);
|
||||
public static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME);
|
||||
|
||||
/**
|
||||
* 日期时间格式化
|
||||
*
|
||||
* @param temporal 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatDateTime(TemporalAccessor temporal) {
|
||||
return DATETIME_FORMAT.format(temporal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期时间格式化
|
||||
*
|
||||
* @param temporal 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatDate(TemporalAccessor temporal) {
|
||||
return DATE_FORMAT.format(temporal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间格式化
|
||||
*
|
||||
* @param temporal 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatTime(TemporalAccessor temporal) {
|
||||
return TIME_FORMAT.format(temporal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期格式化
|
||||
*
|
||||
* @param temporal 时间
|
||||
* @param pattern 表达式
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String format(TemporalAccessor temporal, String pattern) {
|
||||
return DateTimeFormatter.ofPattern(pattern).format(temporal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param pattern 表达式
|
||||
* @return 时间
|
||||
*/
|
||||
public static LocalDateTime parseDateTime(String dateStr, String pattern) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||
return DateTimeUtil.parseDateTime(dateStr, formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param formatter DateTimeFormatter
|
||||
* @return 时间
|
||||
*/
|
||||
public static LocalDateTime parseDateTime(String dateStr, DateTimeFormatter formatter) {
|
||||
return LocalDateTime.parse(dateStr, formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @return 时间
|
||||
*/
|
||||
public static LocalDateTime parseDateTime(String dateStr) {
|
||||
return DateTimeUtil.parseDateTime(dateStr, DateTimeUtil.DATETIME_FORMAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param pattern 表达式
|
||||
* @return 时间
|
||||
*/
|
||||
public static LocalDate parseDate(String dateStr, String pattern) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||
return DateTimeUtil.parseDate(dateStr, formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param formatter DateTimeFormatter
|
||||
* @return 时间
|
||||
*/
|
||||
public static LocalDate parseDate(String dateStr, DateTimeFormatter formatter) {
|
||||
return LocalDate.parse(dateStr, formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为日期
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @return 时间
|
||||
*/
|
||||
public static LocalDate parseDate(String dateStr) {
|
||||
return DateTimeUtil.parseDate(dateStr, DateTimeUtil.DATE_FORMAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param pattern 时间正则
|
||||
* @return 时间
|
||||
*/
|
||||
public static LocalTime parseTime(String dateStr, String pattern) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||
return DateTimeUtil.parseTime(dateStr, formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param formatter DateTimeFormatter
|
||||
* @return 时间
|
||||
*/
|
||||
public static LocalTime parseTime(String dateStr, DateTimeFormatter formatter) {
|
||||
return LocalTime.parse(dateStr, formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @return 时间
|
||||
*/
|
||||
public static LocalTime parseTime(String dateStr) {
|
||||
return DateTimeUtil.parseTime(dateStr, DateTimeUtil.TIME_FORMAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间转 Instant
|
||||
*
|
||||
* @param dateTime 时间
|
||||
* @return Instant
|
||||
*/
|
||||
public static Instant toInstant(LocalDateTime dateTime) {
|
||||
return dateTime.atZone(ZoneId.systemDefault()).toInstant();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instant 转 时间
|
||||
*
|
||||
* @param instant Instant
|
||||
* @return Instant
|
||||
*/
|
||||
public static LocalDateTime toDateTime(Instant instant) {
|
||||
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成 date
|
||||
*
|
||||
* @param dateTime LocalDateTime
|
||||
* @return Date
|
||||
*/
|
||||
public static Date toDate(LocalDateTime dateTime) {
|
||||
return Date.from(DateTimeUtil.toInstant(dateTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较2个时间差,跨度比较小
|
||||
*
|
||||
* @param startInclusive 开始时间
|
||||
* @param endExclusive 结束时间
|
||||
* @return 时间间隔
|
||||
*/
|
||||
public static Duration between(Temporal startInclusive, Temporal endExclusive) {
|
||||
return Duration.between(startInclusive, endExclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较2个时间差,跨度比较大,年月日为单位
|
||||
*
|
||||
* @param startDate 开始时间
|
||||
* @param endDate 结束时间
|
||||
* @return 时间间隔
|
||||
*/
|
||||
public static Period between(LocalDate startDate, LocalDate endDate) {
|
||||
return Period.between(startDate, endDate);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,634 @@
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.time.*;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalAmount;
|
||||
import java.time.temporal.TemporalQuery;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* 日期工具类
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DateUtil {
|
||||
|
||||
public static final String PATTERN_DATETIME = "yyyy-MM-dd HH:mm:ss";
|
||||
public static final String PATTERN_DATETIME_MINI = "yyyyMMddHHmmss";
|
||||
public static final String PATTERN_DATE = "yyyy-MM-dd";
|
||||
public static final String PATTERN_TIME = "HH:mm:ss";
|
||||
/**
|
||||
* 老 date 格式化
|
||||
*/
|
||||
public static final ConcurrentDateFormat DATETIME_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME);
|
||||
public static final ConcurrentDateFormat DATETIME_MINI_FORMAT = ConcurrentDateFormat.of(PATTERN_DATETIME_MINI);
|
||||
public static final ConcurrentDateFormat DATE_FORMAT = ConcurrentDateFormat.of(PATTERN_DATE);
|
||||
public static final ConcurrentDateFormat TIME_FORMAT = ConcurrentDateFormat.of(PATTERN_TIME);
|
||||
/**
|
||||
* java 8 时间格式化
|
||||
*/
|
||||
public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATETIME);
|
||||
public static final DateTimeFormatter DATETIME_MINI_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATETIME_MINI);
|
||||
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_DATE);
|
||||
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(PATTERN_TIME);
|
||||
|
||||
/**
|
||||
* 获取当前日期
|
||||
*
|
||||
* @return 当前日期
|
||||
*/
|
||||
public static Date now() {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加年
|
||||
*
|
||||
* @param date 时间
|
||||
* @param yearsToAdd 添加的年数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plusYears(Date date, int yearsToAdd) {
|
||||
return DateUtil.set(date, Calendar.YEAR, yearsToAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加月
|
||||
*
|
||||
* @param date 时间
|
||||
* @param monthsToAdd 添加的月数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plusMonths(Date date, int monthsToAdd) {
|
||||
return DateUtil.set(date, Calendar.MONTH, monthsToAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加周
|
||||
*
|
||||
* @param date 时间
|
||||
* @param weeksToAdd 添加的周数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plusWeeks(Date date, int weeksToAdd) {
|
||||
return DateUtil.plus(date, Period.ofWeeks(weeksToAdd));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加天
|
||||
*
|
||||
* @param date 时间
|
||||
* @param daysToAdd 添加的天数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plusDays(Date date, long daysToAdd) {
|
||||
return DateUtil.plus(date, Duration.ofDays(daysToAdd));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加小时
|
||||
*
|
||||
* @param date 时间
|
||||
* @param hoursToAdd 添加的小时数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plusHours(Date date, long hoursToAdd) {
|
||||
return DateUtil.plus(date, Duration.ofHours(hoursToAdd));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加分钟
|
||||
*
|
||||
* @param date 时间
|
||||
* @param minutesToAdd 添加的分钟数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plusMinutes(Date date, long minutesToAdd) {
|
||||
return DateUtil.plus(date, Duration.ofMinutes(minutesToAdd));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加秒
|
||||
*
|
||||
* @param date 时间
|
||||
* @param secondsToAdd 添加的秒数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plusSeconds(Date date, long secondsToAdd) {
|
||||
return DateUtil.plus(date, Duration.ofSeconds(secondsToAdd));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加毫秒
|
||||
*
|
||||
* @param date 时间
|
||||
* @param millisToAdd 添加的毫秒数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plusMillis(Date date, long millisToAdd) {
|
||||
return DateUtil.plus(date, Duration.ofMillis(millisToAdd));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加纳秒
|
||||
*
|
||||
* @param date 时间
|
||||
* @param nanosToAdd 添加的纳秒数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plusNanos(Date date, long nanosToAdd) {
|
||||
return DateUtil.plus(date, Duration.ofNanos(nanosToAdd));
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期添加时间量
|
||||
*
|
||||
* @param date 时间
|
||||
* @param amount 时间量
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date plus(Date date, TemporalAmount amount) {
|
||||
Instant instant = date.toInstant();
|
||||
return Date.from(instant.plus(amount));
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少年
|
||||
*
|
||||
* @param date 时间
|
||||
* @param years 减少的年数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minusYears(Date date, int years) {
|
||||
return DateUtil.set(date, Calendar.YEAR, -years);
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少月
|
||||
*
|
||||
* @param date 时间
|
||||
* @param months 减少的月数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minusMonths(Date date, int months) {
|
||||
return DateUtil.set(date, Calendar.MONTH, -months);
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少周
|
||||
*
|
||||
* @param date 时间
|
||||
* @param weeks 减少的周数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minusWeeks(Date date, int weeks) {
|
||||
return DateUtil.minus(date, Period.ofWeeks(weeks));
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少天
|
||||
*
|
||||
* @param date 时间
|
||||
* @param days 减少的天数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minusDays(Date date, long days) {
|
||||
return DateUtil.minus(date, Duration.ofDays(days));
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少小时
|
||||
*
|
||||
* @param date 时间
|
||||
* @param hours 减少的小时数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minusHours(Date date, long hours) {
|
||||
return DateUtil.minus(date, Duration.ofHours(hours));
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少分钟
|
||||
*
|
||||
* @param date 时间
|
||||
* @param minutes 减少的分钟数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minusMinutes(Date date, long minutes) {
|
||||
return DateUtil.minus(date, Duration.ofMinutes(minutes));
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少秒
|
||||
*
|
||||
* @param date 时间
|
||||
* @param seconds 减少的秒数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minusSeconds(Date date, long seconds) {
|
||||
return DateUtil.minus(date, Duration.ofSeconds(seconds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少毫秒
|
||||
*
|
||||
* @param date 时间
|
||||
* @param millis 减少的毫秒数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minusMillis(Date date, long millis) {
|
||||
return DateUtil.minus(date, Duration.ofMillis(millis));
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少纳秒
|
||||
*
|
||||
* @param date 时间
|
||||
* @param nanos 减少的纳秒数
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minusNanos(Date date, long nanos) {
|
||||
return DateUtil.minus(date, Duration.ofNanos(nanos));
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期减少时间量
|
||||
*
|
||||
* @param date 时间
|
||||
* @param amount 时间量
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
public static Date minus(Date date, TemporalAmount amount) {
|
||||
Instant instant = date.toInstant();
|
||||
return Date.from(instant.minus(amount));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置日期属性
|
||||
*
|
||||
* @param date 时间
|
||||
* @param calendarField 更改的属性
|
||||
* @param amount 更改数,-1表示减少
|
||||
* @return 设置后的时间
|
||||
*/
|
||||
private static Date set(Date date, int calendarField, int amount) {
|
||||
Assert.notNull(date, "The date must not be null");
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.setLenient(false);
|
||||
c.setTime(date);
|
||||
c.add(calendarField, amount);
|
||||
return c.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期时间格式化
|
||||
*
|
||||
* @param date 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatDateTime(Date date) {
|
||||
return DATETIME_FORMAT.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期时间格式化
|
||||
*
|
||||
* @param date 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatDateTimeMini(Date date) {
|
||||
return DATETIME_MINI_FORMAT.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期格式化
|
||||
*
|
||||
* @param date 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatDate(Date date) {
|
||||
return DATE_FORMAT.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间格式化
|
||||
*
|
||||
* @param date 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatTime(Date date) {
|
||||
return TIME_FORMAT.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期格式化
|
||||
*
|
||||
* @param date 时间
|
||||
* @param pattern 表达式
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String format(Date date, String pattern) {
|
||||
return ConcurrentDateFormat.of(pattern).format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* java8 日期时间格式化
|
||||
*
|
||||
* @param temporal 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatDateTime(TemporalAccessor temporal) {
|
||||
return DATETIME_FORMATTER.format(temporal);
|
||||
}
|
||||
|
||||
/**
|
||||
* java8 日期时间格式化
|
||||
*
|
||||
* @param temporal 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatDateTimeMini(TemporalAccessor temporal) {
|
||||
return DATETIME_MINI_FORMATTER.format(temporal);
|
||||
}
|
||||
|
||||
/**
|
||||
* java8 日期时间格式化
|
||||
*
|
||||
* @param temporal 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatDate(TemporalAccessor temporal) {
|
||||
return DATE_FORMATTER.format(temporal);
|
||||
}
|
||||
|
||||
/**
|
||||
* java8 时间格式化
|
||||
*
|
||||
* @param temporal 时间
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String formatTime(TemporalAccessor temporal) {
|
||||
return TIME_FORMATTER.format(temporal);
|
||||
}
|
||||
|
||||
/**
|
||||
* java8 日期格式化
|
||||
*
|
||||
* @param temporal 时间
|
||||
* @param pattern 表达式
|
||||
* @return 格式化后的时间
|
||||
*/
|
||||
public static String format(TemporalAccessor temporal, String pattern) {
|
||||
return DateTimeFormatter.ofPattern(pattern).format(temporal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param pattern 表达式
|
||||
* @return 时间
|
||||
*/
|
||||
public static Date parse(String dateStr, String pattern) {
|
||||
ConcurrentDateFormat format = ConcurrentDateFormat.of(pattern);
|
||||
try {
|
||||
return format.parse(dateStr);
|
||||
} catch (ParseException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param format ConcurrentDateFormat
|
||||
* @return 时间
|
||||
*/
|
||||
public static Date parse(String dateStr, ConcurrentDateFormat format) {
|
||||
try {
|
||||
return format.parse(dateStr);
|
||||
} catch (ParseException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串转换为时间
|
||||
*
|
||||
* @param dateStr 时间字符串
|
||||
* @param pattern 表达式
|
||||
* @return 时间
|
||||
*/
|
||||
public static <T> T parse(String dateStr, String pattern, TemporalQuery<T> query) {
|
||||
return DateTimeFormatter.ofPattern(pattern).parse(dateStr, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 时间转 Instant
|
||||
*
|
||||
* @param dateTime 时间
|
||||
* @return Instant
|
||||
*/
|
||||
public static Instant toInstant(LocalDateTime dateTime) {
|
||||
return dateTime.atZone(ZoneId.systemDefault()).toInstant();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instant 转 时间
|
||||
*
|
||||
* @param instant Instant
|
||||
* @return Instant
|
||||
*/
|
||||
public static LocalDateTime toDateTime(Instant instant) {
|
||||
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成 date
|
||||
*
|
||||
* @param dateTime LocalDateTime
|
||||
* @return Date
|
||||
*/
|
||||
public static Date toDate(LocalDateTime dateTime) {
|
||||
return Date.from(DateUtil.toInstant(dateTime));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成 date
|
||||
*
|
||||
* @param localDate LocalDate
|
||||
* @return Date
|
||||
*/
|
||||
public static Date toDate(final LocalDate localDate) {
|
||||
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts local date time to Calendar.
|
||||
*/
|
||||
public static Calendar toCalendar(final LocalDateTime localDateTime) {
|
||||
return GregorianCalendar.from(ZonedDateTime.of(localDateTime, ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
/**
|
||||
* localDateTime 转换成毫秒数
|
||||
*
|
||||
* @param localDateTime LocalDateTime
|
||||
* @return long
|
||||
*/
|
||||
public static long toMilliseconds(final LocalDateTime localDateTime) {
|
||||
return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
/**
|
||||
* localDate 转换成毫秒数
|
||||
*
|
||||
* @param localDate LocalDate
|
||||
* @return long
|
||||
*/
|
||||
public static long toMilliseconds(LocalDate localDate) {
|
||||
return toMilliseconds(localDate.atStartOfDay());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成java8 时间
|
||||
*
|
||||
* @param calendar 日历
|
||||
* @return LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime fromCalendar(final Calendar calendar) {
|
||||
TimeZone tz = calendar.getTimeZone();
|
||||
ZoneId zid = tz == null ? ZoneId.systemDefault() : tz.toZoneId();
|
||||
return LocalDateTime.ofInstant(calendar.toInstant(), zid);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成java8 时间
|
||||
*
|
||||
* @param instant Instant
|
||||
* @return LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime fromInstant(final Instant instant) {
|
||||
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成java8 时间
|
||||
*
|
||||
* @param date Date
|
||||
* @return LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime fromDate(final Date date) {
|
||||
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换成java8 时间
|
||||
*
|
||||
* @param milliseconds 毫秒数
|
||||
* @return LocalDateTime
|
||||
*/
|
||||
public static LocalDateTime fromMilliseconds(final long milliseconds) {
|
||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(milliseconds), ZoneId.systemDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较2个时间差,跨度比较小
|
||||
*
|
||||
* @param startInclusive 开始时间
|
||||
* @param endExclusive 结束时间
|
||||
* @return 时间间隔
|
||||
*/
|
||||
public static Duration between(Temporal startInclusive, Temporal endExclusive) {
|
||||
return Duration.between(startInclusive, endExclusive);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较2个时间差,跨度比较大,年月日为单位
|
||||
*
|
||||
* @param startDate 开始时间
|
||||
* @param endDate 结束时间
|
||||
* @return 时间间隔
|
||||
*/
|
||||
public static Period between(LocalDate startDate, LocalDate endDate) {
|
||||
return Period.between(startDate, endDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较2个 时间差
|
||||
*
|
||||
* @param startDate 开始时间
|
||||
* @param endDate 结束时间
|
||||
* @return 时间间隔
|
||||
*/
|
||||
public static Duration between(Date startDate, Date endDate) {
|
||||
return Duration.between(startDate.toInstant(), endDate.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将秒数转换为日时分秒
|
||||
*
|
||||
* @param second 秒数
|
||||
* @return 时间
|
||||
*/
|
||||
public static String secondToTime(Long second) {
|
||||
// 判断是否为空
|
||||
if (second == null || second == 0L) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
//转换天数
|
||||
long days = second / 86400;
|
||||
//剩余秒数
|
||||
second = second % 86400;
|
||||
//转换小时
|
||||
long hours = second / 3600;
|
||||
//剩余秒数
|
||||
second = second % 3600;
|
||||
//转换分钟
|
||||
long minutes = second / 60;
|
||||
//剩余秒数
|
||||
second = second % 60;
|
||||
if (days > 0) {
|
||||
return StringUtil.format("{}天{}小时{}分{}秒", days, hours, minutes, second);
|
||||
} else {
|
||||
return StringUtil.format("{}小时{}分{}秒", hours, minutes, second);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今天的日期
|
||||
*
|
||||
* @return 时间
|
||||
*/
|
||||
public static String today() {
|
||||
return format(now(), "yyyyMMdd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今天的时间
|
||||
*
|
||||
* @return 时间
|
||||
*/
|
||||
public static String time() {
|
||||
return format(now(), PATTERN_DATETIME_MINI);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今天的小时数
|
||||
*
|
||||
* @return 时间
|
||||
*/
|
||||
public static Integer hour() {
|
||||
return NumberUtil.toInt(format(now(), "HH"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.DESKeySpec;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* DES加解密处理工具
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DesUtil {
|
||||
/**
|
||||
* 数字签名,密钥算法
|
||||
*/
|
||||
public static final String DES_ALGORITHM = "DES";
|
||||
|
||||
/**
|
||||
* 生成 des 密钥
|
||||
*
|
||||
* @return 密钥
|
||||
*/
|
||||
public static String genDesKey() {
|
||||
return StringUtil.random(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data byte array
|
||||
* @param password 密钥
|
||||
* @return des hex
|
||||
*/
|
||||
public static String encryptToHex(byte[] data, String password) {
|
||||
return HexUtil.encodeToString(encrypt(data, password));
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des hex
|
||||
*/
|
||||
@Nullable
|
||||
public static String encryptToHex(@Nullable String data, String password) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
byte[] dataBytes = data.getBytes(Charsets.UTF_8);
|
||||
return encryptToHex(dataBytes, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des context
|
||||
*/
|
||||
@Nullable
|
||||
public static String decryptFormHex(@Nullable String data, String password) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
byte[] hexBytes = HexUtil.decode(data);
|
||||
return new String(decrypt(hexBytes, password), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data byte array
|
||||
* @param password 密钥
|
||||
* @return des hex
|
||||
*/
|
||||
public static String encryptToBase64(byte[] data, String password) {
|
||||
return Base64Util.encodeToString(encrypt(data, password));
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des hex
|
||||
*/
|
||||
@Nullable
|
||||
public static String encryptToBase64(@Nullable String data, String password) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
byte[] dataBytes = data.getBytes(Charsets.UTF_8);
|
||||
return encryptToBase64(dataBytes, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des context
|
||||
*/
|
||||
public static byte[] decryptFormBase64(byte[] data, String password) {
|
||||
byte[] dataBytes = Base64Util.decode(data);
|
||||
return decrypt(dataBytes, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 字符串内容
|
||||
* @param password 密钥
|
||||
* @return des context
|
||||
*/
|
||||
@Nullable
|
||||
public static String decryptFormBase64(@Nullable String data, String password) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
byte[] dataBytes = Base64Util.decodeFromString(data);
|
||||
return new String(decrypt(dataBytes, password), Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data 内容
|
||||
* @param desKey 密钥
|
||||
* @return byte array
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data, byte[] desKey) {
|
||||
return des(data, desKey, Cipher.ENCRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密
|
||||
*
|
||||
* @param data 内容
|
||||
* @param desKey 密钥
|
||||
* @return byte array
|
||||
*/
|
||||
public static byte[] encrypt(byte[] data, String desKey) {
|
||||
return encrypt(data, Objects.requireNonNull(desKey).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 内容
|
||||
* @param desKey 密钥
|
||||
* @return byte array
|
||||
*/
|
||||
public static byte[] decrypt(byte[] data, byte[] desKey) {
|
||||
return des(data, desKey, Cipher.DECRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* DES解密
|
||||
*
|
||||
* @param data 内容
|
||||
* @param desKey 密钥
|
||||
* @return byte array
|
||||
*/
|
||||
public static byte[] decrypt(byte[] data, String desKey) {
|
||||
return decrypt(data, Objects.requireNonNull(desKey).getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* DES加密/解密公共方法
|
||||
*
|
||||
* @param data byte数组
|
||||
* @param desKey 密钥
|
||||
* @param mode 加密:{@link Cipher#ENCRYPT_MODE},解密:{@link Cipher#DECRYPT_MODE}
|
||||
* @return des
|
||||
*/
|
||||
private static byte[] des(byte[] data, byte[] desKey, int mode) {
|
||||
try {
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);
|
||||
Cipher cipher = Cipher.getInstance(DES_ALGORITHM);
|
||||
DESKeySpec desKeySpec = new DESKeySpec(desKey);
|
||||
cipher.init(mode, keyFactory.generateSecret(desKeySpec), Holder.SECURE_RANDOM);
|
||||
return cipher.doFinal(data);
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+304
@@ -0,0 +1,304 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 脱敏工具类
|
||||
*
|
||||
* @author Katrel
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DesensitizationUtil {
|
||||
|
||||
/**
|
||||
* [中文姓名] 只显示第一个汉字,其他隐藏为2个星号(例子:李**)
|
||||
*
|
||||
* @param fullName 全名
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String chineseName(@Nullable final String fullName) {
|
||||
return sensitive(fullName, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。(例子:*************5762)
|
||||
*
|
||||
* @param id 身份证号
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String idCardNum(@Nullable final String id) {
|
||||
return sensitive(id, 0, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* [固定电话] 后四位,其他隐藏(例子:****1234)
|
||||
*
|
||||
* @param num 固定电话号
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String phoneNo(@Nullable final String num) {
|
||||
return sensitive(num, 0, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* [手机号码] 前三位,后四位,其他隐藏(例子:138****1234)
|
||||
*
|
||||
* @param num 手机号
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String mobileNo(@Nullable final String num) {
|
||||
return sensitive(num, 3, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护(例子:北京市海淀区****)
|
||||
*
|
||||
* @param address 地区
|
||||
* @param sensitiveSize 敏感信息长度
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String address(@Nullable final String address, final int sensitiveSize) {
|
||||
return sensitive(address, 0, sensitiveSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示(例子:g**@163.com)
|
||||
*
|
||||
* @param email 邮箱
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String email(@Nullable final String email) {
|
||||
if (email == null) {
|
||||
return null;
|
||||
}
|
||||
if (!StringUtils.hasText(email)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
final int index = email.indexOf(CharPool.AT);
|
||||
if (index <= 1) {
|
||||
return email;
|
||||
} else {
|
||||
return sensitive(email, 1, email.length() - index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号(例子:622260***********1234)
|
||||
*
|
||||
* @param cardNum 银行卡号
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String bankCard(@Nullable final String cardNum) {
|
||||
return sensitive(cardNum, 6, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号(例子:12********)
|
||||
*
|
||||
* @param code 银行联行号
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String cnApsCode(@Nullable final String code) {
|
||||
return sensitive(code, 2, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 右边脱敏
|
||||
*
|
||||
* @param sensitiveStr 待脱敏的字符串
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String right(@Nullable final String sensitiveStr) {
|
||||
if (sensitiveStr == null) {
|
||||
return null;
|
||||
}
|
||||
if (!StringUtils.hasText(sensitiveStr)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
int length = sensitiveStr.length();
|
||||
return sensitive(sensitiveStr, length / 2, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 左边脱敏
|
||||
*
|
||||
* @param sensitiveStr 待脱敏的字符串
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String left(@Nullable final String sensitiveStr) {
|
||||
if (sensitiveStr == null) {
|
||||
return null;
|
||||
}
|
||||
if (!StringUtils.hasText(sensitiveStr)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
int length = sensitiveStr.length();
|
||||
return sensitive(sensitiveStr, 0, length / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 中间脱敏,保留两端
|
||||
*
|
||||
* @param sensitiveStr 待脱敏的字符串
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String middle(@Nullable final String sensitiveStr) {
|
||||
if (sensitiveStr == null) {
|
||||
return null;
|
||||
}
|
||||
if (!StringUtils.hasText(sensitiveStr)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
int length = sensitiveStr.length();
|
||||
if (length < 3) {
|
||||
return StringUtil.leftPad(StringPool.EMPTY, length, CharPool.STAR);
|
||||
} else if (length < 6) {
|
||||
// 小于6个字符,脱敏中间
|
||||
char[] chars = new char[length];
|
||||
int last = length - 1;
|
||||
Arrays.fill(chars, 1, last, CharPool.STAR);
|
||||
chars[0] = sensitiveStr.charAt(0);
|
||||
chars[last] = sensitiveStr.charAt(last);
|
||||
return new String(chars);
|
||||
} else {
|
||||
// 大于6个字符
|
||||
int fromLastLen = length / 3;
|
||||
return sensitive(sensitiveStr, fromLastLen, fromLastLen);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 全部脱敏
|
||||
*
|
||||
* @param sensitiveStr 待脱敏的字符串
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String all(@Nullable final String sensitiveStr) {
|
||||
return sensitive(sensitiveStr, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本脱敏
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param fromIndex 开始的索引
|
||||
* @param lastSize 尾部长度
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String sensitive(@Nullable String str, int fromIndex, int lastSize) {
|
||||
return sensitive(str, fromIndex, lastSize, CharPool.STAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本脱敏
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param fromIndex 开始的索引
|
||||
* @param lastSize 尾部长度
|
||||
* @param padSize 填充的长度
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String sensitive(@Nullable String str, int fromIndex, int lastSize, int padSize) {
|
||||
return sensitive(str, fromIndex, lastSize, CharPool.STAR, padSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本脱敏
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param fromIndex 开始的索引
|
||||
* @param lastSize 尾部长度
|
||||
* @param padChar 填充的字符
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String sensitive(@Nullable String str, int fromIndex, int lastSize, char padChar) {
|
||||
return sensitive(str, fromIndex, lastSize, padChar, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本脱敏
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param fromIndex 开始的索引
|
||||
* @param lastSize 尾部长度
|
||||
* @param padChar 填充的字符
|
||||
* @param padSize 填充的长度
|
||||
* @return 脱敏后的字符串
|
||||
*/
|
||||
@Nullable
|
||||
public static String sensitive(@Nullable String str, int fromIndex, int lastSize, char padChar, int padSize) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
if (!StringUtils.hasText(str)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
int length = str.length();
|
||||
// 全部脱敏
|
||||
if (fromIndex == 0 && lastSize == 0) {
|
||||
int padSiz = padSize > 0 ? padSize : length;
|
||||
return StringUtil.repeat(CharPool.STAR, padSiz);
|
||||
}
|
||||
int toIndex = length - lastSize;
|
||||
int padSiz = padSize > 0 ? padSize : toIndex - fromIndex;
|
||||
// 头部脱敏
|
||||
if (fromIndex == 0) {
|
||||
String tail = str.substring(toIndex);
|
||||
return StringUtil.repeat(padChar, padSiz).concat(tail);
|
||||
}
|
||||
// 尾部脱敏
|
||||
if (toIndex == length) {
|
||||
String head = str.substring(0, fromIndex);
|
||||
return head.concat(StringUtil.repeat(padChar, padSiz));
|
||||
}
|
||||
// 中部
|
||||
String head = str.substring(0, fromIndex);
|
||||
String tail = str.substring(toIndex);
|
||||
return head + StringUtil.repeat(padChar, padSiz) + tail;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* 加密相关工具类直接使用Spring util封装,减少jar依赖
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class DigestUtil extends org.springframework.util.DigestUtils {
|
||||
private static final char[] HEX_CODE = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
/**
|
||||
* Calculates the MD5 digest and returns the value as a 32 character hex string.
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @return MD5 digest as a hex string
|
||||
*/
|
||||
public static String md5Hex(final String data) {
|
||||
return DigestUtils.md5DigestAsHex(data.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a hexadecimal string representation of the MD5 digest of the given bytes.
|
||||
*
|
||||
* @param bytes the bytes to calculate the digest over
|
||||
* @return a hexadecimal digest string
|
||||
*/
|
||||
public static String md5Hex(final byte[] bytes) {
|
||||
return DigestUtils.md5DigestAsHex(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* sha1Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha1Hex(String data) {
|
||||
return DigestUtil.sha1Hex(data.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* sha1Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha1Hex(final byte[] bytes) {
|
||||
return DigestUtil.digestHex("SHA-1", bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA224Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha224Hex(String data) {
|
||||
return DigestUtil.sha224Hex(data.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* SHA224Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha224Hex(final byte[] bytes) {
|
||||
return DigestUtil.digestHex("SHA-224", bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* sha256Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha256Hex(String data) {
|
||||
return DigestUtil.sha256Hex(data.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* sha256Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha256Hex(final byte[] bytes) {
|
||||
return DigestUtil.digestHex("SHA-256", bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* sha384Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha384Hex(String data) {
|
||||
return DigestUtil.sha384Hex(data.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* sha384Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha384Hex(final byte[] bytes) {
|
||||
return DigestUtil.digestHex("SHA-384", bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* sha512Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha512Hex(String data) {
|
||||
return DigestUtil.sha512Hex(data.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* sha512Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String sha512Hex(final byte[] bytes) {
|
||||
return DigestUtil.digestHex("SHA-512", bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* digest Hex
|
||||
*
|
||||
* @param algorithm 算法
|
||||
* @param bytes Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String digestHex(String algorithm, byte[] bytes) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance(algorithm);
|
||||
return encodeHex(md.digest(bytes));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacMd5 Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacMd5Hex(String data, String key) {
|
||||
return DigestUtil.hmacMd5Hex(data.getBytes(Charsets.UTF_8), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacMd5 Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacMd5Hex(final byte[] bytes, String key) {
|
||||
return DigestUtil.digestHMacHex("HmacMD5", bytes, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha1 Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha1Hex(String data, String key) {
|
||||
return DigestUtil.hmacSha1Hex(data.getBytes(Charsets.UTF_8), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha1 Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha1Hex(final byte[] bytes, String key) {
|
||||
return DigestUtil.digestHMacHex("HmacSHA1", bytes, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha224 Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha224Hex(String data, String key) {
|
||||
return DigestUtil.hmacSha224Hex(data.getBytes(Charsets.UTF_8), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha224 Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha224Hex(final byte[] bytes, String key) {
|
||||
return DigestUtil.digestHMacHex("HmacSHA224", bytes, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha256
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static byte[] hmacSha256(String data, String key) {
|
||||
return DigestUtil.hmacSha256(data.getBytes(Charsets.UTF_8), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha256
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @param key key
|
||||
* @return digest as a byte array
|
||||
*/
|
||||
public static byte[] hmacSha256(final byte[] bytes, String key) {
|
||||
return DigestUtil.digestHMac("HmacSHA256", bytes, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha256 Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha256Hex(String data, String key) {
|
||||
return DigestUtil.hmacSha256Hex(data.getBytes(Charsets.UTF_8), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha256 Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha256Hex(final byte[] bytes, String key) {
|
||||
return DigestUtil.digestHMacHex("HmacSHA256", bytes, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha384 Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha384Hex(String data, String key) {
|
||||
return DigestUtil.hmacSha384Hex(data.getBytes(Charsets.UTF_8), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha384 Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha384Hex(final byte[] bytes, String key) {
|
||||
return DigestUtil.digestHMacHex("HmacSHA384", bytes, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha512 Hex
|
||||
*
|
||||
* @param data Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha512Hex(String data, String key) {
|
||||
return DigestUtil.hmacSha512Hex(data.getBytes(Charsets.UTF_8), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmacSha512 Hex
|
||||
*
|
||||
* @param bytes Data to digest
|
||||
* @param key key
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String hmacSha512Hex(final byte[] bytes, String key) {
|
||||
return DigestUtil.digestHMacHex("HmacSHA512", bytes, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* digest HMac Hex
|
||||
*
|
||||
* @param algorithm 算法
|
||||
* @param bytes Data to digest
|
||||
* @return digest as a hex string
|
||||
*/
|
||||
public static String digestHMacHex(String algorithm, final byte[] bytes, String key) {
|
||||
SecretKey secretKey = new SecretKeySpec(key.getBytes(Charsets.UTF_8), algorithm);
|
||||
try {
|
||||
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
|
||||
mac.init(secretKey);
|
||||
return DigestUtil.encodeHex(mac.doFinal(bytes));
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* digest HMac
|
||||
*
|
||||
* @param algorithm 算法
|
||||
* @param bytes Data to digest
|
||||
* @return digest as a byte array
|
||||
*/
|
||||
public static byte[] digestHMac(String algorithm, final byte[] bytes, String key) {
|
||||
SecretKey secretKey = new SecretKeySpec(key.getBytes(Charsets.UTF_8), algorithm);
|
||||
try {
|
||||
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
|
||||
mac.init(secretKey);
|
||||
return mac.doFinal(bytes);
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param bytes Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
public static String encodeHex(byte[] bytes) {
|
||||
StringBuilder r = new StringBuilder(bytes.length * 2);
|
||||
for (byte b : bytes) {
|
||||
r.append(HEX_CODE[(b >> 4) & 0xF]);
|
||||
r.append(HEX_CODE[(b & 0xF)]);
|
||||
}
|
||||
return r.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* decode Hex
|
||||
*
|
||||
* @param hexStr Hex string
|
||||
* @return decode hex to bytes
|
||||
*/
|
||||
public static byte[] decodeHex(final String hexStr) {
|
||||
return DatatypeConverterUtil.parseHexBinary(hexStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较字符串,避免字符串因为过长,产生耗时
|
||||
*
|
||||
* @param a String
|
||||
* @param b String
|
||||
* @return 是否相同
|
||||
*/
|
||||
public static boolean slowEquals(@Nullable String a, @Nullable String b) {
|
||||
if (a == null || b == null) {
|
||||
return false;
|
||||
}
|
||||
return DigestUtil.slowEquals(a.getBytes(Charsets.UTF_8), b.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较 byte 数组,避免字符串因为过长,产生耗时
|
||||
*
|
||||
* @param a byte array
|
||||
* @param b byte array
|
||||
* @return 是否相同
|
||||
*/
|
||||
public static boolean slowEquals(@Nullable byte[] a, @Nullable byte[] b) {
|
||||
if (a == null || b == null) {
|
||||
return false;
|
||||
}
|
||||
if (a.length != b.length) {
|
||||
return false;
|
||||
}
|
||||
int diff = a.length ^ b.length;
|
||||
for (int i = 0; i < a.length; i++) {
|
||||
diff |= a[i] ^ b[i];
|
||||
}
|
||||
return diff == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义加密 将前端传递的密码再次加密
|
||||
*
|
||||
* @param data 数据
|
||||
* @return {String}
|
||||
*/
|
||||
public static String hex(String data) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
return sha1Hex(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户密码加密规则 先MD5再SHA1
|
||||
*
|
||||
* @param data 数据
|
||||
* @return {String}
|
||||
*/
|
||||
public static String encrypt(String data) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
return sha1Hex(md5Hex(data));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springblade.core.tool.support.FastStringWriter;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
|
||||
/**
|
||||
* 异常处理工具类
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class Exceptions {
|
||||
|
||||
/**
|
||||
* 将CheckedException转换为UncheckedException.
|
||||
*
|
||||
* @param e Throwable
|
||||
* @return {RuntimeException}
|
||||
*/
|
||||
public static RuntimeException unchecked(Throwable e) {
|
||||
if (e instanceof Error) {
|
||||
throw (Error) e;
|
||||
} else if (e instanceof IllegalAccessException ||
|
||||
e instanceof IllegalArgumentException ||
|
||||
e instanceof NoSuchMethodException) {
|
||||
return new IllegalArgumentException(e);
|
||||
} else if (e instanceof InvocationTargetException) {
|
||||
return new RuntimeException(((InvocationTargetException) e).getTargetException());
|
||||
} else if (e instanceof RuntimeException) {
|
||||
return (RuntimeException) e;
|
||||
} else if (e instanceof InterruptedException) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return Exceptions.runtime(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 不采用 RuntimeException 包装,直接抛出,使异常更加精准
|
||||
*
|
||||
* @param throwable Throwable
|
||||
* @param <T> 泛型标记
|
||||
* @return Throwable
|
||||
* @throws T 泛型
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Throwable> T runtime(Throwable throwable) throws T {
|
||||
throw (T) throwable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 代理异常解包
|
||||
*
|
||||
* @param wrapped 包装过得异常
|
||||
* @return 解包后的异常
|
||||
*/
|
||||
public static Throwable unwrap(Throwable wrapped) {
|
||||
Throwable unwrapped = wrapped;
|
||||
while (true) {
|
||||
if (unwrapped instanceof InvocationTargetException) {
|
||||
unwrapped = ((InvocationTargetException) unwrapped).getTargetException();
|
||||
} else if (unwrapped instanceof UndeclaredThrowableException) {
|
||||
unwrapped = ((UndeclaredThrowableException) unwrapped).getUndeclaredThrowable();
|
||||
} else {
|
||||
return unwrapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将ErrorStack转化为String.
|
||||
*
|
||||
* @param ex Throwable
|
||||
* @return {String}
|
||||
*/
|
||||
public static String getStackTraceAsString(Throwable ex) {
|
||||
FastStringWriter stringWriter = new FastStringWriter();
|
||||
ex.printStackTrace(new PrintWriter(stringWriter));
|
||||
return stringWriter.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件工具类
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class FileUtil extends org.springframework.util.FileCopyUtils {
|
||||
/**
|
||||
* 特殊后缀
|
||||
*/
|
||||
private static final CharSequence[] SPECIAL_SUFFIX = {"tar.bz2", "tar.Z", "tar.gz", "tar.xz"};
|
||||
|
||||
/**
|
||||
* 默认为true
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public static class TrueFilter implements FileFilter, Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -6420452043795072619L;
|
||||
|
||||
public final static TrueFilter TRUE = new TrueFilter();
|
||||
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录下的文件
|
||||
*
|
||||
* @param path 路径
|
||||
* @return 文件集合
|
||||
*/
|
||||
public static List<File> list(String path) {
|
||||
File file = new File(path);
|
||||
return list(file, TrueFilter.TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录下的文件
|
||||
*
|
||||
* @param path 路径
|
||||
* @param fileNamePattern 文件名 * 号
|
||||
* @return 文件集合
|
||||
*/
|
||||
public static List<File> list(String path, final String fileNamePattern) {
|
||||
File file = new File(path);
|
||||
return list(file, pathname -> {
|
||||
String fileName = pathname.getName();
|
||||
return PatternMatchUtils.simpleMatch(fileNamePattern, fileName);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录下的文件
|
||||
*
|
||||
* @param path 路径
|
||||
* @param filter 文件过滤
|
||||
* @return 文件集合
|
||||
*/
|
||||
public static List<File> list(String path, FileFilter filter) {
|
||||
File file = new File(path);
|
||||
return list(file, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录下的文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @return 文件集合
|
||||
*/
|
||||
public static List<File> list(File file) {
|
||||
List<File> fileList = new ArrayList<>();
|
||||
return list(file, fileList, TrueFilter.TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录下的文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @param fileNamePattern Spring AntPathMatcher 规则
|
||||
* @return 文件集合
|
||||
*/
|
||||
public static List<File> list(File file, final String fileNamePattern) {
|
||||
List<File> fileList = new ArrayList<>();
|
||||
return list(file, fileList, pathname -> {
|
||||
String fileName = pathname.getName();
|
||||
return PatternMatchUtils.simpleMatch(fileNamePattern, fileName);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录下的文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @param filter 文件过滤
|
||||
* @return 文件集合
|
||||
*/
|
||||
public static List<File> list(File file, FileFilter filter) {
|
||||
List<File> fileList = new ArrayList<>();
|
||||
return list(file, fileList, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录下的文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @param filter 文件过滤
|
||||
* @return 文件集合
|
||||
*/
|
||||
private static List<File> list(File file, List<File> fileList, FileFilter filter) {
|
||||
if (file.isDirectory()) {
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
list(f, fileList, filter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 过滤文件
|
||||
boolean accept = filter.accept(file);
|
||||
if (file.exists() && accept) {
|
||||
fileList.add(file);
|
||||
}
|
||||
}
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件后缀名
|
||||
* @param fullName 文件全名
|
||||
* @return {String}
|
||||
*/
|
||||
public static String getFileExtension(String fullName) {
|
||||
if (StringUtil.isBlank(fullName)) return StringPool.EMPTY;
|
||||
String fileName = new File(fullName).getName();
|
||||
final int index = fileName.lastIndexOf(CharPool.DOT);
|
||||
if (index == -1) {
|
||||
return StringPool.EMPTY;
|
||||
}
|
||||
|
||||
// 特殊文件名称,如tar.gz,tar.bz2,tar.Z,tar.xz
|
||||
final int secondToLastIndex = fileName.substring(0, index).lastIndexOf(CharPool.DOT);
|
||||
final String substr = fileName.substring(secondToLastIndex == -1 ? index : secondToLastIndex + 1);
|
||||
if (StringUtil.containsAny(substr, SPECIAL_SUFFIX)) {
|
||||
return substr;
|
||||
}
|
||||
return fileName.substring(index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名,去除后缀名
|
||||
* @param fullName 文件全名
|
||||
* @return {String}
|
||||
*/
|
||||
public static String getNameWithoutExtension(String fullName) {
|
||||
if (StringUtil.isBlank(fullName)) return StringPool.EMPTY;
|
||||
String fileName = new File(fullName).getName();
|
||||
final int index = fileName.lastIndexOf(CharPool.DOT);
|
||||
if (index == -1) {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
// 检查是否为特殊后缀名
|
||||
int secondToLastIndex = fileName.substring(0, index).lastIndexOf(CharPool.DOT);
|
||||
String substr = fileName.substring(secondToLastIndex == -1? index : secondToLastIndex + 1);
|
||||
if (StringUtil.containsAny(substr, SPECIAL_SUFFIX)) {
|
||||
return fileName.substring(0, secondToLastIndex);
|
||||
}
|
||||
|
||||
return fileName.substring(0, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the system temporary directory.
|
||||
*
|
||||
* @return the path to the system temporary directory.
|
||||
*/
|
||||
public static String getTempDirPath() {
|
||||
return System.getProperty("java.io.tmpdir");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link File} representing the system temporary directory.
|
||||
*
|
||||
* @return the system temporary directory.
|
||||
*/
|
||||
public static File getTempDir() {
|
||||
return new File(getTempDirPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of a file into a String.
|
||||
* The file is always closed.
|
||||
*
|
||||
* @param file the file to read, must not be {@code null}
|
||||
* @return the file contents, never {@code null}
|
||||
*/
|
||||
public static String readToString(final File file) {
|
||||
return readToString(file, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of a file into a String.
|
||||
* The file is always closed.
|
||||
*
|
||||
* @param file the file to read, must not be {@code null}
|
||||
* @param encoding the encoding to use, {@code null} means platform default
|
||||
* @return the file contents, never {@code null}
|
||||
*/
|
||||
public static String readToString(final File file, final Charset encoding) {
|
||||
try (InputStream in = Files.newInputStream(file.toPath())) {
|
||||
return IoUtil.readToString(in, encoding);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of a file into a String.
|
||||
* The file is always closed.
|
||||
*
|
||||
* @param file the file to read, must not be {@code null}
|
||||
* @return the file contents, never {@code null}
|
||||
*/
|
||||
public static byte[] readToByteArray(final File file) {
|
||||
try (InputStream in = Files.newInputStream(file.toPath())) {
|
||||
return IoUtil.readToByteArray(in);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a String to a file creating the file if it does not exist.
|
||||
*
|
||||
* @param file the file to write
|
||||
* @param data the content to write to the file
|
||||
*/
|
||||
public static void writeToFile(final File file, final String data) {
|
||||
writeToFile(file, data, Charsets.UTF_8, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a String to a file creating the file if it does not exist.
|
||||
*
|
||||
* @param file the file to write
|
||||
* @param data the content to write to the file
|
||||
* @param append if {@code true}, then the String will be added to the
|
||||
* end of the file rather than overwriting
|
||||
*/
|
||||
public static void writeToFile(final File file, final String data, final boolean append){
|
||||
writeToFile(file, data, Charsets.UTF_8, append);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a String to a file creating the file if it does not exist.
|
||||
*
|
||||
* @param file the file to write
|
||||
* @param data the content to write to the file
|
||||
* @param encoding the encoding to use, {@code null} means platform default
|
||||
*/
|
||||
public static void writeToFile(final File file, final String data, final Charset encoding) {
|
||||
writeToFile(file, data, encoding, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a String to a file creating the file if it does not exist.
|
||||
*
|
||||
* @param file the file to write
|
||||
* @param data the content to write to the file
|
||||
* @param encoding the encoding to use, {@code null} means platform default
|
||||
* @param append if {@code true}, then the String will be added to the
|
||||
* end of the file rather than overwriting
|
||||
*/
|
||||
public static void writeToFile(final File file, final String data, final Charset encoding, final boolean append) {
|
||||
try (OutputStream out = new FileOutputStream(file, append)) {
|
||||
IoUtil.write(data, out, encoding);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转成file
|
||||
* @param multipartFile MultipartFile
|
||||
* @param file File
|
||||
*/
|
||||
public static void toFile(MultipartFile multipartFile, final File file) {
|
||||
try {
|
||||
FileUtil.toFile(multipartFile.getInputStream(), file);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转成file
|
||||
* @param in InputStream
|
||||
* @param file File
|
||||
*/
|
||||
public static void toFile(InputStream in, final File file) {
|
||||
try (OutputStream out = new FileOutputStream(file)) {
|
||||
FileUtil.copy(in, out);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves a file.
|
||||
* <p>
|
||||
* When the destination file is on another file system, do a "copy and delete".
|
||||
*
|
||||
* @param srcFile the file to be moved
|
||||
* @param destFile the destination file
|
||||
* @throws NullPointerException if source or destination is {@code null}
|
||||
* @throws IOException if source or destination is invalid
|
||||
* @throws IOException if an IO error occurs moving the file
|
||||
*/
|
||||
public static void moveFile(final File srcFile, final File destFile) throws IOException {
|
||||
Assert.notNull(srcFile, "Source must not be null");
|
||||
Assert.notNull(destFile, "Destination must not be null");
|
||||
if (!srcFile.exists()) {
|
||||
throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
|
||||
}
|
||||
if (srcFile.isDirectory()) {
|
||||
throw new IOException("Source '" + srcFile + "' is a directory");
|
||||
}
|
||||
if (destFile.exists()) {
|
||||
throw new IOException("Destination '" + destFile + "' already exists");
|
||||
}
|
||||
if (destFile.isDirectory()) {
|
||||
throw new IOException("Destination '" + destFile + "' is a directory");
|
||||
}
|
||||
final boolean rename = srcFile.renameTo(destFile);
|
||||
if (!rename) {
|
||||
FileUtil.copy(srcFile, destFile);
|
||||
if (!srcFile.delete()) {
|
||||
FileUtil.deleteQuietly(destFile);
|
||||
throw new IOException("Failed to delete original file '" + srcFile + "' after copy to '" + destFile + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
|
||||
* <p>
|
||||
* The difference between File.delete() and this method are:
|
||||
* <ul>
|
||||
* <li>A directory to be deleted does not have to be empty.</li>
|
||||
* <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param file file or directory to delete, can be {@code null}
|
||||
* @return {@code true} if the file or directory was deleted, otherwise
|
||||
* {@code false}
|
||||
*/
|
||||
public static boolean deleteQuietly(@Nullable final File file) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
if (file.isDirectory()) {
|
||||
FileSystemUtils.deleteRecursively(file);
|
||||
}
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
return file.delete();
|
||||
} catch (final Exception ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* hex 工具,编解码全用 byte
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class HexUtil {
|
||||
public static final Charset DEFAULT_CHARSET = Charsets.UTF_8;
|
||||
private static final byte[] DIGITS_LOWER = new byte[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
private static final byte[] DIGITS_UPPER = new byte[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data data to hex
|
||||
* @return hex bytes
|
||||
*/
|
||||
public static byte[] encode(byte[] data) {
|
||||
return encode(data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data data to hex
|
||||
* @param toLowerCase 是否小写
|
||||
* @return hex bytes
|
||||
*/
|
||||
public static byte[] encode(byte[] data, boolean toLowerCase) {
|
||||
return encode(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
private static byte[] encode(byte[] data, byte[] digits) {
|
||||
int len = data.length;
|
||||
byte[] out = new byte[len << 1];
|
||||
for (int i = 0, j = 0; i < len; i++) {
|
||||
out[j++] = digits[(0xF0 & data[i]) >>> 4];
|
||||
out[j++] = digits[0xF & data[i]];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @param toLowerCase 是否小写
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
public static String encodeToString(byte[] data, boolean toLowerCase) {
|
||||
return new String(encode(data, toLowerCase), DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
public static String encodeToString(byte[] data) {
|
||||
return encodeToString(data, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @param charset Charset
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
public static String encodeToString(byte[] data, Charset charset) {
|
||||
return new String(encode(data), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* encode Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
@Nullable
|
||||
public static String encodeToString(@Nullable String data) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
return encodeToString(data.getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
/**
|
||||
* decode Hex
|
||||
*
|
||||
* @param data Hex data
|
||||
* @return decode hex to bytes
|
||||
*/
|
||||
public static byte[] decode(String data) {
|
||||
return decode(data, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* decode Hex
|
||||
*
|
||||
* @param data Hex data
|
||||
* @param charset Charset
|
||||
* @return decode hex to bytes
|
||||
*/
|
||||
public static byte[] decode(String data, Charset charset) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
return decode(data.getBytes(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* decodeToString Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
public static String decodeToString(byte[] data) {
|
||||
byte[] decodeBytes = decode(data);
|
||||
return new String(decodeBytes, DEFAULT_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* decodeToString Hex
|
||||
*
|
||||
* @param data Data to Hex
|
||||
* @return bytes as a hex string
|
||||
*/
|
||||
@Nullable
|
||||
public static String decodeToString(@Nullable String data) {
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return null;
|
||||
}
|
||||
return decodeToString(data.getBytes(DEFAULT_CHARSET));
|
||||
}
|
||||
|
||||
/**
|
||||
* decode Hex
|
||||
*
|
||||
* @param data Hex data
|
||||
* @return decode hex to bytes
|
||||
*/
|
||||
public static byte[] decode(byte[] data) {
|
||||
int len = data.length;
|
||||
if ((len & 0x01) != 0) {
|
||||
throw new IllegalArgumentException("hexBinary needs to be even-length: " + len);
|
||||
}
|
||||
byte[] out = new byte[len >> 1];
|
||||
for (int i = 0, j = 0; j < len; i++) {
|
||||
int f = toDigit(data[j], j) << 4;
|
||||
j++;
|
||||
f |= toDigit(data[j], j);
|
||||
j++;
|
||||
out[i] = (byte) (f & 0xFF);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static int toDigit(byte b, int index) {
|
||||
int digit = Character.digit(b, 16);
|
||||
if (digit == -1) {
|
||||
throw new IllegalArgumentException("Illegal hexadecimal byte " + b + " at index " + index);
|
||||
}
|
||||
return digit;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 一些常用的单利对象
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class Holder {
|
||||
|
||||
/**
|
||||
* RANDOM
|
||||
*/
|
||||
public final static Random RANDOM = new Random();
|
||||
|
||||
/**
|
||||
* SECURE_RANDOM
|
||||
*/
|
||||
public final static SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.support.IMultiOutputStream;
|
||||
import org.springblade.core.tool.support.ImagePosition;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.color.ColorSpace;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.*;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* 图片工具类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
public final class ImageUtil {
|
||||
|
||||
/**
|
||||
* 默认输出图片类型
|
||||
*/
|
||||
public static final String DEFAULT_IMG_TYPE = "JPEG";
|
||||
|
||||
/**
|
||||
* 转换输入流到byte
|
||||
*
|
||||
* @param src 源
|
||||
* @param type 类型
|
||||
* @return byte[]
|
||||
* @throws IOException 异常
|
||||
*/
|
||||
public static byte[] toByteArray(BufferedImage src, String type) throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
ImageIO.write(src, defaultString(type, DEFAULT_IMG_TYPE), os);
|
||||
return os.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图像内容
|
||||
*
|
||||
* @param srcImageFile 文件路径
|
||||
* @return BufferedImage
|
||||
*/
|
||||
public static BufferedImage readImage(String srcImageFile) {
|
||||
try {
|
||||
return ImageIO.read(new File(srcImageFile));
|
||||
} catch (IOException e) {
|
||||
log.error("Error readImage", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图像内容
|
||||
*
|
||||
* @param srcImageFile 文件
|
||||
* @return BufferedImage
|
||||
*/
|
||||
public static BufferedImage readImage(File srcImageFile) {
|
||||
try {
|
||||
return ImageIO.read(srcImageFile);
|
||||
} catch (IOException e) {
|
||||
log.error("Error readImage", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图像内容
|
||||
*
|
||||
* @param srcInputStream 输入流
|
||||
* @return BufferedImage
|
||||
*/
|
||||
public static BufferedImage readImage(InputStream srcInputStream) {
|
||||
try {
|
||||
return ImageIO.read(srcInputStream);
|
||||
} catch (IOException e) {
|
||||
log.error("Error readImage", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图像内容
|
||||
*
|
||||
* @param url URL地址
|
||||
* @return BufferedImage
|
||||
*/
|
||||
public static BufferedImage readImage(URL url) {
|
||||
try {
|
||||
return ImageIO.read(url);
|
||||
} catch (IOException e) {
|
||||
log.error("Error readImage", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 缩放图像(按比例缩放)
|
||||
*
|
||||
* @param src 源图像
|
||||
* @param output 输出流
|
||||
* @param type 类型
|
||||
* @param scale 缩放比例
|
||||
* @param flag 缩放选择:true 放大; false 缩小;
|
||||
*/
|
||||
public static void zoomScale(BufferedImage src, OutputStream output, String type, double scale, boolean flag) {
|
||||
try {
|
||||
// 得到源图宽
|
||||
int width = src.getWidth();
|
||||
// 得到源图长
|
||||
int height = src.getHeight();
|
||||
if (flag) {
|
||||
// 放大
|
||||
width = Long.valueOf(Math.round(width * scale)).intValue();
|
||||
height = Long.valueOf(Math.round(height * scale)).intValue();
|
||||
} else {
|
||||
// 缩小
|
||||
width = Long.valueOf(Math.round(width / scale)).intValue();
|
||||
height = Long.valueOf(Math.round(height / scale)).intValue();
|
||||
}
|
||||
Image image = src.getScaledInstance(width, height, Image.SCALE_DEFAULT);
|
||||
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = tag.getGraphics();
|
||||
|
||||
g.drawImage(image, 0, 0, null);
|
||||
g.dispose();
|
||||
|
||||
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), output);
|
||||
|
||||
output.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Error in zoom image", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放图像(按高度和宽度缩放)
|
||||
*
|
||||
* @param src 源图像
|
||||
* @param output 输出流
|
||||
* @param type 类型
|
||||
* @param height 缩放后的高度
|
||||
* @param width 缩放后的宽度
|
||||
* @param bb 比例不对时是否需要补白:true为补白; false为不补白;
|
||||
* @param fillColor 填充色,null时为Color.WHITE
|
||||
*/
|
||||
public static void zoomFixed(BufferedImage src, OutputStream output, String type, int height, int width, boolean bb, Color fillColor) {
|
||||
try {
|
||||
double ratio = 0.0;
|
||||
Image itemp = src.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
|
||||
// 计算比例
|
||||
if (src.getHeight() > src.getWidth()) {
|
||||
ratio = Integer.valueOf(height).doubleValue() / src.getHeight();
|
||||
} else {
|
||||
ratio = Integer.valueOf(width).doubleValue() / src.getWidth();
|
||||
}
|
||||
AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
|
||||
itemp = op.filter(src, null);
|
||||
|
||||
if (bb) {
|
||||
//补白
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = image.createGraphics();
|
||||
Color fill = fillColor == null ? Color.white : fillColor;
|
||||
g.setColor(fill);
|
||||
g.fillRect(0, 0, width, height);
|
||||
if (width == itemp.getWidth(null)) {
|
||||
g.drawImage(itemp, 0, (height - itemp.getHeight(null)) / 2, itemp.getWidth(null), itemp.getHeight(null), fill, null);
|
||||
} else {
|
||||
g.drawImage(itemp, (width - itemp.getWidth(null)) / 2, 0, itemp.getWidth(null), itemp.getHeight(null), fill, null);
|
||||
}
|
||||
g.dispose();
|
||||
itemp = image;
|
||||
}
|
||||
// 输出为文件
|
||||
ImageIO.write((BufferedImage) itemp, defaultString(type, DEFAULT_IMG_TYPE), output);
|
||||
// 关闭流
|
||||
output.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Error in zoom image", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像裁剪(按指定起点坐标和宽高切割)
|
||||
*
|
||||
* @param src 源图像
|
||||
* @param output 切片后的图像地址
|
||||
* @param type 类型
|
||||
* @param x 目标切片起点坐标X
|
||||
* @param y 目标切片起点坐标Y
|
||||
* @param width 目标切片宽度
|
||||
* @param height 目标切片高度
|
||||
*/
|
||||
public static void crop(BufferedImage src, OutputStream output, String type, int x, int y, int width, int height) {
|
||||
try {
|
||||
// 源图宽度
|
||||
int srcWidth = src.getWidth();
|
||||
// 源图高度
|
||||
int srcHeight = src.getHeight();
|
||||
if (srcWidth > 0 && srcHeight > 0) {
|
||||
Image image = src.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
|
||||
// 四个参数分别为图像起点坐标和宽高
|
||||
ImageFilter cropFilter = new CropImageFilter(x, y, width, height);
|
||||
Image img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
|
||||
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = tag.getGraphics();
|
||||
g.drawImage(img, 0, 0, width, height, null);
|
||||
g.dispose();
|
||||
// 输出为文件
|
||||
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), output);
|
||||
// 关闭流
|
||||
output.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error in cut image", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像切割(指定切片的行数和列数)
|
||||
*
|
||||
* @param src 源图像地址
|
||||
* @param mos 切片目标文件夹
|
||||
* @param type 类型
|
||||
* @param prows 目标切片行数。默认2,必须是范围 [1, 20] 之内
|
||||
* @param pcols 目标切片列数。默认2,必须是范围 [1, 20] 之内
|
||||
*/
|
||||
public static void sliceWithNumber(BufferedImage src, IMultiOutputStream mos, String type, int prows, int pcols) {
|
||||
try {
|
||||
int rows = prows <= 0 || prows > 20 ? 2 : prows;
|
||||
int cols = pcols <= 0 || pcols > 20 ? 2 : pcols;
|
||||
// 源图宽度
|
||||
int srcWidth = src.getWidth();
|
||||
// 源图高度
|
||||
int srcHeight = src.getHeight();
|
||||
if (srcWidth > 0 && srcHeight > 0) {
|
||||
Image img;
|
||||
ImageFilter cropFilter;
|
||||
Image image = src.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
|
||||
// 每张切片的宽度
|
||||
int destWidth = (srcWidth % cols == 0) ? (srcWidth / cols) : (srcWidth / cols + 1);
|
||||
// 每张切片的高度
|
||||
int destHeight = (srcHeight % rows == 0) ? (srcHeight / rows) : (srcHeight / rows + 1);
|
||||
// 循环建立切片
|
||||
// 改进的想法:是否可用多线程加快切割速度
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
// 四个参数分别为图像起点坐标和宽高
|
||||
cropFilter = new CropImageFilter(j * destWidth, i * destHeight, destWidth, destHeight);
|
||||
img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
|
||||
BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = tag.getGraphics();
|
||||
// 绘制缩小后的图
|
||||
g.drawImage(img, 0, 0, null);
|
||||
g.dispose();
|
||||
// 输出为文件
|
||||
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), mos.buildOutputStream(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error in slice image", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像切割(指定切片的宽度和高度)
|
||||
*
|
||||
* @param src 源图像地址
|
||||
* @param mos 切片目标文件夹
|
||||
* @param type 类型
|
||||
* @param pdestWidth 目标切片宽度。默认200
|
||||
* @param pdestHeight 目标切片高度。默认150
|
||||
*/
|
||||
public static void sliceWithSize(BufferedImage src, IMultiOutputStream mos, String type, int pdestWidth, int pdestHeight) {
|
||||
try {
|
||||
int destWidth = pdestWidth <= 0 ? 200 : pdestWidth;
|
||||
int destHeight = pdestHeight <= 0 ? 150 : pdestHeight;
|
||||
// 源图宽度
|
||||
int srcWidth = src.getWidth();
|
||||
// 源图高度
|
||||
int srcHeight = src.getHeight();
|
||||
if (srcWidth > destWidth && srcHeight > destHeight) {
|
||||
Image img;
|
||||
ImageFilter cropFilter;
|
||||
Image image = src.getScaledInstance(srcWidth, srcHeight, Image.SCALE_DEFAULT);
|
||||
// 切片横向数量
|
||||
int cols = (srcWidth % destWidth == 0) ? (srcWidth / destWidth) : (srcWidth / destWidth + 1);
|
||||
// 切片纵向数量
|
||||
int rows = (srcHeight % destHeight == 0) ? (srcHeight / destHeight) : (srcHeight / destHeight + 1);
|
||||
// 循环建立切片
|
||||
// 改进的想法:是否可用多线程加快切割速度
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
// 四个参数分别为图像起点坐标和宽高
|
||||
cropFilter = new CropImageFilter(j * destWidth, i * destHeight, destWidth, destHeight);
|
||||
img = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), cropFilter));
|
||||
BufferedImage tag = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = tag.getGraphics();
|
||||
// 绘制缩小后的图
|
||||
g.drawImage(img, 0, 0, null);
|
||||
g.dispose();
|
||||
// 输出为文件
|
||||
ImageIO.write(tag, defaultString(type, DEFAULT_IMG_TYPE), mos.buildOutputStream(i, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Error in slice image", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像类型转换:GIF-JPG、GIF-PNG、PNG-JPG、PNG-GIF(X)、BMP-PNG
|
||||
*
|
||||
* @param src 源图像地址
|
||||
* @param formatName 包含格式非正式名称的 String:如JPG、JPEG、GIF等
|
||||
* @param output 目标图像地址
|
||||
*/
|
||||
public static void convert(BufferedImage src, OutputStream output, String formatName) {
|
||||
try {
|
||||
// 输出为文件
|
||||
ImageIO.write(src, formatName, output);
|
||||
// 关闭流
|
||||
output.close();
|
||||
} catch (Exception e) {
|
||||
log.error("Error in convert image", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 彩色转为黑白
|
||||
*
|
||||
* @param src 源图像地址
|
||||
* @param output 目标图像地址
|
||||
* @param type 类型
|
||||
*/
|
||||
public static void gray(BufferedImage src, OutputStream output, String type) {
|
||||
try {
|
||||
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
|
||||
ColorConvertOp op = new ColorConvertOp(cs, null);
|
||||
src = op.filter(src, null);
|
||||
// 输出为文件
|
||||
ImageIO.write(src, defaultString(type, DEFAULT_IMG_TYPE), output);
|
||||
// 关闭流
|
||||
output.close();
|
||||
} catch (IOException e) {
|
||||
log.error("Error in gray image", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给图片添加文字水印
|
||||
*
|
||||
* @param src 源图像
|
||||
* @param output 输出流
|
||||
* @param type 类型
|
||||
* @param text 水印文字
|
||||
* @param font 水印的字体
|
||||
* @param color 水印的字体颜色
|
||||
* @param position 水印位置 {@link ImagePosition}
|
||||
* @param x 修正值
|
||||
* @param y 修正值
|
||||
* @param alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
|
||||
*/
|
||||
public static void textStamp(BufferedImage src, OutputStream output, String type, String text, Font font, Color color
|
||||
, int position, int x, int y, float alpha) {
|
||||
try {
|
||||
int width = src.getWidth(null);
|
||||
int height = src.getHeight(null);
|
||||
BufferedImage image = new BufferedImage(width, height,
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = image.createGraphics();
|
||||
g.drawImage(src, 0, 0, width, height, null);
|
||||
g.setColor(color);
|
||||
g.setFont(font);
|
||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
|
||||
// 在指定坐标绘制水印文字
|
||||
ImagePosition boxPos = new ImagePosition(width, height, calcTextWidth(text) * font.getSize(), font.getSize(), position);
|
||||
g.drawString(text, boxPos.getX(x), boxPos.getY(y));
|
||||
g.dispose();
|
||||
// 输出为文件
|
||||
ImageIO.write((BufferedImage) image, defaultString(type, DEFAULT_IMG_TYPE), output);
|
||||
// 关闭流
|
||||
output.close();
|
||||
} catch (Exception e) {
|
||||
log.error("Error in textStamp image", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给图片添加图片水印
|
||||
*
|
||||
* @param src 源图像
|
||||
* @param output 输出流
|
||||
* @param type 类型
|
||||
* @param stamp 水印图片
|
||||
* @param position 水印位置 {@link ImagePosition}
|
||||
* @param x 修正值
|
||||
* @param y 修正值
|
||||
* @param alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
|
||||
*/
|
||||
public static void imageStamp(BufferedImage src, OutputStream output, String type, BufferedImage stamp
|
||||
, int position, int x, int y, float alpha) {
|
||||
try {
|
||||
int width = src.getWidth();
|
||||
int height = src.getHeight();
|
||||
BufferedImage image = new BufferedImage(width, height,
|
||||
BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = image.createGraphics();
|
||||
g.drawImage(src, 0, 0, width, height, null);
|
||||
// 水印文件
|
||||
int stampWidth = stamp.getWidth();
|
||||
int stampHeight = stamp.getHeight();
|
||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
|
||||
ImagePosition boxPos = new ImagePosition(width, height, stampWidth, stampHeight, position);
|
||||
g.drawImage(stamp, boxPos.getX(x), boxPos.getY(y), stampWidth, stampHeight, null);
|
||||
// 水印文件结束
|
||||
g.dispose();
|
||||
// 输出为文件
|
||||
ImageIO.write((BufferedImage) image, defaultString(type, DEFAULT_IMG_TYPE), output);
|
||||
// 关闭流
|
||||
output.close();
|
||||
} catch (Exception e) {
|
||||
log.error("Error imageStamp", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算text的长度(一个中文算两个字符)
|
||||
*
|
||||
* @param text text
|
||||
* @return int
|
||||
*/
|
||||
public static int calcTextWidth(String text) {
|
||||
int length = 0;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
if ((text.charAt(i) + "").getBytes().length > 1) {
|
||||
length += 2;
|
||||
} else {
|
||||
length += 1;
|
||||
}
|
||||
}
|
||||
return length / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param defaultStr 默认值
|
||||
* @return String
|
||||
*/
|
||||
public static String defaultString(String str, String defaultStr) {
|
||||
return ((str == null) ? defaultStr : str);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
/**
|
||||
* 静态 Integer 池.
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IntegerPool {
|
||||
|
||||
Integer INT_1024 = 1024;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: DreamLu (596392912@qq.com)
|
||||
*/
|
||||
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 流工具类
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public class IoUtil extends org.springframework.util.StreamUtils {
|
||||
|
||||
/**
|
||||
* closeQuietly
|
||||
*
|
||||
* @param closeable 自动关闭
|
||||
*/
|
||||
public static void closeQuietly(@Nullable Closeable closeable) {
|
||||
if (closeable == null) {
|
||||
return;
|
||||
}
|
||||
if (closeable instanceof Flushable) {
|
||||
try {
|
||||
((Flushable) closeable).flush();
|
||||
} catch (IOException ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (IOException ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* InputStream to String utf-8
|
||||
*
|
||||
* @param input the <code>InputStream</code> to read from
|
||||
* @return the requested String
|
||||
*/
|
||||
public static String readToString(InputStream input) {
|
||||
return readToString(input, Charsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* InputStream to String
|
||||
*
|
||||
* @param input the <code>InputStream</code> to read from
|
||||
* @param charset the <code>Charset</code>
|
||||
* @return the requested String
|
||||
*/
|
||||
public static String readToString(@Nullable InputStream input, Charset charset) {
|
||||
try {
|
||||
return IoUtil.copyToString(input, charset);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] readToByteArray(@Nullable InputStream input) {
|
||||
try {
|
||||
return IoUtil.copyToByteArray(input);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.unchecked(e);
|
||||
} finally {
|
||||
IoUtil.closeQuietly(input);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes chars from a <code>String</code> to bytes on an
|
||||
* <code>OutputStream</code> using the specified character encoding.
|
||||
* <p>
|
||||
* This method uses {@link String#getBytes(String)}.
|
||||
* </p>
|
||||
* @param data the <code>String</code> to write, null ignored
|
||||
* @param output the <code>OutputStream</code> to write to
|
||||
* @param encoding the encoding to use, null means platform default
|
||||
* @throws NullPointerException if output is null
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public static void write(@Nullable final String data, final OutputStream output, final Charset encoding) throws IOException {
|
||||
if (data != null) {
|
||||
output.write(data.getBytes(encoding));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user