Java 将EJB注入动态映射servlet

Java 将EJB注入动态映射servlet,java,jakarta-ee,servlets,jboss,ejb,Java,Jakarta Ee,Servlets,Jboss,Ejb,我有一个过滤器,在这里我以友好的方式映射servlet类: @Override public void init( FilterConfig filterConfig ) throws ServletException { servletContext = filterConfig.getServletContext(); File directory = getConventionDirectory(); FileSystemI

我有一个过滤器,在这里我以友好的方式映射servlet类:

    @Override
    public void init( FilterConfig filterConfig ) throws ServletException {
        servletContext = filterConfig.getServletContext();

        File directory = getConventionDirectory();
        FileSystemInspector fileInspector = new FileSystemInspector();
        Set<ActionInfoData> actions = fileInspector.getActions( directory );

        for ( ActionInfoData action : actions ) {
            servletContext
                .addServlet( action.getServletName(), action.getClassName() )
                .addMapping( action.getServletMapping() );
        }

    }
如果我在web.xml中手动创建映射,那么给定的EJB在该servlet中工作。 这让我想知道,如果我在运行时注册servlet,容器不考虑那些servlet作为托管。< /P> 如果是这样的话,在不改变EJB通过过滤器进行动态注册的方式的情况下,将EJB注入到我的servlet中的正确方法是什么

通过JNDI是注入EJB的唯一方法吗

编辑1: 我已尝试使用
web.xml
中的以下代码实现“Will”建议的
ServletContextListener
类:

<listener>
        <listener-class>com.megafone.web.filter.convention.InitServlet</listener-class>
    </listener>

com.megafone.web.filter.convention.InitServlet
以及实施的相关部分:

...

@Override
    public void contextInitialized( ServletContextEvent sce ) {
        ServletContext servletContext = sce.getServletContext();

        FileSystemInspector fileInspector = new FileSystemInspector();
        Set<ActionInfoData> actions = fileInspector.getActions( getConventionDirectory() );

        for ( ActionInfoData action : actions ) {
            servletContext
                .addServlet( action.getServletName(), action.getClassName() )
                .addMapping( action.getServletMapping() );
        }
    }

...
。。。
@凌驾
public void contextInitialized(ServletContextEvent sce){
ServletContext=sce.getServletContext();
FileSystemInspector fileInspector=新建FileSystemInspector();
Set actions=fileInspector.getActions(getConventionDirectory());
for(ActionInfoData操作:操作){
清除请求
.addServlet(action.getServletName(),action.getClassName())
.addMapping(action.getServletMapping());
}
}
...
不幸的是,它不会使容器注入EJB,空指针仍然存在。我目前正在对该服务进行自定义类型安全的JNDI查找。显然,这比使用正确的注入要昂贵得多(如果我错了,请纠正我,我还没有做过关于性能的实验)

使用:
Java EE 6

JBossAS7.1首先,在我的测试中,它使用GlassFishV3版本运行良好

但是,第二,您可能与Servlet3.0规范的这一条款有冲突

自Servlet3.0发布以来,以下方法被添加到ServletContext中 启用servlet、筛选器和url的编程定义 它们映射到的模式。这些方法只能在 从上下文初始化应用程序 ServletContextListener实现的方法或来自 ServletContainerInitializer实现的onStartup方法

值得注意的是,这些方法不能从
Filter.init()方法调用。我最初在一个
Servlet.init()
方法中尝试了这个方法,但是
init
方法失败了,因为上下文已经初始化

因此,我的实验没有完全复制您的测试——我没有为此使用
Filter.init()
方法,而是将代码放在
ServletContextListener
中。当我这么做的时候,我的
@EJB
注释很荣幸

编辑:


虽然听起来没有帮助,但我认为这是JBoss中的一个bug。当我最初尝试从过滤器注入您的原始代码时,Glassfish抛出了一个异常,因为您不允许在我前面提到的地方进行注入。现在,这可能是JBoss的一个“新增功能”,但显然@EJB注入处理根本不起作用。根据规范,这应该像广告宣传的那样工作。

Servlet 3.0规范,第节。4.4.3.5

添加了所有组件(servlet、筛选器和侦听器)上的资源注入[例如@EJB] 以编程方式或以编程方式创建,而不是通过 仅当组件是一个实例时,才会支持使用实例的方法 托管Bean。有关什么是托管Bean的详细信息,请参阅 托管Bean规范定义为JavaEE6和JSR299的一部分

托管Bean声明

JavaEE6托管bean被注释为
@javax.annotation.ManagedBean
,并且具有无参数构造函数。JSR299(CDI)管理的bean只需要一个无参数构造函数或一个带注释的构造函数
@javax.inject.inject

回答

要启用资源注入,您需要:

  • 在动态添加的servlet上放置
    @ManagedBean
    注释

  • 启用CDI&包括一个空的
    beans.xml


编辑

即使是动态创建servlet,容器进行创建也是很重要的。不要认为ServletContext中的创建将支持注入。这一点很模糊

使用CDI尝试:

 servletContext.addServlet("your servlet name", @Inject YourServletClass servlet)

这个问题似乎与尚未解决的问题有关。对于JSF规范定义的托管bean,资源解析工作得很好,但对于CDI托管bean则不行。只需使用
@javax.faces.bean.ManagedBean
注释动态servlet类就可以解决这个问题(是的,这是一个相当丑陋的解决方案):

使用JEE6(ofc)和JBoss 7.1.1和7.2.0(EAP 6.1.0 Alpha)进行测试

编辑


动态映射servlet的问题实际上在基本JBoss体系结构中非常严重。他们使用JBossWeb(Tomcat的一个分叉版本)作为servlet实现,在其上下文管理代码的核心中,它决定是通过注入实例化新组件还是通过常规的新组件实例化新组件。到目前为止,您的servlet需要以某种方式进行注释,以便通过注入进行处理:我在最初的回答中提到了@ManagedBean,但看起来使用@WebServlet进行注释也很有效。

感谢您的回复。不幸的是,当通过ServletContextListener加载时,它不起作用。我已经编辑了上面的问题,您可能会告诉我是否做错了什么。好的,我在扩展HttpServlet的类中尝试了
@javax.annotation.ManagedBean
注释(正在进行动态映射的类)。我的豆子没有注射。我尝试通过放置一个
 servletContext.addServlet("your servlet name", @Inject YourServletClass servlet)
@ManagedBean
public class DynServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @EJB
    private LoginService loginService;

    public DynServlet() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        response.getOutputStream().println(
                "Request made to: " + getClass().getSimpleName());
        response.getOutputStream().println("Login Service: " + loginService);

        return;
    }
}

@WebListener
public class DynamicServletLoadListener implements ServletContextListener {

    public DynamicServletLoadListener() {
        super();
    }

    @Override
    public void contextDestroyed(final ServletContextEvent contextEvent) {
        return;
    }

    @Override
    public void contextInitialized(final ServletContextEvent contextEvent) {
        contextEvent.getServletContext().addServlet("dynservlet", DynServlet.class)
                .addMapping("/services/dynservlet");
    }
}