繁体   English   中英

ThreadLocal - 使用 spring-boot 作为 REST API 的上下文信息

[英]ThreadLocal - using as context information for REST API with spring-boot

我有一些spring-boot应用程序(它公开了 rest api)。 提到的 REST API 由spring-security 一切都很好,但是现在我需要为服务请求设置上下文。 设置上下文是关于根据用户上下文选择数据源。 关键是 RoutingDataSource 需要用到这个上下文。 (由于其他原因,必须在对请求进行身份验证后直接设置此上下文,我还有其他线程使用 RoutingDataSource,但没有被请求调用(无用户上下文))。

我可以做这些事情,但是我的疑虑是关于上下文的线程安全性和清除它。 我试图在文档中找到答案,但我没有找到。

public class CustomContextHolder {

   private static final ThreadLocal<DatabaseType> contextHolder = 
            new ThreadLocal<DatabaseType>();

   public static void setContext(DatabaseType databaseType) {
      contextHolder.set(databaseType);
   }

   public static CustomerType getContext() {
      return (CustomerType) contextHolder.get();
   }

   public static void clearContext() {
      contextHolder.remove();
   }
}

并设置上下文:

@Component
class AuthorizedRequestFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            // here we set context
        }

        filterChain.doFilter(request, response);
    }
}

我能做到。 但是,因为spring-boot是多线程的,并且我使用ThreadLocal作为保持上下文,所以我担心这种配置的线程安全性。

当我设置这个上下文时? 在过滤器中,只有在请求授权成功后 所以问题是:

  1. 它是线程安全的吗? 这意味着:我可以假设执行过滤器的同一个线程(因此也是这个线程在其自己的本地上下文中设置上下文)也执行整个请求(例如从 dao 调用方法、发送响应、执行控制器主体)?

  2. 如果在情况 1. 我可以假设一个线程从头到尾处理请求(开始包括安全请求后的过滤器),那么我应该什么时候调用clearContext()

它是单线程的,除非你故意启动子线程。 在这种情况下,使用InheritableThreadLocal来存储信息。

关于您的第二个问题:在您设置它的同一过滤器中清除本地线程。

    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    boolean contextSetViaThreadLocal = false;
    if (authentication != null && authentication.isAuthenticated()) {
        contextSetViaThreadLocal = true;
        // here we set context
    }
    // immediately after the conditional context store
    try {
        filterChain.doFilter(request, response);
    } finally {
        if (contextSetViaThreadLocal) {
            // clear the context
        }
    }
  1. 如果您在程序中只使用一个线程,那么答案是肯定的。 没有理由在不同的线程中运行此操作,因为切换线程是开销。 但是在您的程序中,您或其他人可以定义异步操作(@Async、Thread.start()、事件等),在这种情况下有多个线程,并且您的 ThreadLocal 将仅处理第一个线程的值。

  2. 是的,但请参阅第一段。

我建议为此任务使用与用户关联的线程安全缓存(例如 ConcurrentHashMap)。 理解和线程安全会更简单。 如果您想使用 ThreadLocal,您需要在您的应用程序中阐明并最小化他的生命周期。

您应该在请求完成后清除上下文。

try {
    filterChain.doFilter(request, response);
}
finally {
    // remove context here
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM