自学内容网 自学内容网

springcloud中的线程问题

今天在学微服务时产生了个小问题

我有gateway网关模块以下代码可以将用户信息保存在请求头中,然后我又在common模块下定义了拦截器,将用户信息保存在threadlocal中,我的其他模块已经引入了common模块的坐标,我想问的是我访问其他模块时如果需要获取用户信息,为什么可以直接从threadlocal中获取。一个微服务模块不是一个线程吗,threadlocal不是线程隔离的吗。以下为解答。

因为购物车模块没有直接写拦截器,而是引入了 common 模块的坐标,这里的关键点是 common 模块中的拦截器是否被 Spring 的上下文自动加载并生效。我来一步步解释为什么购物车模块仍然能够使用 ThreadLocal 获取用户信息。


背景分析

  1. common 模块作为工具模块
    • common 模块中的拦截器 (UserInfoInterceptor) 是一个通用的用户信息拦截器。
    • 购物车模块通过引入 common 模块的坐标,获得了拦截器的代码及相关工具类(如 UserContext)。
  2. Spring Boot 自动加载机制
    • 如果 UserInfoInterceptor 被正确注册为 Spring 的拦截器(通常通过 @Component 或显式配置 WebMvcConfigurer),它就会被加载到 Spring 容器中,成为全局拦截器。
    • Spring 的拦截器生效后,每次 HTTP 请求到达购物车模块时,都会执行 UserInfoInterceptor 的逻辑。

关键点解答:为什么能从 ThreadLocal 中获取用户信息?

  1. 拦截器被 Spring 加载
    • 当你在 common 模块中定义了 UserInfoInterceptor,并通过 @Component 或手动注册,Spring 会扫描并加载它。
    • 即使拦截器是在 common 模块中定义的,只要购物车模块引入了 common,且 Spring Boot 启动时扫描到了该拦截器,拦截器就会在购物车模块的请求中自动生效。
  2. 拦截器为每个请求提取用户信息并存入 ThreadLocal
    • 购物车模块的每个请求都会触发 UserInfoInterceptor,该拦截器从请求头中获取 user-info,并将其存入当前线程的 ThreadLocal 中。
  3. 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("/**"); // 拦截所有路径
    }
}
  • 拦截路径:你可以控制拦截器的作用范围,比如只拦截特定路径。

购物车模块如何使用拦截器的功能?

  1. 引入 common 模块坐标

    • 购物车模块通过 Maven 或 Gradle 引入

      common
      

      模块:

      <dependency>
          <groupId>com.example</groupId>
          <artifactId>common</artifactId>
          <version>1.0.0</version>
      </dependency>
      
  2. 拦截器在购物车模块自动生效

    • 如果 UserInfoInterceptor 已经被 Spring 容器加载(通过 @Component 或配置类),购物车模块无需额外操作,拦截器会自动对所有 HTTP 请求生效。
    • 每次请求都会执行 preHandle 方法,提取 user-info 并存入 ThreadLocal
  3. 购物车模块获取用户信息

    • 在购物车模块的业务逻辑中,只需通过

      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 容器中的全局性

  1. Spring 容器管理的拦截器是全局的
    • 拦截器是 Spring MVC 的核心组件之一。
    • 一旦拦截器被注册(通过 @Component 或显式在 WebMvcConfigurer 中配置),它会对当前 Spring Boot 应用上下文 中的所有 HTTP 请求生效。
  2. 购物车模块和拦截器共享同一个 Spring 上下文
    • 当购物车模块启动时,它会共享同一个 Spring 容器上下文。
    • common 模块中的拦截器已经被注册到容器中,因此购物车模块的所有 HTTP 请求都会触发拦截器逻辑。

2. 使用拦截器的前提条件

要确保购物车模块可以正常使用 common 中的拦截器,需要满足以下条件:

  1. 拦截器在 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("/**"); // 拦截所有路径
          }
      }
      
  2. 购物车模块引入了 common 模块

    • 购物车模块通过 Maven 或 Gradle 引入

      common
      

      模块的依赖,例如:

      <dependency>
          <groupId>com.example</groupId>
          <artifactId>common</artifactId>
          <version>1.0.0</version>
      </dependency>
      
  3. Spring Boot 应用会自动扫描并加载拦截器

    • 默认情况下,Spring 会自动加载类路径中的所有配置(例如 @Component@Configuration)。
    • 因此,无需额外配置,拦截器会对购物车模块的所有 HTTP 请求生效。

3. 核心原理:拦截器如何在购物车模块生效?

请求处理过程

  1. 用户发起请求,比如访问购物车模块:

    GET /cart HTTP/1.1
    user-info: 12345
    
  2. Spring MVC 的拦截器链被触发:

    • Spring MVC 会根据 WebMvcConfigurer 注册的拦截器,将 UserInfoInterceptor 插入到请求的处理流程中。
    • 不管拦截器是定义在购物车模块中,还是在 common 模块中,只要它被 Spring 容器加载,都会在请求处理时生效。
  3. UserInfoInterceptor 执行:

    • 从请求头中提取用户信息:

      String userInfo = request.getHeader("user-info");
      UserContext.setUser(Long.valueOf(userInfo));
      
    • 存入 ThreadLocal,供当前请求线程的后续逻辑使用。

  4. 后续业务逻辑可以直接从 ThreadLocal 获取用户信息:

    Long userId = UserContext.getUser();
    

4. 为什么可以做到模块共享?

这是因为 Spring 的 @Component@Configuration 本质上将拦截器注册到 Spring MVC 的拦截器链 中。只要服务是运行在同一个 Spring Boot 应用上下文中,所有模块的 HTTP 请求都会共享这个拦截器链。

例子:两个模块共享拦截器
  1. 模块结构

    common 模块:
        - UserInfoInterceptor
        - UserContext 工具类
    
    购物车模块:
        - CartController
    
  2. 流程

    • UserInfoInterceptorcommon 模块中注册。
    • 购物车模块启动时,Spring Boot 加载了 common 模块的拦截器。
    • 购物车模块的请求被 UserInfoInterceptor 拦截,存储用户信息到 ThreadLocal

5. 总结

  1. 只要拦截器被注册到 Spring 容器(通过 @Component 或配置类注册),它就会对整个 Spring 应用的请求生效。
  2. 无论拦截器定义在哪个模块,只要模块被正确引入,并且运行在同一个 Spring 应用上下文中,拦截器都会自动生效。
  3. 购物车模块不需要额外配置拦截器,只需依赖 common 模块,就能共享 UserInfoInterceptor 的功能。

原文地址:https://blog.csdn.net/2302_78571314/article/details/144017279

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!