springcloud中的线程问题
今天在学微服务时产生了个小问题
我有gateway网关模块以下代码可以将用户信息保存在请求头中,然后我又在common模块下定义了拦截器,将用户信息保存在threadlocal中,我的其他模块已经引入了common模块的坐标,我想问的是我访问其他模块时如果需要获取用户信息,为什么可以直接从threadlocal中获取。一个微服务模块不是一个线程吗,threadlocal不是线程隔离的吗。以下为解答。
因为购物车模块没有直接写拦截器,而是引入了 common
模块的坐标,这里的关键点是 common
模块中的拦截器是否被 Spring 的上下文自动加载并生效。我来一步步解释为什么购物车模块仍然能够使用 ThreadLocal
获取用户信息。
背景分析
common
模块作为工具模块:common
模块中的拦截器 (UserInfoInterceptor
) 是一个通用的用户信息拦截器。- 购物车模块通过引入
common
模块的坐标,获得了拦截器的代码及相关工具类(如UserContext
)。
- Spring Boot 自动加载机制:
- 如果
UserInfoInterceptor
被正确注册为 Spring 的拦截器(通常通过@Component
或显式配置WebMvcConfigurer
),它就会被加载到 Spring 容器中,成为全局拦截器。 - Spring 的拦截器生效后,每次 HTTP 请求到达购物车模块时,都会执行
UserInfoInterceptor
的逻辑。
- 如果
关键点解答:为什么能从 ThreadLocal
中获取用户信息?
- 拦截器被 Spring 加载:
- 当你在
common
模块中定义了UserInfoInterceptor
,并通过@Component
或手动注册,Spring 会扫描并加载它。 - 即使拦截器是在
common
模块中定义的,只要购物车模块引入了common
,且Spring Boot
启动时扫描到了该拦截器,拦截器就会在购物车模块的请求中自动生效。
- 当你在
- 拦截器为每个请求提取用户信息并存入
ThreadLocal
:- 购物车模块的每个请求都会触发
UserInfoInterceptor
,该拦截器从请求头中获取user-info
,并将其存入当前线程的ThreadLocal
中。
- 购物车模块的每个请求都会触发
- ThreadLocal 在当前线程中生效:
- 由于拦截器已经将用户信息存入
ThreadLocal
,购物车模块的后续业务代码可以直接通过UserContext.getUser()
获取用户信息。
- 由于拦截器已经将用户信息存入
核心代码回顾
common
模块中的拦截器
@Component
public class UserInfoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取 user-info
String userInfo = request.getHeader("user-info");
if (StrUtil.isNotBlank(userInfo)) {
// 存入 ThreadLocal
UserContext.setUser(Long.valueOf(userInfo));
}
return true;
}
}
@Component
注解:Spring 会自动将拦截器加载到容器中。- 生效范围:一旦拦截器注册成功,所有经过 Spring MVC 处理的请求都会执行这个拦截器。
common
模块的拦截器注册方式
如果你使用了 WebMvcConfigurer
来显式注册拦截器:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new UserInfoInterceptor())
.addPathPatterns("/**"); // 拦截所有路径
}
}
- 拦截路径:你可以控制拦截器的作用范围,比如只拦截特定路径。
购物车模块如何使用拦截器的功能?
-
引入
common
模块坐标:-
购物车模块通过 Maven 或 Gradle 引入
common
模块:
<dependency> <groupId>com.example</groupId> <artifactId>common</artifactId> <version>1.0.0</version> </dependency>
-
-
拦截器在购物车模块自动生效:
- 如果
UserInfoInterceptor
已经被 Spring 容器加载(通过@Component
或配置类),购物车模块无需额外操作,拦截器会自动对所有 HTTP 请求生效。 - 每次请求都会执行
preHandle
方法,提取user-info
并存入ThreadLocal
。
- 如果
-
购物车模块获取用户信息:
-
在购物车模块的业务逻辑中,只需通过
UserContext.getUser()
获取用户信息:
Long userId = UserContext.getUser(); System.out.println("当前用户ID:" + userId);
-
为什么不需要额外配置?
-
Spring 的拦截器机制
- Spring 的拦截器是全局性的,注册成功后,对模块内部的所有 HTTP 请求生效。
-
模块的隔离性
- 购物车模块并不需要知道拦截器的具体实现,只要
common
提供的拦截器生效,就能在业务代码中获取用户信息。
- 购物车模块并不需要知道拦截器的具体实现,只要
总结
-
ThreadLocal
的值是由common
模块的拦截器设置的。 -
购物车模块无需重复写拦截器,只需确保
common
模块的拦截器在 Spring 容器中注册并生效。 -
模块之间的线程隔离性 依然存在,但用户信息是通过请求头在网关和微服务之间传递的,每个模块都有自己的
ThreadLocal
,不会相互干扰。
也就是说就算拦截器在其他模块,只要被spring容器管理了,购物车模块就可以使用吗?
**是的,完全正确!**只要拦截器被 Spring 容器管理,那么 任何引入该模块(common
模块)并运行在同一个 Spring Boot 应用中的模块,都可以享受到拦截器的功能,而无需重复定义。
这是 Spring 的拦截器机制带来的优势,它的核心逻辑如下:
1. 拦截器在 Spring 容器中的全局性
- Spring 容器管理的拦截器是全局的:
- 拦截器是 Spring MVC 的核心组件之一。
- 一旦拦截器被注册(通过
@Component
或显式在WebMvcConfigurer
中配置),它会对当前 Spring Boot 应用上下文 中的所有 HTTP 请求生效。
- 购物车模块和拦截器共享同一个 Spring 上下文:
- 当购物车模块启动时,它会共享同一个 Spring 容器上下文。
common
模块中的拦截器已经被注册到容器中,因此购物车模块的所有 HTTP 请求都会触发拦截器逻辑。
2. 使用拦截器的前提条件
要确保购物车模块可以正常使用 common
中的拦截器,需要满足以下条件:
-
拦截器在
common
模块中被注册到 Spring 容器:-
如果你的拦截器使用了
@Component
注解:@Component public class UserInfoInterceptor implements HandlerInterceptor { ... }
那么 Spring 容器会自动扫描并加载它,无需额外操作。
-
如果手动注册,则需要在
common
模块中提供WebMvcConfigurer
:@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserInfoInterceptor()) .addPathPatterns("/**"); // 拦截所有路径 } }
-
-
购物车模块引入了
common
模块:-
购物车模块通过 Maven 或 Gradle 引入
common
模块的依赖,例如:
<dependency> <groupId>com.example</groupId> <artifactId>common</artifactId> <version>1.0.0</version> </dependency>
-
-
Spring Boot 应用会自动扫描并加载拦截器:
- 默认情况下,Spring 会自动加载类路径中的所有配置(例如
@Component
和@Configuration
)。 - 因此,无需额外配置,拦截器会对购物车模块的所有 HTTP 请求生效。
- 默认情况下,Spring 会自动加载类路径中的所有配置(例如
3. 核心原理:拦截器如何在购物车模块生效?
请求处理过程:
-
用户发起请求,比如访问购物车模块:
GET /cart HTTP/1.1 user-info: 12345
-
Spring MVC 的拦截器链被触发:
- Spring MVC 会根据
WebMvcConfigurer
注册的拦截器,将UserInfoInterceptor
插入到请求的处理流程中。 - 不管拦截器是定义在购物车模块中,还是在
common
模块中,只要它被 Spring 容器加载,都会在请求处理时生效。
- Spring MVC 会根据
-
UserInfoInterceptor
执行:-
从请求头中提取用户信息:
String userInfo = request.getHeader("user-info"); UserContext.setUser(Long.valueOf(userInfo));
-
存入
ThreadLocal
,供当前请求线程的后续逻辑使用。
-
-
后续业务逻辑可以直接从
ThreadLocal
获取用户信息:Long userId = UserContext.getUser();
4. 为什么可以做到模块共享?
这是因为 Spring 的 @Component
和 @Configuration
本质上将拦截器注册到 Spring MVC 的拦截器链 中。只要服务是运行在同一个 Spring Boot 应用上下文中,所有模块的 HTTP 请求都会共享这个拦截器链。
例子:两个模块共享拦截器
-
模块结构:
common 模块: - UserInfoInterceptor - UserContext 工具类 购物车模块: - CartController
-
流程:
UserInfoInterceptor
在common
模块中注册。- 购物车模块启动时,Spring Boot 加载了
common
模块的拦截器。 - 购物车模块的请求被
UserInfoInterceptor
拦截,存储用户信息到ThreadLocal
。
5. 总结
- 只要拦截器被注册到 Spring 容器(通过
@Component
或配置类注册),它就会对整个 Spring 应用的请求生效。 - 无论拦截器定义在哪个模块,只要模块被正确引入,并且运行在同一个 Spring 应用上下文中,拦截器都会自动生效。
- 购物车模块不需要额外配置拦截器,只需依赖
common
模块,就能共享UserInfoInterceptor
的功能。
原文地址:https://blog.csdn.net/2302_78571314/article/details/144017279
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!