将EJB注入动态映射的servlet

时间:2013-05-05 22:49:06

标签: java java-ee servlets jboss ejb

我有一个过滤器,我在其中映射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() );
        }

    }

然后,当我访问给定的映射时,不会注入EJB。

    @EJB
    private I18nManager i18nManager;

    @Override
    protected void doGet( HttpServletRequest request, HttpServletResponse response )
    throws ServletException, IOException {
        I18nManager i18n = i18nManager; //null
    }

如果我在web.xml中手动创建一个映射,那个servlet中的给定EJB正在工作。 这让我想知道我在运行时注册servlet的事实是不是将这些servlet视为托管。

如果是这样的话,将EJB注入我的servlet的正确方法是什么,而不改变它们通过过滤器进行dinamically注册的方式?

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

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

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

实施的相关部分:

...

@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() );
        }
    }

...

不幸的是,它不会使容器注入EJB并且空指针仍然存在。我目前正在为服务进行自定义类型安全的JNDI查找。显然,这比使用正确的注射要昂贵得多(如果我错了就纠正我,没有做过关于性能的实验)。

使用:
Java EE 6
JBoss AS 7.1

3 个答案:

答案 0 :(得分:3)

Servlet 3.0 Spec,Sect。 4.4.3.5

资源注入 [例如添加了所有组件(Servlet,过滤器和监听器)的@EJB] 以编程方式或以编程方式创建,而不是通过 采用实例的方法,只有在组件为a时才支持 托管Bean 。有关什么是Managed Bean的详细信息,请参阅 Managed Bean规范定义为Java EE 6和JSR 299的一部分。

管理Bean声明

Java EE 6托管bean带有注释@javax.annotation.ManagedBean并且具有无参数构造函数。 JSR 299(CDI)托管bean只需要一个无参数构造函数或带注释的构造函数@javax.inject.Inject

<强>答案

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

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

    或者

  • 启用CDI&amp;包含空beans.xml


修改

即使您是动态创建servlet,容器也必须完成创建。不要认为ServletContext中的创建将支持注入。 Servlet doc在这里很模糊。

使用CDI尝试:

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

答案 1 :(得分:3)

问题似乎与this reported bug有关,但尚未解决。资源解析对于JSF规范定义的Managed Beans来说效果很好,但不适用于CDI Managed Beans。简单地使用@javax.faces.bean.ManagedBean注释动态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");
    }
}

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

修改

动态映射servlet的问题实际上在基础JBoss体系结构中非常深入。他们使用JBossWeb(Tomcat的分叉版本)作为servlet实现,并且在其上下文管理代码的内容中,它确定了通过注入或常规 new 实例化新组件的方法。 Afaik截止日期,您的servlet需要以某种方式进行注释才能通过注入进行处理:我在原始答案中提到了@ManagedBean,但它看起来像使用@WebServlet进行注释也适用。

答案 2 :(得分:0)

首先,在我的测试中,使用Glassfish V3版本可以正常工作。

但是,第二,你很可能违反了Servlet 3.0规范的这一条款。

  

自Servlet 3.0以来,ServletContext中添加了以下方法   启用servlet,过滤器和url的编程定义   他们映射到的模式。这些方法只能在调用期间调用   从contexInitialized初始化应用程序   ServletContextListener实现的方法或来自   ServletContainerInitializer实现的onStartup方法。

值得注意的是,无法从Filter.init()方法调用这些方法。我最初在Servlet.init()方法中尝试过此操作,init方法失败,因为上下文已经初始化。

所以,我的实验没有完全复制你的测试 - 我没有使用Filter.init()方法,而是将代码放在ServletContextListener中。当我这样做时,我的@EJB注释得到了尊重。

编辑:

听起来没有帮助,我建议这是JBoss中的一个错误。当我最初使用Filter的注入尝试原始代码时,Glassfish引发了异常,因为除了前面提到的那样,你不允许进行注射。现在也许这是JBoss的“附加功能”,但显然@EJB注入处理根本不起作用。根据规范,这应该像宣传的那样工作。