Jakarta ee servlet项目中的HttpSession CDI注入

Jakarta ee servlet项目中的HttpSession CDI注入,jakarta-ee,servlets,cdi,httpsession,Jakarta Ee,Servlets,Cdi,Httpsession,我的学习计划有问题。我有一个带有一个servlet的简单项目,我需要一些具有不同作用域的CDIBean。这一部分非常简单,但我必须能够将HttpSession注入到每个CDIBean中。为了解决这个问题,我让ServletRequestListener获取HttpServletRequest对象,我将这个对象存储在ThreadLocal对象中的应用程序作用域bean中,在这个bean中,我从存储的HttpServletRequest中获得了HttpSession对象的producer方法。之后,

我的学习计划有问题。我有一个带有一个servlet的简单项目,我需要一些具有不同作用域的CDIBean。这一部分非常简单,但我必须能够将HttpSession注入到每个CDIBean中。为了解决这个问题,我让ServletRequestListener获取HttpServletRequest对象,我将这个对象存储在ThreadLocal对象中的应用程序作用域bean中,在这个bean中,我从存储的HttpServletRequest中获得了HttpSession对象的producer方法。之后,我可以将HttpSession注入任何CDIBean,会话范围的bean除外。会话在会话初始化之后被正确地注入该bean,但是对于同一会话中的第二个请求,我有空指针异常,因为会话bean是在RequestInitialized方法之前创建(或反序列化)的,并且我的生产者返回空值,根据stacktrace,这是非法的

以下是一个会话中第二个请求的stacktrace:

    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);
    }
}
会话bean

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"));
        }
    }
}
包pl.lab2.servlet;
导入org.apache.log4j.Logger;
导入org.jboss.weld.context.ConversationContext;
导入org.jboss.weld.context.http.http;
导入pl.lab2.bean.ApplicationBean;
导入pl.lab2.bean.ConversationBean;
导入pl.lab2.bean.RequestBean;
导入pl.lab2.bean.SessionBean;
导入pl.lab2.cdi.producer.SessionObjectsProducer;
导入javax.enterprise.context.Conversation;
导入javax.inject.inject;
导入javax.servlet.ServletException;
导入javax.servlet.http.HttpServlet;
导入javax.servlet.http.HttpServletRequest;
导入javax.servlet.http.HttpServletResponse;
导入java.io.IOException;
导入java.io.PrintWriter;
公共类ServletDispatcher扩展了HttpServlet{
私有静态最终记录器log=Logger.getLogger(ServletDispatcher.class);
@注入
私有应用程序bean应用程序bean;
@注入
私有SessionBean SessionBean;
@注入
私有会话bean会话bean;
@注入
私有请求bean请求bean;
@注入
私人谈话;
@注入
@Http
私人会话语境;
@注入
私有会话对象制作者会话对象制作者;
@凌驾
public void init()引发ServletException{
super.init();
setParameterName(“cId”);
}
@凌驾
受保护的void doGet(HttpServletRequest请求、HttpServletResponse响应)抛出ServletException,
IOException{
这个。请求(请求,响应);
}
@凌驾
受保护的void doPost(HttpServletRequest请求、HttpServletResponse响应)引发ServletException,
IOException{
这个。请求(请求,响应);
}
私有void请求(HttpServletRequest请求、HttpServletResponse响应)引发IOException{
log.info(“请求在会话中启动”+request.getSession().getId());
字符串cid=request.getParameter(conversationContext.getParameterName());
如果(cid!=null){
会话上下文激活(cid);
}否则{
activate();
}
采取行动(请求);
更新数据(请求);
printState(response.getWriter(),request);
}
私有void printState(PrintWriter编写器,HttpServletRequest){
作者:印刷体(“”);
作者。印刷(“豆:”);
print(applicationBean.toString()+“
”; print(sessionBean.toString()+“
”); writer.print(conversationBean.toString()+“
”); writer.print(requestBean.toString()+“
”); 作者:印刷体(“”); 作者:印刷体(“”); writer.print(“数据:”); writer.print(“会话id:+request.getSession().getId()+”
); writer.print(“会话id:+conversation.getId()+”
); 作者:印刷体(“”); } 私有无效操作(HttpServletRequest){ if(“begin”.equals(request.getParameter(“conversationState”))conversation.begin(); else if(“end”.equals(request.getParameter(“conversationState”))conversation.end(); } 私有void updateData(HttpServletRequest){ if(request.getParameter(“应用程序”)!=null){ setName(request.getParameter(“应用”); } if(request.getParameter(“会话”)!=null){ setName(request.getParameter(“session”); } if(request.getParameter(“对话”)!=null){ setName(request.getParameter(“conversation”); } if(request.getParameter(“request”)!=null){ setName(request.getParameter(“request”); } } }
为此,我使用来自github的seam/servlet源代码作为示例


我已经上传了我当前的代码,只需构建,作为7.1.1.Final部署在JBoss上,转到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();
    }

}
import javax.enterprise.context.requestscope;
导入javax.enterprise.inject.products;
导入javax.servlet.ServletRequestEvent;
导入javax.servlet.ServletRequestListener;
导入javax.servlet.annotation.WebListener;
导入javax.servlet.http.HttpServletRequest;
导入javax.servlet.http.HttpSession;
//注意:这会产生一个请求范围的会话对象,因为OP似乎想要这样
@网络监听器
公共类SessionProducer实现ServletRequestListener{
私有静态ThreadLocal会话=新ThreadLocal();
@凌驾
公共无效请求已销毁(ServletRequestEvent sre){
SESSIONS.remove();
}
@凌驾
public void requestInitialized(ServletRequestEvent sre){
SESSIONS.set(HttpServletRequest.class.cast(sre.getServletRequest()).getSession());
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();
    }

}