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

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<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-context</artifactId>
<name>${project.artifactId}</name>
<version>${project.parent.version}</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-tool</artifactId>
</dependency>
<!-- Auto -->
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-auto</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,74 @@
/**
* 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.context;
import org.slf4j.MDC;
import org.springblade.core.tool.utils.ThreadLocalUtil;
import org.springframework.lang.Nullable;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* 多线程中传递 context 和 mdc
*
* @author L.cm
*/
public class BladeCallableWrapper<V> implements Callable<V> {
private final Callable<V> delegate;
private final Map<String, Object> tlMap;
/**
* logback 下有可能为 null
*/
@Nullable
private final Map<String, String> mdcMap;
public BladeCallableWrapper(Callable<V> callable) {
this.delegate = callable;
this.tlMap = ThreadLocalUtil.getAll();
this.mdcMap = MDC.getCopyOfContextMap();
}
@Override
public V call() throws Exception {
if (!tlMap.isEmpty()) {
ThreadLocalUtil.put(tlMap);
}
if (mdcMap != null && !mdcMap.isEmpty()) {
MDC.setContextMap(mdcMap);
}
try {
return delegate.call();
} finally {
tlMap.clear();
if (mdcMap != null) {
mdcMap.clear();
}
ThreadLocalUtil.clear();
MDC.clear();
}
}
}

View File

@@ -0,0 +1,82 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.context;
import org.springframework.lang.Nullable;
import java.util.function.Function;
/**
* Blade微服务上下文
*
* @author L.cm
*/
public interface BladeContext {
/**
* 获取 请求 id
*
* @return 请求id
*/
@Nullable
String getRequestId();
/**
* 账号id
*
* @return 账号id
*/
@Nullable
String getAccountId();
/**
* 获取租户id
*
* @return 租户id
*/
@Nullable
String getTenantId();
/**
* 获取上下文中的数据
*
* @param ctxKey 上下文中的key
* @return 返回对象
*/
@Nullable
String get(String ctxKey);
/**
* 获取上下文中的数据
*
* @param ctxKey 上下文中的key
* @param function 函数式
* @param <T> 泛型对象
* @return 返回对象
*/
@Nullable
<T> T get(String ctxKey, Function<String, T> function);
}

View File

@@ -0,0 +1,59 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.context;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import jakarta.servlet.http.HttpServletRequest;
/**
* HttpHeaders 获取器,用于跨服务和线程的传递,
* <p>
* 暂时不支持 webflux。
*
* @author L.cm
*/
public interface BladeHttpHeadersGetter {
/**
* 获取 HttpHeaders
*
* @return HttpHeaders
*/
@Nullable
HttpHeaders get();
/**
* 获取 HttpHeaders
*
* @param request 请求
* @return HttpHeaders
*/
@Nullable
HttpHeaders get(HttpServletRequest request);
}

View File

@@ -0,0 +1,73 @@
/**
* 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.context;
import org.slf4j.MDC;
import org.springblade.core.tool.utils.ThreadLocalUtil;
import org.springframework.lang.Nullable;
import java.util.Map;
/**
* 多线程中传递 context 和 mdc
*
* @author L.cm
*/
public class BladeRunnableWrapper implements Runnable {
private final Runnable delegate;
private final Map<String, Object> tlMap;
/**
* logback 下有可能为 null
*/
@Nullable
private final Map<String, String> mdcMap;
public BladeRunnableWrapper(Runnable runnable) {
this.delegate = runnable;
this.tlMap = ThreadLocalUtil.getAll();
this.mdcMap = MDC.getCopyOfContextMap();
}
@Override
public void run() {
if (!tlMap.isEmpty()) {
ThreadLocalUtil.put(tlMap);
}
if (mdcMap != null && !mdcMap.isEmpty()) {
MDC.setContextMap(mdcMap);
}
try {
delegate.run();
} finally {
tlMap.clear();
if (mdcMap != null) {
mdcMap.clear();
}
ThreadLocalUtil.clear();
MDC.clear();
}
}
}

View File

@@ -0,0 +1,89 @@
/**
* 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.context;
import lombok.RequiredArgsConstructor;
import org.springblade.core.context.props.BladeContextProperties;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.ThreadLocalUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import java.util.function.Function;
import static org.springblade.core.tool.constant.BladeConstant.CONTEXT_KEY;
/**
* blade servlet 上下文,跨线程失效
*
* @author L.cm
*/
@RequiredArgsConstructor
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class BladeServletContext implements BladeContext {
private final BladeContextProperties contextProperties;
private final BladeHttpHeadersGetter httpHeadersGetter;
@Nullable
@Override
public String getRequestId() {
return get(contextProperties.getHeaders().getRequestId());
}
@Nullable
@Override
public String getAccountId() {
return get(contextProperties.getHeaders().getAccountId());
}
@Nullable
@Override
public String getTenantId() {
return get(contextProperties.getHeaders().getTenantId());
}
@Nullable
@Override
public String get(String ctxKey) {
HttpHeaders headers = ThreadLocalUtil.getIfAbsent(CONTEXT_KEY, httpHeadersGetter::get);
if (headers == null || headers.isEmpty()) {
return null;
}
return headers.getFirst(ctxKey);
}
@Nullable
@Override
public <T> T get(String ctxKey, Function<String, T> function) {
String ctxValue = get(ctxKey);
if (StringUtil.isBlank(ctxValue)) {
return null;
}
return function.apply(ctxKey);
}
}

View File

@@ -0,0 +1,84 @@
/**
* 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.context;
import lombok.RequiredArgsConstructor;
import org.springblade.core.context.props.BladeContextProperties;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.WebUtil;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.List;
/**
* HttpHeaders 获取器
*
* @author L.cm
*/
@RequiredArgsConstructor
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class ServletHttpHeadersGetter implements BladeHttpHeadersGetter {
private final BladeContextProperties properties;
@Nullable
@Override
public HttpHeaders get() {
HttpServletRequest request = WebUtil.getRequest();
if (request == null) {
return null;
}
return get(request);
}
@Nullable
@Override
public HttpHeaders get(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
List<String> crossHeaders = properties.getCrossHeaders();
// 传递请求头
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
List<String> allowed = properties.getHeaders().getAllowed();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
// 只支持配置的 header
if (crossHeaders.contains(key) || allowed.contains(key)) {
String values = request.getHeader(key);
// header value 不为空的 传递
if (StringUtil.isNotBlank(values)) {
headers.add(key, values);
}
}
}
}
return headers;
}
}

View File

@@ -0,0 +1,62 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.context.config;
import org.springblade.core.context.BladeContext;
import org.springblade.core.context.BladeHttpHeadersGetter;
import org.springblade.core.context.BladeServletContext;
import org.springblade.core.context.ServletHttpHeadersGetter;
import org.springblade.core.context.props.BladeContextProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* blade 服务上下文配置
*
* @author L.cm
*/
@AutoConfiguration
@Order(Ordered.HIGHEST_PRECEDENCE)
@EnableConfigurationProperties(BladeContextProperties.class)
public class BladeContextAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public BladeHttpHeadersGetter bladeHttpHeadersGetter(BladeContextProperties contextProperties) {
return new ServletHttpHeadersGetter(contextProperties);
}
@Bean
@ConditionalOnMissingBean
public BladeContext bladeContext(BladeContextProperties contextProperties, BladeHttpHeadersGetter httpHeadersGetter) {
return new BladeServletContext(contextProperties, httpHeadersGetter);
}
}

View File

@@ -0,0 +1,51 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.context.config;
import org.springblade.core.context.BladeHttpHeadersGetter;
import org.springblade.core.context.listener.BladeServletRequestListener;
import org.springblade.core.context.props.BladeContextProperties;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
/**
* Servlet 监听器自动配置
*
* @author L.cm
*/
@AutoConfiguration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class BladeServletListenerConfiguration {
@Bean
public ServletListenerRegistrationBean<?> registerCustomListener(BladeContextProperties properties,
BladeHttpHeadersGetter httpHeadersGetter) {
return new ServletListenerRegistrationBean<>(new BladeServletRequestListener(properties, httpHeadersGetter));
}
}

View File

@@ -0,0 +1,83 @@
/**
* BladeX Commercial License Agreement
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
* <p>
* Use of this software is governed by the Commercial License Agreement
* obtained after purchasing a license from BladeX.
* <p>
* 1. This software is for development use only under a valid license
* from BladeX.
* <p>
* 2. Redistribution of this software's source code to any third party
* without a commercial license is strictly prohibited.
* <p>
* 3. Licensees may copyright their own code but cannot use segments
* from this software for such purposes. Copyright of this software
* remains with BladeX.
* <p>
* Using this software signifies agreement to this License, and the software
* must not be used for illegal purposes.
* <p>
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
* not liable for any claims arising from secondary or illegal development.
* <p>
* Author: DreamLu (596392912@qq.com)
*/
package org.springblade.core.context.listener;
import lombok.RequiredArgsConstructor;
import org.slf4j.MDC;
import org.springblade.core.context.BladeHttpHeadersGetter;
import org.springblade.core.context.props.BladeContextProperties;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.StringUtil;
import org.springblade.core.tool.utils.ThreadLocalUtil;
import org.springframework.http.HttpHeaders;
import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.http.HttpServletRequest;
/**
* Servlet 请求监听器
*
* @author L.cm
*/
@RequiredArgsConstructor
public class BladeServletRequestListener implements ServletRequestListener {
private final BladeContextProperties contextProperties;
private final BladeHttpHeadersGetter httpHeadersGetter;
@Override
public void requestInitialized(ServletRequestEvent event) {
HttpServletRequest request = (HttpServletRequest) event.getServletRequest();
// MDC 获取透传的 变量
BladeContextProperties.Headers headers = contextProperties.getHeaders();
String requestId = request.getHeader(headers.getRequestId());
if (StringUtil.isNotBlank(requestId)) {
MDC.put(BladeConstant.MDC_REQUEST_ID_KEY, requestId);
}
String accountId = request.getHeader(headers.getAccountId());
if (StringUtil.isNotBlank(accountId)) {
MDC.put(BladeConstant.MDC_ACCOUNT_ID_KEY, accountId);
}
String tenantId = request.getHeader(headers.getTenantId());
if (StringUtil.isNotBlank(tenantId)) {
MDC.put(BladeConstant.MDC_TENANT_ID_KEY, tenantId);
}
// 处理 context直接传递 request因为 spring 中的尚未初始化完成
HttpHeaders httpHeaders = httpHeadersGetter.get(request);
ThreadLocalUtil.put(BladeConstant.CONTEXT_KEY, httpHeaders);
}
@Override
public void requestDestroyed(ServletRequestEvent event) {
// 会话销毁时,清除上下文
ThreadLocalUtil.clear();
// 会话销毁时,清除 mdc
MDC.remove(BladeConstant.MDC_REQUEST_ID_KEY);
MDC.remove(BladeConstant.MDC_ACCOUNT_ID_KEY);
MDC.remove(BladeConstant.MDC_TENANT_ID_KEY);
}
}

View File

@@ -0,0 +1,90 @@
/**
* 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.context.props;
import lombok.Getter;
import lombok.Setter;
import org.springblade.core.launch.constant.TokenConstant;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Headers 配置
*
* @author L.cm
*/
@Getter
@Setter
@ConfigurationProperties(BladeContextProperties.PREFIX)
public class BladeContextProperties {
/**
* 配置前缀
*/
public static final String PREFIX = "blade.context";
/**
* 上下文传递的 headers 信息
*/
private Headers headers = new Headers();
@Getter
@Setter
public static class Headers {
/**
* 请求id默认Blade-RequestId
*/
private String requestId = "Blade-RequestId";
/**
* 用于 聚合层 向调用层传递用户信息 的请求头默认Blade-AccountId
*/
private String accountId = "Blade-AccountId";
/**
* 用于 聚合层 向调用层传递租户id 的请求头默认Blade-TenantId
*/
private String tenantId = "Blade-TenantId";
/**
* 自定义 RestTemplate 和 Feign 透传到下层的 Headers 名称列表
*/
private List<String> allowed = Arrays.asList("X-Real-IP", "x-forwarded-for", "version", "VERSION", "authorization", "Authorization", TokenConstant.HEADER.toLowerCase(), TokenConstant.HEADER);
}
/**
* 获取跨服务的请求头
*
* @return 请求头列表
*/
public List<String> getCrossHeaders() {
List<String> headerList = new ArrayList<>();
headerList.add(headers.getRequestId());
headerList.add(headers.getAccountId());
headerList.add(headers.getTenantId());
headerList.addAll(headers.getAllowed());
return headerList;
}
}