在 Java 中获取当前播放 session()

Get the current Play session() in Java

我有这个class:

package ds;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import play.mvc.Http;

public class MyRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected String determineCurrentLookupKey() {
        return Http.Context.current().session().get("currentDB");
    }
}

但是当我想访问 Play 时 session 我有这个错误:

Caused by: java.lang.RuntimeException: There is no HTTP Context available from here.
at play.mvc.Http$Context.current(Http.java:34) ~[play_2.10-2.3.10.jar:2.3.10]
at play.mvc.Controller.session(Controller.java:72) ~[play_2.10-2.3.10.jar:2.3.10]

我也尝试使用 HttpExecution.defaultContext() 和他的 HttpExecutionContext,但它无法转换为我需要的 Http.Context。

我正在考虑获取请求 header 但我当然不知道如何从我的 class 中处理它并从请求 [=14= 中确定 session ]

您无法访问该层中的 Play Context,因为超出范围。在 AbstractRoutingDatasource 的文档中:

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSource.html

class 的主要描述说:

Abstract DataSource implementation that routes getConnection() calls to one of various target DataSources based on a lookup key. The latter is usually (but not necessarily) determined through some thread-bound transaction context.

所以这个 class 建议获取当前上下文信息的方法应该是使用线程绑定事务上下文。

现在,PlayFramework 线程上下文安全吗?阅读 Play 文档:

https://www.playframework.com/documentation/2.3.x/ThreadPools#Java-thread-locals

Java code in Play uses a thread local to find out about contextual information such as the current HTTP request. Scala code doesn’t need to use thread locals because it can use implicit parameters to pass context instead. Threads locals are used in Java so that Java code can access contextual information without needing to pass context parameters everywhere.

因此,如果您使用的是 Java 实现,则可以将 ThreadLocal 用作组件之间的上下文通道。如果您创建自己的线程池,请小心,因为同一文档中有警告:

The default objects wrap the default user thread pool. If you want to do your own threading then you should use the HttpExecution class’s helper methods to get an ExecutionContextExecutor object yourself.

但如果您没有在您的应用中使用自定义线程池,那将不是问题。

这么说,你要做的是:

  1. 为要用作路由器的对象定义一个 ThreadLocalContext。

  2. 放在上下文中。 (您可以在控制器中执行此操作,如果您使用 Deadbolt 之类的授权框架甚至实现新的请求过滤器,则可以在安全控制器中执行此操作。)

  3. 在 AbstractRoutingDataSource 中读取 ThreadLocal 上下文。

重要!不要忘记清理 Thread-Local,否则您可能会面临内存泄漏。

第 1 步:

public class RequestContext {

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

   public static void setRoutingKey(String key) {
      contextHolder.set(key);
   }

   public static String getRoutingKey() {
      return (String) contextHolder.get();
   }

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

第 2 步:

//Demostrative code, not tested, not even compiled
public static void myController() {
   RoutingContext.setRoutingKey(Play.Context.request()); 
   return bla;
}

第 3 步:

@Override
protected Object determineCurrentLookupKey() {
  String datasource = RoutingContext.getRoutingKey();
  RoutingContext.clearRoutingKey();
  return datasource;
}

此致!