【问题标题】:Injecting an EJB into a dynamic mapped servlet将 EJB 注入动态映射的 servlet
【发布时间】:2013-05-05 22:49:05
【问题描述】:

我有一个过滤器,我在其中动态映射 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 注入我的 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
JBoss AS 7.1

【问题讨论】:

    标签: java jakarta-ee servlets jboss ejb


    【解决方案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 实例化新组件。到目前为止,Afaik 需要以某种方式对您的 servlet 进行注释,以便通过注入对其进行处理:我在原始答案中提到了 @ManagedBean,但看起来使用 @WebServlet 进行注释也可以。

    【讨论】:

    • 没有注释每个 servlet 的任何解决方案? =(
    • @FagnerBrack - 到目前为止我还没有找到。是您无权访问 servlet 源的问题吗?还是说它们太多了?
    • 实际上我正在创建一个映射约定,以根据给定的 jsp 和 servlet 位置(文件系统)动态创建 Http URI 路径。 Java EE 的主要用途是用于后端管理。在这个项目中,由于 JSF 的限制性和利用 HTML5 Boilerplate、jQuery、requirejs、bootstrap(自定义)和 JSF 没有内置的所有前端酷炫特性,我没有使用 JSF。使用注释的主要问题是我应该在未来将它应用到每个班级,而不是将此类任务委派给自动流程。只是方便。
    【解决方案2】:

    Servlet 3.0 规范,Sect。 4.4.3.5

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

    托管 Bean 声明

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

    回答

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

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

    • 启用 CDI 并包含一个空的 beans.xml


    编辑

    即使您是动态创建 servlet,容器也很重要。不要认为在 ServletContext 中创建会支持注入。 Servlet doc 在这里非常模糊。

    使用 CDI 尝试:

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

    【讨论】:

    • 好的,我在扩展 HttpServlet(动态映射的那个)的类中尝试了 @javax.annotation.ManagedBean 注释。我的豆子没有被注射。我尝试通过在 META-INF 目录中放置一个空 beans.xml 来启用 CDI。使用@Inject 时仍然没有被注入。知道可能是什么吗?不过,我将根据目前获得的信息进行几次测试。
    • 当 ServletContext 创建 Servlet 时可能会发生这种情况。强制容器创建 Servlet- 对于 CDI,调用 servletContext.addServlet("your servlet name", @Inject YourServletClass servlet)
    • 只是为了澄清@GlenBest,“定义为 Java EE 6 和 JSR 299 的一部分”。 JSR 299 定义了托管 Bean,Java EE 6 定义了其他支持注入的类,包括 servlet(规范的第 69 页)。无需采取额外的步骤来使 Servlet 成为 ManagedBean。
    • Java EE 6 (JSR 318) 定义了“基础”托管 Bean (jcp.org/aboutJava/communityprocess/final/jsr316/index.html)。 JSR 299 定义了扩展的托管 Bean。需要采取额外的步骤 - Servlet 规范明确表示不支持注入,但托管 bean 除外。
    • 真正缺少的部分是使用已经注入的 Servlet 来创建动态映射。谢谢格伦
    【解决方案3】:

    首先,在我的测试中,使用 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 中的一个错误。当我最初尝试使用来自过滤器的注入的原始代码时,Glassfish 抛出了一个异常,因为除了我之前提到的地方,不允许你进行注入。现在也许这是 JBoss 的“附加功能”,但显然@EJB 注入处理根本不起作用。根据规范,这应该像宣传的那样工作。

    【讨论】:

    • 感谢您的回复。不幸的是,它在通过 ServletContextListener 加载时不起作用。我已经编辑了上面的问题,如果我做错了什么,你可以告诉我。
    猜你喜欢
    • 2023-03-31
    • 2014-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 2012-06-03
    • 1970-01-01
    相关资源
    最近更新 更多