【问题标题】:NullPointerException when setting attribute?设置属性时出现 NullPointerException?
【发布时间】:2012-12-20 11:22:54
【问题描述】:

例如,我有一个将属性设置为 HttpServletRequest 的 servlet 代码:

request.setAttribute("someValue", someValue());
        RequestDispatcher rd = getServletContext().getRequestDispatcher("/SomeJsp.jsp");
        rd.forward(this.request, this.response);
        return;

如何确保上面的代码是线程安全的?

这是我得到的堆栈跟踪:

java.lang.NullPointerException
    at org.apache.catalina.connector.Request.notifyAttributeAssigned(Request.java:1552)
    at org.apache.catalina.connector.Request.access$000(Request.java:105)
    at org.apache.catalina.connector.Request$3.set(Request.java:3342)
    at org.apache.catalina.connector.Request.setAttribute(Request.java:1504)
    at org.apache.catalina.connector.RequestFacade.setAttribute(RequestFacade.java:541)
    at org.apache.catalina.core.ApplicationHttpRequest.setAttribute(ApplicationHttpRequest.java:281)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:286)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:684)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:471)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:402)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:329)
    at com.mycompany.myapp.servlet.SomeServlet.doRequest(SomeServlet.java:103)
    at com.mycompany.myapp.servlet.SomeServlet.doGet(SomeServlet.java:159)

【问题讨论】:

  • 你的请求对象为空
  • 什么是someValue()?是否有可能返回null
  • 检查 someValue() 是否为空
  • 看起来像一个内部错误(将属性设置为null 不是问题,但forward 内部的某些东西会崩溃)。您的 ServletRequest 发生了一些奇怪的事情。
  • 你可能也忘记使用 getSession() 了??

标签: java jsp servlets


【解决方案1】:
rd.forward(this.request, this.response);

这(双关语)表明您已将requestresponse 分配为类的实例变量。您的具体问题反过来表明该类本身的实例不是线程安全的。

假设它实际上是 servlet 本身,那么您就有了问题的原因。 Servlet 根本不是线程安全的。在 webapp 启动期间只创建了一个实例,然后在应用程序范围内的所有个请求之间共享。

您应该从不将请求或会话范围的数据分配为 servlet 的实例变量。它只会在同时发生另一个 HTTP 请求时被覆盖。正如您自己遇到的那样,这会使您的代码线程不安全。

这里有一些代码说明了这一点:

public class MyServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

将 HTTP 请求本身指定为 servlet 的实例变量实际上是一个重大错误。当用户 Y 在处理用户 X 的请求的同时触发另一个请求时,用户 X 将立即获得用户 Y 的 requestresponse 对象。这绝对是线程不安全的。 NPE 是因为 request 在那一刻“完成”了对用户 Y 的处理,因此被释放/销毁。

另见:

【讨论】:

【解决方案2】:

Request 根据其定义是线程安全的(与 Session 和 ServletContext 不同)。

关于例外:您使用的是哪个 Tomcat 版本?这看起来像一个 Tomcat 错误。

someValue()方法返回类型的类是否实现HttpAttributeBindingListener?而且,someValue() 方法可以返回null 吗?如果两者都是,那么NullPointerException 是显而易见的。

【讨论】:

  • 明显吗?真的吗?一个值怎么可能既是 null 又是实现某些东西?
  • 容器期望某个类型的值可以实现ServletRequestAttributeListener。如果一个属性确实实现了这个接口,那么在属性被附加到容器调用的请求ServletRequestAttributeListener.attributeAdded(...)ServletRequestAttributeListener.attributeReplace(...) 之后。
  • null 设置为属性值是绝对合法的。所以你的“显而易见”的论点完全没有根据。至于可能存在的 Tomcat 漏洞,请先查看 Tomcat 源代码,然后再做出如此危险的声明。
【解决方案3】:

我不确定这与线程安全有什么关系。

您得到的异常是 NullPointerException,看起来 Tomcat 正在尝试调用空对象上的方法。

在 Java Web 应用程序中,每个请求都有自己的 HttpServletRequest 实例,因此您可以设置请求的属性并确信它仅适用于该用户。

【讨论】:

  • 这不是他的代码试图在空对象上调用方法,而是 Tomcat 内部。
  • 如果您浏览过 Tomcat 的源代码,那么您会注意到该方法正在尝试访问一个变量,该变量在 HTTP 请求处理结束之前就已发布。这反过来表明另一个线程已经释放了它,因此肯定是一个线程安全问题。
【解决方案4】:

不使用这个操作符

     RequestDispatcher rd = getServletContext().getRequestDispatcher("/SomeJsp.jsp");
    rd.forward(request, response);
    return;

【讨论】:

    【解决方案5】:

    如果你在 tomcat 中查看请求接口的实现,它看起来像下面的代码。

    public void setAttribute(String name, Object value) {
    
        // Name cannot be null
        if (name == null)
            throw new IllegalArgumentException
                (sm.getString("coyoteRequest.setAttribute.namenull"));
    
        // Null value is the same as removeAttribute()
        if (value == null) {
            removeAttribute(name);
            return;
        }
    
        if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) {
            internalDispatcherType = (DispatcherType)value;
            return;
        } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) {
            requestDispatcherPath = value;
            return;
        }
    

    很明显,如果 key 为 null,则抛出 IllegalArgumentException,但如果 value 为 null,则它只需从存储库中删除 key 和与该 key 的旧关联对象。但是,如果它们都不是 null 它们,它将将该对象与该键相关联并将它们添加到存储库中。

    这似乎是一个临时或 tomcat 问题。

    有关实施的更多信息,请参阅以下链接 Source code of Request Implementation by tomcat

    【讨论】:

    • "这似乎是一个临时或 tomcat 问题。"或者@BalusC 描述的问题。
    猜你喜欢
    • 2020-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-11
    • 2016-07-20
    • 1970-01-01
    相关资源
    最近更新 更多