Java ThreadLocal-使用spring引导作为REST API的上下文信息

Java ThreadLocal-使用spring引导作为REST API的上下文信息,java,spring-boot,spring-security,thread-safety,thread-local,Java,Spring Boot,Spring Security,Thread Safety,Thread Local,我有一些springboot应用程序(它公开了restapi)。提到的RESTAPI 由spring security保护。一切都很好,但现在我需要设置上下文 服务请求。设置上下文是关于根据用户上下文在中选择数据源。关键是RoutingDataSource需要使用此上下文。(由于其他原因,此上下文必须在验证请求后直接设置,我还有其他线程使用RoutingDataSource,但没有被请求调用(没有用户上下文)) 我可以做这些事情,但我怀疑上下文的线程安全性和清除它。我试图在文档中找到答案,但没能

我有一些
springboot
应用程序(它公开了restapi)。提到的RESTAPI 由
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();
   }
}
我能做到。但是,由于
spring-boot
是多线程的,并且我使用
ThreadLocal
作为保持上下文,因此我担心此配置的线程安全性

当我设置这个上下文时? 在筛选器中,仅在成功授权请求后。因此,问题是:

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

  • 如果是案例1。我可以假设一个线程自始至终都在处理请求(在安全请求之后,begin包括filter),然后我应该何时调用
    clearContext()


  • 关于第二个问题:在设置线程本地的过滤器中清除线程本地

        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
            }
        }
    
  • 如果在程序中只使用一个线程,那么答案是肯定的。没有理由在不同的线程中运行此操作,因为切换线程会增加开销。但在您的程序中,您或其他人可以定义异步操作(@async、Thread.start()、events等)。在这种情况下,有多个线程,而您的ThreadLocal将只处理第一个线程的值

  • 是的,但见第一段

  • 我建议为此任务使用与用户关联的线程安全缓存(例如ConcurrentHashMap)。它将更易于理解和线程安全。
    如果您想使用ThreadLocal,您需要澄清并最小化应用程序中的线程生命周期。

    请求完成后,您应该立即清除上下文

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

    它是单线程的,除非您有意启动子线程。 在这种情况下,请使用存储信息。

    还有另一种与weblogic部署相关的方法,线程本地方法可能是一种可行的解决方案。
    try {
        filterChain.doFilter(request, response);
    }
    finally {
        // remove context here
    }