Explorar el Código

bump: 发布1.6.0

* chore: 升级至 1.6.0 及部分依赖版本

* feat: 新增 StreamFunction; 修改 PageResult 字段类型

* chore: 去除 helio-starter-cloud 默认引入的 sentinel

* feat: Dubbo 附件直接使用可序列化的对象

* feat: 增加单独的`clear`方法专门用于清空`ThreadLocal`中数据

* feat: `HelioBaseEnum#assertNotEmpty`方法首参数类型更改为更抽象的`Iterable<?>`

* pref: 在未切换数据源时,`finally`不将数据源出栈

* bump: 依赖升级

* bump: 发布1.6.0
Uncarbon hace 4 años
padre
commit
83761719ce

+ 5 - 18
helio-core/src/main/java/cc/uncarbon/framework/core/constant/HelioConstant.java

@@ -1,8 +1,10 @@
 package cc.uncarbon.framework.core.constant;
 
-
-import java.util.Locale;
-
+/**
+ * Helio 基础常量定义
+ *
+ * @author Uncarbon
+ */
 public interface HelioConstant {
     interface Dubbo {
         String ENABLE_VALIDATION = "true";
@@ -29,22 +31,12 @@ public interface HelioConstant {
     }
 
     interface Jackson {
-        @Deprecated
-        Locale LOCALE = Locale.CHINA;
-        @Deprecated
-        String TIME_ZONE = "GMT+8";
         String DATE_FORMAT = "yyyy-MM-dd";
         String TIME_FORMAT = "HH:mm:ss";
         String DATE_TIME_FORMAT = Jackson.DATE_FORMAT + " " + Jackson.TIME_FORMAT;
     }
 
     interface CRUD {
-        /**
-         * 该租户ID为超级租户, 可以无视租户SQL拦截器
-         */
-        @Deprecated
-        Long DEFAULT_PRIVILEGED_TENANT_ID = Tenant.DEFAULT_PRIVILEGED_TENANT_ID;
-
         /**
          * 租户ID
          */
@@ -80,11 +72,6 @@ public interface HelioConstant {
          */
         String SQL_LIMIT_1 = " LIMIT 1";
 
-        /**
-         * SQL 列名 id
-         */
-        String SQL_COLUMN_ID = " id ";
-
     }
 
     interface Regex {

+ 14 - 7
helio-core/src/main/java/cc/uncarbon/framework/core/context/TenantContextHolder.java

@@ -11,7 +11,7 @@ import lombok.experimental.UtilityClass;
 @UtilityClass
 public class TenantContextHolder {
 
-    private final TransmittableThreadLocal<TenantContext> THREAD_LOCAL_TENANT = new TransmittableThreadLocal<>();
+    private final TransmittableThreadLocal<TenantContext> THREAD_LOCAL_CONTEXT = new TransmittableThreadLocal<>();
 
 
     /**
@@ -21,21 +21,28 @@ public class TenantContextHolder {
      * @return null or 当前租户上下文
      */
     public TenantContext getTenantContext() throws NullPointerException {
-        return THREAD_LOCAL_TENANT.get();
+        return THREAD_LOCAL_CONTEXT.get();
     }
 
     /**
      * 设置当前租户上下文
      *
-     * @param tenantContext 新上下文,传 null 则为清除
+     * @param newContext 新上下文,传 null 则为清除
      */
-    public void setTenantContext(TenantContext tenantContext) {
-        if (tenantContext == null) {
-            THREAD_LOCAL_TENANT.remove();
+    public synchronized void setTenantContext(TenantContext newContext) {
+        if (newContext == null) {
+            THREAD_LOCAL_CONTEXT.remove();
             return;
         }
 
-        THREAD_LOCAL_TENANT.set(tenantContext);
+        THREAD_LOCAL_CONTEXT.set(newContext);
+    }
+
+    /**
+     * 强制清空本线程的租户上下文,防止影响被线程池复用的其他线程,以及内存泄露
+     */
+    public void clear() {
+        setTenantContext(null);
     }
 
     /**

+ 14 - 7
helio-core/src/main/java/cc/uncarbon/framework/core/context/UserContextHolder.java

@@ -11,7 +11,7 @@ import lombok.experimental.UtilityClass;
 @UtilityClass
 public class UserContextHolder {
 
-    private final TransmittableThreadLocal<UserContext> THREAD_LOCAL_USER = new TransmittableThreadLocal<>();
+    private final TransmittableThreadLocal<UserContext> THREAD_LOCAL_CONTEXT = new TransmittableThreadLocal<>();
 
 
     /**
@@ -21,21 +21,28 @@ public class UserContextHolder {
      * @return null or 当前用户上下文
      */
     public UserContext getUserContext() throws NullPointerException {
-        return THREAD_LOCAL_USER.get();
+        return THREAD_LOCAL_CONTEXT.get();
     }
 
     /**
      * 设置当前用户上下文
      *
-     * @param userContext 新上下文,传 null 则为清除
+     * @param newContext 新上下文,传 null 则为清除
      */
-    public void setUserContext(UserContext userContext) {
-        if (userContext == null) {
-            THREAD_LOCAL_USER.remove();
+    public synchronized void setUserContext(UserContext newContext) {
+        if (newContext == null) {
+            THREAD_LOCAL_CONTEXT.remove();
             return;
         }
 
-        THREAD_LOCAL_USER.set(userContext);
+        THREAD_LOCAL_CONTEXT.set(newContext);
+    }
+
+    /**
+     * 强制清空本线程的用户上下文,防止影响被线程池复用的其他线程,以及内存泄露
+     */
+    public void clear() {
+        setUserContext(null);
     }
 
     /**

+ 8 - 9
helio-core/src/main/java/cc/uncarbon/framework/core/enums/HelioBaseEnum.java

@@ -1,13 +1,12 @@
 package cc.uncarbon.framework.core.enums;
 
 import cc.uncarbon.framework.core.exception.BusinessException;
-import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.IterUtil;
 import cn.hutool.core.util.NumberUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import java.io.Serializable;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Optional;
 import java.util.function.Predicate;
 
@@ -170,11 +169,11 @@ public interface HelioBaseEnum<T> extends Serializable {
      * 断言不为空文本
      * 注意:枚举选项的 value 建议为整数类型
      *
-     * @param str 需要判断的对象
+     * @param cs 需要判断的对象
      * @param templateParams label 中如果有占位符的话,向里面填充的模板参数
      */
-    default void assertNotBlank(CharSequence str, Object... templateParams) {
-        if (StrUtil.isNotBlank(str)) {
+    default void assertNotBlank(CharSequence cs, Object... templateParams) {
+        if (StrUtil.isNotBlank(cs)) {
             return;
         }
 
@@ -182,14 +181,14 @@ public interface HelioBaseEnum<T> extends Serializable {
     }
 
     /**
-     * 断言不为空列表
+     * 断言不为空集合
      * 注意:枚举选项的 value 建议为整数类型
      *
-     * @param list 需要判断的对象
+     * @param iterable 需要判断的对象
      * @param templateParams label 中如果有占位符的话,向里面填充的模板参数
      */
-    default void assertNotEmpty(Collection<?> list, Object... templateParams) {
-        if (CollUtil.isNotEmpty(list)) {
+    default void assertNotEmpty(Iterable<?> iterable, Object... templateParams) {
+        if (IterUtil.isNotEmpty(iterable)) {
             return;
         }
 

+ 25 - 0
helio-core/src/main/java/cc/uncarbon/framework/core/function/StreamFunction.java

@@ -0,0 +1,25 @@
+package cc.uncarbon.framework.core.function;
+
+import java.util.function.BinaryOperator;
+
+/**
+ * Stream 流函数
+ * 其实更像是一个另类的工具方法集合
+ *
+ * @author Uncarbon
+ */
+public class StreamFunction {
+
+    /**
+     * 用于 stream-collect-toMap 时,忽略已存在的 key & value;不忽略的话,遇到重复项时默认会抛出异常
+     *
+     * 用法:stream().collect(Collectors.toMap( XXX, XXX, StreamFunction.ignoredThrowingMerger()))
+     *
+     * @param <T> 键类型
+     * @return 键名
+     */
+    public static <T> BinaryOperator<T> ignoredThrowingMerger() {
+        return (u, v) -> u;
+    }
+
+}

+ 16 - 4
helio-core/src/main/java/cc/uncarbon/framework/core/page/PageResult.java

@@ -2,15 +2,15 @@ package cc.uncarbon.framework.core.page;
 
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.List;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
 import lombok.experimental.SuperBuilder;
 
-import java.io.Serializable;
-import java.util.List;
-
 /**
  * 分页查询结果
  * @author Uncarbon
@@ -30,9 +30,21 @@ public class PageResult<T extends Serializable> implements Serializable {
     private int size;
 
     @ApiModelProperty(value = "总量")
-    private int total;
+    private long total;
 
     @ApiModelProperty(value = "记录")
     private List<T> records;
 
+
+    /**
+     * 构造方法 - 用于空集合结果
+     * @param pageParam 分页查询参数
+     */
+    public PageResult(PageParam pageParam) {
+        this.current = pageParam.getPageNum();
+        this.size = pageParam.getPageSize();
+        this.total = 0L;
+        this.records = Collections.emptyList();
+    }
+
 }

+ 0 - 6
helio-starter-cloud/pom.xml

@@ -37,12 +37,6 @@
             <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>com.alibaba.cloud</groupId>
-            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
-            <version>${spring-cloud-alibaba.version}</version>
-        </dependency>
-
         <dependency>
             <groupId>com.alibaba.nacos</groupId>
             <artifactId>nacos-client</artifactId>

+ 9 - 14
helio-starter-dubbo/src/main/java/cc/uncarbon/framework/dubbo/filter/ConsumerContextFilter.java

@@ -4,16 +4,10 @@ import cc.uncarbon.framework.core.context.TenantContext;
 import cc.uncarbon.framework.core.context.TenantContextHolder;
 import cc.uncarbon.framework.core.context.UserContext;
 import cc.uncarbon.framework.core.context.UserContextHolder;
-import cn.hutool.json.JSONUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.rpc.Filter;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.*;
 
 /**
  * 自定义Dubbo消费者上下文
@@ -27,21 +21,22 @@ public class ConsumerContextFilter implements Filter {
 
     @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
-        // 将用户上下文、租户上下文等业务字段,放进 Dubbo 附件中
+        /*
+        将用户上下文、租户上下文等业务字段,放进 Dubbo 附件中
+        @since 1.6.0 不再转换成 JSON 字符串后放入附件中,直接使用可序列化的对象
+         */
 
         UserContext userContext = UserContextHolder.getUserContext();
         if (userContext != null) {
-            String userContextJson = JSONUtil.toJsonStr(userContext);
-            log.debug("[Dubbo RPC] 设置当前用户上下文 >> {}", userContextJson);
-            RpcContext.getClientAttachment().setAttachment(UserContext.CAMEL_NAME, userContextJson);
+            log.debug("[Dubbo RPC] 设置当前用户上下文 >> {}", userContext);
+            RpcContext.getClientAttachment().setAttachment(UserContext.CAMEL_NAME, userContext);
         }
 
         TenantContext tenantContext = TenantContextHolder.getTenantContext();
         if (tenantContext != null && tenantContext.getTenantId() != null) {
             // 实际启用了租户
-            String tenantContextJson = JSONUtil.toJsonStr(tenantContext);
-            log.debug("[Dubbo RPC] 设置当前租户上下文 >> {}", tenantContextJson);
-            RpcContext.getClientAttachment().setAttachment(TenantContext.CAMEL_NAME, tenantContextJson);
+            log.debug("[Dubbo RPC] 设置当前租户上下文 >> {}", tenantContext);
+            RpcContext.getClientAttachment().setAttachment(TenantContext.CAMEL_NAME, tenantContext);
         }
 
         return invoker.invoke(invocation);

+ 13 - 17
helio-starter-dubbo/src/main/java/cc/uncarbon/framework/dubbo/filter/ProviderContextFilter.java

@@ -4,17 +4,10 @@ import cc.uncarbon.framework.core.context.TenantContext;
 import cc.uncarbon.framework.core.context.TenantContextHolder;
 import cc.uncarbon.framework.core.context.UserContext;
 import cc.uncarbon.framework.core.context.UserContextHolder;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.json.JSONUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.common.constants.CommonConstants;
 import org.apache.dubbo.common.extension.Activate;
-import org.apache.dubbo.rpc.Filter;
-import org.apache.dubbo.rpc.Invocation;
-import org.apache.dubbo.rpc.Invoker;
-import org.apache.dubbo.rpc.Result;
-import org.apache.dubbo.rpc.RpcContext;
-import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.*;
 
 /**
  * 自定义Dubbo提供者上下文
@@ -28,20 +21,23 @@ public class ProviderContextFilter implements Filter {
 
     @Override
     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
-        // 从 Dubbo 附件中,取出用户上下文、租户上下文等业务字段
+        /*
+        从 Dubbo 附件中,取出用户上下文、租户上下文等业务字段
+        since 1.6.0 附件内容不再是 JSON 字符串,直接使用可序列化的对象
+         */
 
-        String userContextJson = RpcContext.getServerAttachment().getAttachment(UserContext.CAMEL_NAME);
-        if (StrUtil.isNotEmpty(userContextJson)) {
-            UserContext userContext = JSONUtil.parseObj(userContextJson).toBean(UserContext.class);
+        Object attachment = RpcContext.getServerAttachment().getObjectAttachment(UserContext.CAMEL_NAME);
+        if (attachment instanceof UserContext) {
+            UserContext userContext = (UserContext) attachment;
             UserContextHolder.setUserContext(userContext);
-            log.debug("[Dubbo RPC] 取出当前用户上下文 >> {}", userContextJson);
+            log.debug("[Dubbo RPC] 取出当前用户上下文 >> {}", userContext);
         }
 
-        String tenantContextJson = RpcContext.getServerAttachment().getAttachment(TenantContext.CAMEL_NAME);
-        if (StrUtil.isNotEmpty(tenantContextJson)) {
-            TenantContext tenantContext = JSONUtil.parseObj(tenantContextJson).toBean(TenantContext.class);
+        attachment = RpcContext.getServerAttachment().getObjectAttachment(TenantContext.CAMEL_NAME);
+        if (attachment instanceof TenantContext) {
+            TenantContext tenantContext = (TenantContext) attachment;
             TenantContextHolder.setTenantContext(tenantContext);
-            log.debug("[Dubbo RPC] 取出当前租户上下文 >> {}", tenantContextJson);
+            log.debug("[Dubbo RPC] 取出当前租户上下文 >> {}", tenantContext);
         }
 
         return invoker.invoke(invocation);

+ 7 - 2
helio-starter-tenant/src/main/java/cc/uncarbon/framework/tenant/tenantdatasource/GlobalTenantDataSourceInterceptor.java

@@ -32,17 +32,22 @@ public class GlobalTenantDataSourceInterceptor implements MethodInterceptor {
             return invocation.proceed();
         }
 
+        // 是否有切换过数据源(入栈)
+        boolean pushedFlag = false;
         try {
             // 不适合纯数字作为数据源名称,给他拼个前缀
             String tenantDataSourceName = CRUD.COLUMN_TENANT_ID + currentTenantId;
             if (dataSourceRegistry.containsDataSource(tenantDataSourceName, true)) {
                 log.debug("[多租户][数据源级] 使用租户数据源 >> {}", tenantDataSourceName);
                 DynamicDataSourceContextHolder.push(tenantDataSourceName);
+                pushedFlag = true;
             }
-
             return invocation.proceed();
         } finally {
-            DynamicDataSourceContextHolder.poll();
+            if (pushedFlag) {
+                // 数据源出栈
+                DynamicDataSourceContextHolder.poll();
+            }
         }
     }
 }

+ 8 - 8
pom.xml

@@ -23,7 +23,7 @@
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
-        <version>2.6.8</version>
+        <version>2.7.1</version>
         <relativePath/>
     </parent>
 
@@ -33,12 +33,12 @@
 
     <properties>
         <!-- core versions -->
-        <revision>1.5.1</revision>
+        <revision>1.6.0</revision>
         <helio-starters.version>${revision}</helio-starters.version>
         <!-- JDK8则改为'1.8'  JDK11则改为'11' -->
         <java.version>1.8</java.version>
         <!-- 若变更Spring Boot版本, 上面parent依赖版本也要变 -->
-        <spring-boot.version>2.6.8</spring-boot.version>
+        <spring-boot.version>2.7.1</spring-boot.version>
 
 
         <!-- Spring Cloud versions -->
@@ -48,17 +48,17 @@
 
         <!-- Spring Cloud Alibaba versions -->
         <spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>
-        <dubbo.version>3.0.6</dubbo.version>
+        <dubbo.version>3.0.9</dubbo.version>
         <seata.vesion>1.4.2</seata.vesion>
-        <nacos-client.vesion>2.0.4</nacos-client.vesion>
+        <nacos-client.vesion>2.1.0</nacos-client.vesion>
 
 
         <!-- 3rd-party versions -->
-        <hutool.version>5.7.22</hutool.version>
+        <hutool.version>5.8.4</hutool.version>
         <sa-token.version>1.30.0</sa-token.version>
         <transmittable-thread-local.version>2.12.1</transmittable-thread-local.version>
-        <!-- 3.5.1 版本根据 issue 显示,有多租户、枚举类等兼容性问题,暂缓升级 -->
-        <mybatis-plus.version>3.4.3.4</mybatis-plus.version>
+        <!-- 如果 mp 较新版存在问题,可以回退至 3.4.3.4 试试  -->
+        <mybatis-plus.version>3.5.2</mybatis-plus.version>
         <knife4j.version>3.0.3</knife4j.version>
         <redisson.version>3.16.7</redisson.version>
         <aspectj.version>1.9.6</aspectj.version>