java - @Context 返回代理而不是 HttpServletRequest(代理范围内没有线程本地值)

标签 java dependency-injection jersey glassfish-3

我有点难以理解为什么 @Context 依赖注入(inject)返回 $Proxy(random number) 实例集合而不是 HttpServletRequest 或 HttpServletResponse。

我正在使用 Glassfish 3.1.2.2 及其版本的 Jersey(Jersey:1.11.1),我的应用程序构建为 EAR 应用程序。

我有一个简单的@Remote 接口(interface),我在其中注释我的方法和 REST 服务工作没有任何问题,但是当我尝试访问 HttpServletRequest 信息时它只会导致问题。

我在我的 session bean 中注释了私有(private)字段:

@Context
private HttpServletRequest request;
@Context
private HttpServletResponse response;

并且还创建了方法签名以包含@Context 作为参数集:

@POST
@Path("authenticate")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Response authenticate(@FormParam("username") String username, @FormParam("password") String password, @Context HttpServletRequest request, @Context HttpServletResponse response); 

当我在一个条目之后尝试调试该方法时,我可以看到全局请求和响应对象为空,而本地方法实例为 $ProxyXXX 类型。

问题是我无法访问(或者我不确定如何访问)这些对象。根据网络上的教程,我应该可以使用它们,但是当我尝试访问它们时,它被抛出:

WARNING: StandardWrapperValve[RestDataService]: PWC1406: Servlet.service() for servlet RestDataService threw exception
java.lang.IllegalStateException: No thread local value in scope for proxy of class $Proxy245
    at com.sun.jersey.server.impl.ThreadLocalInvoker.invoke(ThreadLocalInvoker.java:93)
    at $Proxy245.getContextPath(Unknown Source)

这就是我尝试调用它们的方式(在这个例子中我只是调用 getContextPath)

@Override
public Response authenticate(String username, String password, HttpServletRequest request, HttpServletResponse response) {

    System.out.println("just a test: " + request.getContextPath());

我在这里错过了什么?有没有人遇到过类似的问题?

格雷格

最佳答案

要理解这个问题,您需要了解作用域的工作原理。为了描述发生了什么,这里有一个例子。假设你有这个

@Singleton
@Path("..")
public class SomeResource {

    @Context
    private HttpServletRequest request;
}

这种情况带来的问题是为每个请求生成一个HttpServletRequest,但是资源类只创建一次,因为它是单例,所以最初没有HttpServletRequest 在单例创建时注入(inject)单例。

为了解决这个问题,Jersey 注入(inject)了一个代理。所以从概念上讲,结果更像是

@Singleton
@Path("..")
public class SomeResource {

    @Context
    private ProxyHttpServletRequest proxyRequest;
}

当请求进来时,实际的 HttpServletRequest 被放入 scope contextThreadLocal 中。这不是一个精确的实现,但您可以将范围上下文想象成类似

public class RequestScopeContext {
    private static final ThreadLocal<HttpServletReqest> request
            = new ThreadLocal<>();

    public static HttpServletRequest get() { .. }
    public static void setRequest(HttpServletRequest request) { .. }
}

当请求进入时,HttpServletRequest 被设置到上下文中。当调用 SomeResource 中的 HttpServletRequest 时,它实际上是在代理对象上进行的,该代理对象从 ThreadLocal 获取请求并转发电话。从概念上讲,你想象它看起来像

class ProxyHttpServletRequest {
    public String getContextPath() {
        HttpServletRequest request = RequestScopeContext.get();
        return request.getContextPath();
    }
}

现在假设我们有这个

@Singleton
@Path("..")
public class SomeResource {

    @GET
    public Response get(@Context HttpServletRequest request) {
        ...
    }
}

与此不同的是请求不再注入(inject)到字段中,因此不需要代理。 HttpServletRequest 仅在调用该方法时才需要。所以 Jersey 将注入(inject)实际请求而不是代理。

请注意,此模式并不仅仅针对 Jersey。任何涉及 DI 和作用域的框架在尝试将较小作用域对象注入(inject)较宽作用域对象时都会使用类似的代理模式。


致 OP: 现在这不是您的确切问题。上面的回答更可能让大多数对代理有疑问的人受益。

在您的例子中,您说您正在尝试将 HttpServletRequest 注入(inject)到 @Remote EJB 中。一方面,我什至不知道那是或可能的。我的猜测是 EJB 引擎从不设置 ThreadLocal。因此,当 Jersey 尝试调用它时,上下文中没有任何内容。

@Context 注释适用于 JAX-RS 组件,不适用于 EJB。我个人不知道如何将 HttpServletRequest 注入(inject)到 EJB 中,因为我对 EJB 的工作不多。因此,任何面临您确切问题的人都需要搜索如何那个。但就像我说的,我认为搜索这个问题的人不是大多数。我想他们只是想知道为什么请求是代理而不是实际请求。所以这个答案更适合他们。

关于java - @Context 返回代理而不是 HttpServletRequest(代理范围内没有线程本地值),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13198550/

相关文章:

java - 使用Java在android studio中查询firebase数据库

java - 正则表达式匹配字符串内的文本,并删除尾随换行符

java - 哈希码和排除字段

java - Jersey/JAX-RS 中的嵌套内部类

java - Jersey REST ResourceConfig 实例不包含任何根资源类

java - 获取 ArtifactDescriptorException : Failed to read artifact descriptor for org. glassfish.jersey.media :jersey-media-moxy:jar:2. 22.1

java - 我用不同的参数执行哪种方法?

c# - 在依赖解析期间获取类类型

java - java中的依赖注入(inject)

c# - 如何在 ASP.NET Core 中获取没有构造函数注入(inject)的注入(inject)服务的实例?