HttpSession CDI 注入 servlet 项目



我的研究项目有问题。我有一个带有一个 servlet 的简单项目,我需要一些具有不同范围的 CDI 豆。这部分非常简单,但我必须能够将 HttpSession 注入到我每个 CDI 豆中。为了解决这个问题,我制作了ServletRequestListener来获取HttpServletRequest对象,我将这个对象存储在ThreadLocal对象的应用程序范围的bean中,在这个bean中,我有来自存储的HttpServletRequest的HttpSession对象的生产者方法。之后,我可以在任何CDI豆中注入HttpSession,除了会话范围的bean。会话初始化后,会话被正确注入到该 bean,但对于同一会话中的第二个请求,我有空指针异常,因为会话 bean 是在 RequestInitialized 方法之前创建(或反序列化(的,我的生产者返回 null 值,这是非法的堆栈跟踪。

下面是一个会话中第二个请求的堆栈跟踪:

    org.jboss.weld.exceptions.IllegalProductException: WELD-000052 Cannot return null from a non-dependent producer method:  [method] @Produces @RequestScoped public pl.lab2.cdi.producers.SessionObjectsProducer.getSession()
    org.jboss.weld.bean.AbstractProducerBean.checkReturnValue(AbstractProducerBean.java:217)
    org.jboss.weld.bean.AbstractProducerBean.create(AbstractProducerBean.java:300)
    org.jboss.weld.context.AbstractContext.get(AbstractContext.java:107)
    org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:90)
    org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:104)
    org.jboss.weld.proxies.HttpSession$776413422$Proxy$_$$_WeldClientProxy.getId(HttpSession$776413422$Proxy$_$$_WeldClientProxy.java)
    pl.lab2.bean.SessionBean.toString(SessionBean.java:31)
    java.lang.String.valueOf(String.java:2854)
    java.lang.StringBuilder.append(StringBuilder.java:128)
    org.jboss.weld.context.SerializableContextualInstanceImpl.toString(SerializableContextualInstanceImpl.java:60)
    java.lang.String.valueOf(String.java:2854)
    java.lang.StringBuilder.append(StringBuilder.java:128)
    org.jboss.weld.context.beanstore.AttributeBeanStore.attach(AttributeBeanStore.java:109)
    org.jboss.weld.context.AbstractBoundContext.activate(AbstractBoundContext.java:66)
    org.jboss.weld.servlet.WeldListener.requestInitialized(WeldListener.java:141)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
    org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
    org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671)
    org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930)
    java.lang.Thread.run(Thread.java:724)

和来源:

听者

package pl.lab2.servlet;
import org.apache.log4j.Logger;
import pl.lab2.cdi.BeanManagerHelper;
import pl.lab2.servlet.events.literal.*;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
public class ServletListener implements ServletRequestListener {
    private static final Logger log = Logger.getLogger(ServletListener.class);
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        log.info("request destroyed event");
        BeanManagerHelper.getBeanManagerByJNDI().fireEvent((HttpServletRequest) sre.getServletRequest(), DestroyedLiteral.INSTANCE);
    }
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        log.info("request initialized event");
        BeanManagerHelper.getBeanManagerByJNDI().fireEvent((HttpServletRequest) sre.getServletRequest(), InitializedLiteral.INSTANCE);
    }
}

持有

package pl.lab2.servlet;
import org.apache.log4j.Logger;
import pl.lab2.servlet.events.Destroyed;
import pl.lab2.servlet.events.Initialized;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@ApplicationScoped
public class ServletObjectHolder {
    private static final Logger log = Logger.getLogger(ServletObjectHolder.class);
    private final ThreadLocal<HttpServletRequest> threadRequest = new ThreadLocal<HttpServletRequest>();
    public HttpSession getSession() {
        log.info("get session");
        if (threadRequest.get() != null) {
            return threadRequest.get().getSession();
        }
        return null;
    }
    public void servletRequestInitialized(@Observes @Initialized final HttpServletRequest request) {
        log.info("receive request initialization");
        threadRequest.set(request);
    }
    public void servletRequestDestroyed(@Observes @Destroyed final HttpServletRequest request) {
        log.info("receive request destroyed");
        threadRequest.set(null);
    }
}

制作人

package pl.lab2.cdi.producers;
import org.apache.log4j.Logger;
import pl.lab2.servlet.ServletObjectHolder;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
import java.io.Serializable;
public class SessionObjectsProducer implements Serializable {
    private static final Logger log = Logger.getLogger(SessionObjectsProducer.class);
    @Inject
    private ServletObjectHolder servletObjectHolder;
    @Produces
    @RequestScoped
    public HttpSession getSession() {
        log.info("get session");
        return servletObjectHolder.getSession();
    }
}

会话豆

package pl.lab2.bean;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpSession;
import java.io.Serializable;
@SessionScoped
@Named
public class SessionBean implements Serializable {
    private String name;
    @Inject
    private HttpSession session;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "SessionBean{" +
                "name='" + name + "', " +
                "sessionId='" + session.getId() + "'" +
                '}';
    }
}

和 servlet:

package pl.lab2.servlet;
import org.apache.log4j.Logger;
import org.jboss.weld.context.ConversationContext;
import org.jboss.weld.context.http.Http;
import pl.lab2.bean.ApplicationBean;
import pl.lab2.bean.ConversationBean;
import pl.lab2.bean.RequestBean;
import pl.lab2.bean.SessionBean;
import pl.lab2.cdi.producers.SessionObjectsProducer;
import javax.enterprise.context.Conversation;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ServletDispatcher extends HttpServlet {
    private static final Logger log = Logger.getLogger(ServletDispatcher.class);
    @Inject
    private ApplicationBean applicationBean;
    @Inject
    private SessionBean sessionBean;
    @Inject
    private ConversationBean conversationBean;
    @Inject
    private RequestBean requestBean;
    @Inject
    private Conversation conversation;
    @Inject
    @Http
    private ConversationContext conversationContext;
    @Inject
    private SessionObjectsProducer sessionObjectsProducer;
    @Override
    public void init() throws ServletException {
        super.init();
        conversationContext.setParameterName("cId");
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        this.request(request, response);
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {
        this.request(request, response);
    }
    private void request(HttpServletRequest request, HttpServletResponse response) throws IOException {
        log.info("request started in session " + request.getSession().getId());
        String cid = request.getParameter(conversationContext.getParameterName());
        if (cid != null) {
            conversationContext.activate(cid);
        } else {
            conversationContext.activate();
        }
        takeActions(request);
        updateData(request);
        printState(response.getWriter(), request);
    }
    private void printState(PrintWriter writer, HttpServletRequest request) {
        writer.print("<div>");
        writer.print("<div>Beans:</div>");
        writer.print(applicationBean.toString() + "<br />");
        writer.print(sessionBean.toString() + "<br />");
        writer.print(conversationBean.toString() + "<br />");
        writer.print(requestBean.toString() + "<br />");
        writer.print("</div>");
        writer.print("<div>");
        writer.print("<div>Data:</div>");
        writer.print("session id: " + request.getSession().getId() + "<br />");
        writer.print("conversation id: " + conversation.getId() + "<br />");
        writer.print("</div>");
    }
    private void takeActions(HttpServletRequest request) {
        if ("begin".equals(request.getParameter("conversationState"))) conversation.begin();
        else if ("end".equals(request.getParameter("conversationState"))) conversation.end();
    }
    private void updateData(HttpServletRequest request) {
        if (request.getParameter("application") != null) {
            applicationBean.setName(request.getParameter("application"));
        }
        if (request.getParameter("session") != null) {
            sessionBean.setName(request.getParameter("session"));
        }
        if (request.getParameter("conversation") != null) {
            conversationBean.setName(request.getParameter("conversation"));
        }
        if (request.getParameter("request") != null) {
            requestBean.setName(request.getParameter("request"));
        }
    }
}

为此,我使用来自github的seam/servlet源代码作为示例。

我已经将我当前的代码上传到 dropbox 刚刚构建,在 JBoss 上部署为 7.1.1.Final,转到 localhost:8080/lab2,按两次 F5,您将看到问题。

下面这样简单的事情就可以做你想做的事,就像一个魅力:

import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
// NOTE: this produces a request scoped session object because that's what the OP seems to want
@WebListener
public class SessionProducer implements ServletRequestListener {
    private static ThreadLocal<HttpSession> SESSIONS = new ThreadLocal<>();
    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        SESSIONS.remove();
    }
    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        SESSIONS.set(HttpServletRequest.class.cast(sre.getServletRequest()).getSession());
    }
    @Produces @RequestScoped
    protected HttpSession getSession() {
        return SESSIONS.get();
    }
}

享受!

我发现问题出在会话范围 bean 中的 toString 方法中。在这种方法中,我试图获取会话 ID,但由于某些原因(我认为是日志记录(cdi 在请求初始化之前调用 String 方法。当我从 toString 方法中删除对会话对象的访问时,一切正常。

使用 JSF,事实上,你可以访问你的 httpsession with FacesContext...

我们没有您的所有配置,在这里我看不到此配置的目的,没有人像那样使用 servlet,您可以通过请求参数而不是注入来访问 HTTP 会话......

最新更新