feat(context): 新增业务上下文组件并迁移相关功能

- 创建 `hanserwei-spring-boot-starter-biz-context` 模块,封装为starter供其余模块使用
- 迁移 `HeaderUserId2ContextFilter` 和 `LoginUserContextHolder` 到Starter
- 使用 `TransmittableThreadLocal` 替代普通 `ThreadLocal`
- 在 `han-note-auth` 中引入新的上下文组件依赖
- 调整包结构和日志输出格式
- 异步任务中验证上下文传递功能正常工作
This commit is contained in:
Hanserwei
2025-10-02 23:23:47 +08:00
parent d448c524b7
commit 5c406b48f9
11 changed files with 106 additions and 12 deletions

View File

@@ -0,0 +1,48 @@
<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>
<groupId>com.hanserwei</groupId>
<artifactId>hanserwei-framework</artifactId>
<version>${revision}</version>
</parent>
<packaging>jar</packaging>
<artifactId>hanserwei-spring-boot-starter-biz-context</artifactId>
<name>${project.artifactId}</name>
<description>业务上下文组件</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.hanserwei</groupId>
<artifactId>hanserwei-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,13 @@
package com.hanserwei.framework.biz.context.config;
import com.hanserwei.framework.biz.context.filter.HeaderUserId2ContextFilter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
public class ContextAutoConfiguration {
@Bean
public HeaderUserId2ContextFilter headerUserId2ContextFilter() {
return new HeaderUserId2ContextFilter();
}
}

View File

@@ -0,0 +1,47 @@
package com.hanserwei.framework.biz.context.filter;
import com.hanserwei.framework.biz.context.holder.LoginUserContextHolder;
import com.hanserwei.framework.common.constant.GlobalConstants;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
@Slf4j
public class HeaderUserId2ContextFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
log.info("=====> HeaderUserId2ContextFilter 开始执行");
// 从请求头中获取用户 ID
String userId = request.getHeader(GlobalConstants.USER_ID);
log.info("=====> HeaderUserId2ContextFilter, 用户 ID: {}", userId);
// 判断请求头中是否存在用户 ID
if (StringUtils.isBlank(userId)) {
// 若为空,则直接放行
chain.doFilter(request, response);
return;
}
// 如果 header 中存在 userId则设置到 ThreadLocal 中
log.info("=====> 设置 userId 到 ThreadLocal 中, 用户 ID: {}", userId);
LoginUserContextHolder.setUserId(userId);
try {
chain.doFilter(request, response);
} finally {
// 一定要删除 ThreadLocal ,防止内存泄露
LoginUserContextHolder.remove();
log.info("=====> 删除 ThreadLocal userId: {}", userId);
}
}
}

View File

@@ -0,0 +1,46 @@
package com.hanserwei.framework.biz.context.holder;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.hanserwei.framework.common.constant.GlobalConstants;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class LoginUserContextHolder {
// 初始化一个 ThreadLocal 变量
private static final ThreadLocal<Map<String, Object>> LOGIN_USER_CONTEXT_THREAD_LOCAL
= TransmittableThreadLocal.withInitial(HashMap::new);
/**
* 设置用户 ID
*
* @param value 用户 ID
*/
public static void setUserId(Object value) {
LOGIN_USER_CONTEXT_THREAD_LOCAL.get().put(GlobalConstants.USER_ID, value);
}
/**
* 获取用户 ID
*
* @return 用户 ID
*/
public static Long getUserId() {
Object value = LOGIN_USER_CONTEXT_THREAD_LOCAL.get().get(GlobalConstants.USER_ID);
if (Objects.isNull(value)) {
return null;
}
return Long.valueOf(value.toString());
}
/**
* 删除 ThreadLocal
*/
public static void remove() {
LOGIN_USER_CONTEXT_THREAD_LOCAL.remove();
}
}

View File

@@ -0,0 +1 @@
com.hanserwei.framework.biz.context.config.ContextAutoConfiguration