有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java将EJB注入到动态映射的servlet中

我有一个过滤器,在这里我以友好的方式映射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给定的EJB正在该servlet中工作。 这让我想知道,如果我在运行时注册servlet,容器就不会把这些servlet看作托管的。

如果是这样的话,在不改变EJB通过过滤器进行动态注册的方式的情况下,将EJB注入到我的servlet中的正确方法是什么

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

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

<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
JBossAS7.1


共 (3) 个答案

  1. # 1 楼答案

    这个问题似乎与this reported bug有关,但尚未解决。对于JSF规范定义的托管bean,资源解析工作得很好,但对于CDI托管bean则不行。简单地用@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实例化新组件。到目前为止,您的servlet需要以某种方式进行注释,以便通过注入进行处理:我在最初的回答中提到了@ManagedBean,但它看起来像是使用@WebServlet works进行注释

  2. # 2 楼答案

    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)
    
  3. # 3 楼答案

    首先,在我的测试中,使用Glassfish V3的一个版本,它工作得很好

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

    The following methods are added to ServletContext since Servlet 3.0 to enable programmatic definition of servlets, filters and the url pattern that they map to. These methods can only be called during the initialization of the application either from the contexInitialized method of a ServletContextListener implementation or from the onStartup method of a ServletContainerInitializer implementation.

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

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

    编辑:

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