如何让Google Guice与JaxRS(Jersey)一起使用

8
我有一个基本的JAXRS服务,可以轻松地公开,但是我希望使用依赖注入API,我认为Google Guice将是最好的选择。考虑到这一点,我尝试集成它,但文档有些复杂,我不得不四处寻找正确的组合:
- Web.xml - 上下文监听器(我应该使用ServletContainer还是GuiceContainer) - 服务 - 是否在服务上注释@Singleton或@Request或什么都不注释(我应该使用@Singleton进行注释——文档说我应该这样做,但然后又说它默认为请求范围) - 是否在构造函数参数上注释@InjectParam
但目前我从Google Guice那里得到了错误,而且这些错误会根据我是否使用@InjectParam注释而改变。
如果我使用@InjectParam进行注释,那么我会得到以下错误:
       Mar 29, 2013 9:52:04 PM com.sun.jersey.spi.inject.Errors processErrorMessages
   SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
   SEVERE: The class com.hillingar.server.dao.interfaces.UserDao is an interface and cannot be instantiated.
   SEVERE: Missing dependency for constructor public com.hillingar.server.SessionUtility(com.hillingar.server.dao.interfaces.UserDao) at parameter index 0

如果我不进行注释,那么我会得到:
    Mar 29, 2013 9:54:59 PM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
  SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 0
  SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 1

这是我的web.xml文件。
    <?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <filter>
        <filter-name>guiceFilter</filter-name>
        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>guiceFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>com.hillingar.server.ServletContextListener</listener-class>
    </listener>

    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
</web-app>

这是我的ServletContextListener

package com.hillingar.server;

import java.util.logging.Logger;

import javax.servlet.ServletContextEvent;

import com.google.inject.Guice;
import com.google.inject.Singleton;
import com.hillingar.server.dao.jdbcImpl.UserJdbc;
import com.hillingar.server.dao.interfaces.UserDao;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import com.sun.jersey.spi.container.servlet.ServletContainer;

public class ServletContextListener implements javax.servlet.ServletContextListener {

    Logger logger = Logger.getLogger(this.getClass().getName());

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
    }

        /*
         * Covered in URL
         * https://code.google.com/p/google-guice/wiki/ServletModule
         */
    @Override
    public void contextInitialized(ServletContextEvent arg0) {

            // Note the user of JerseyServletModule instead of ServletModule
            // otherwise the expected constructor injection doesn't happen
            // (just the default constructor is called)
            Guice.createInjector(new JerseyServletModule() {
                @Override
                protected void configureServlets() {

                    /*
                     * Note: Every servlet (or filter) is required to be a 
                     * @Singleton. If you cannot annotate the class directly, 
                     * you must bind it using bind(..).in(Singleton.class), 
                     * separate to the filter() or servlet() rules. 
                     * Mapping under any other scope is an error. This is to 
                     * maintain consistency with the Servlet specification. 
                     * Guice Servlet does not support the 
                     * deprecated SingleThreadModel.
                     */
                    bind(SecurityFilter.class).in(Singleton.class);
                    bind(ServletContainer.class).in(Singleton.class);

                    /*
                     * Filter Mapping
                     * 
                     * This will route every incoming request through MyFilter, 
                     * and then continue to any other matching filters before 
                     * finally being dispatched to a servlet for processing.
                     * 
                     */

                    // SECURITY - currently disabled
                    // filter("/*").through(SecurityFilter.class);

                    /*
                     * Registering Servlets
                     * 
                     * This registers a servlet (subclass of HttpServlet) called 
                     * ServletContainer, the same one that I would have used in 
                     * the web.xml file, to serve any web requests with the 
                     * path /rest/*  i.e. ...
                     * 
                        <servlet>
                            <servlet-name>ServletAdaptor</servlet-name>
                            <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
                            <load-on-startup>1</load-on-startup>
                        </servlet>
                        <servlet-mapping>
                            <servlet-name>ServletAdaptor</servlet-name>
                            <url-pattern>/rest/*</url-pattern>
                        </servlet-mapping>

                     */
                    serve("/rest/*").with(ServletContainer.class); // JAX-RS

                    // Using this and it starts bitching about
                    // com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.
                    // So presumably wants an Application class that enumerates 
                    // all my services?
                    //serve("/rest/*").with(GuiceContainer.class);

                    /*
                     * Bindings
                     */
                    bind(UserDao.class).to(UserJdbc.class);
                    bind(SessionUtility.class);
                }
            });
    }

}

这是我的用户服务(UserService)。
package com.hillingar.server.rest;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;

import com.hillingar.server.SessionUtility;
import com.hillingar.server.dao.interfaces.UserDao;
import com.hillingar.server.model.User;
import com.hillingar.server.model.dto.AuthenticationResponse;

@Path("/user")
@Produces("application/json")
@Consumes({"application/xml","application/json"})
@Singleton // <-- Added Singleton here
public class UserService {

    private UserDao userDao;
    private SessionUtility sessionManager;

        /*
           Error if I annotate with @InjectParam...

           Mar 29, 2013 9:52:04 PM com.sun.jersey.spi.inject.Errors processErrorMessages
           SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
           SEVERE: The class com.hillingar.server.dao.interfaces.UserDao is an interface and cannot be instantiated.
           SEVERE: Missing dependency for constructor public com.hillingar.server.SessionUtility(com.hillingar.server.dao.interfaces.UserDao) at parameter index 0

           Error If I don't annotate at all...
            Mar 29, 2013 9:54:59 PM com.sun.jersey.spi.inject.Errors processErrorMessages
            SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
              SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 0
              SEVERE: Missing dependency for constructor public com.hillingar.server.rest.UserService(com.hillingar.server.dao.interfaces.UserDao,com.hillingar.server.SessionUtility) at parameter index 1

           (both output Initiating Jersey application, version 'Jersey: 1.13 06/29/2012 05:14 PM')
        */
    @Inject
    public UserService(UserDao userDao, SessionUtility sessionManager) {
        this.userDao = userDao;
                this.sessionManager = sessionManager;
    }

    @GET
    public List<User> test(@Context HttpServletRequest hsr) {
                // USER DAO IS ALWAYS NULL - CONSTRUCTOR INJECTION NOT WORKING
        User loggedInUser = userDao.findBySessionId(hsr.getSession().getId());
                ...
        return users;
    }

}

如果将UserDaoSessionUtility作为字段注入,而不是作为构造函数的一部分,会发生什么? - condit
我将构造函数更改为无参数(并删除@Inject),然后为UserDao和SessionUtility创建了getter和setter(setter带有@Inject)。但仍然没有被调用。 - RobbiewOnline
今天早上我改了两件事情,然后它按预期开始工作了,1)我将我的servlet上下文监听器更改为扩展GuiceServletContextListener并覆盖getInjector方法,2)在注入器中更改回使用serve("/rest/*").with(GuiceContainer.class); 这次它成功了! - RobbiewOnline
嗨Spiky,你能告诉我们在App Engine中你的init时间是多少吗?我计算了一个Resource类需要15秒钟。谢谢。 - Michele Orsi
嗨,Michele - 最终我停止使用GAE了,我记不清时间了。 - RobbiewOnline
1个回答

5

将ServletContextListener更改为

package com.hillingar.server;

import java.util.logging.Logger;

import javax.servlet.ServletContextEvent;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.google.inject.servlet.GuiceServletContextListener;
import com.hillingar.server.dao.jdbcImpl.UserJdbc;
import com.hillingar.server.dao.interfaces.UserDao;
import com.hillingar.server.rest.UserService;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
import com.sun.jersey.spi.container.servlet.ServletContainer;

// (1) Extend GuiceServletContextListener
public class ServletContextListener extends GuiceServletContextListener {

    Logger logger = Logger.getLogger(this.getClass().getName());

    // (1) Override getInjector
    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new JerseyServletModule() {
            @Override
            protected void configureServlets() {
                bind(SecurityFilter.class).in(Singleton.class);
                bind(UserService.class);// .in(Singleton.class);
                bind(ServletContainer.class).in(Singleton.class);

                // (2) Change to using the GuiceContainer
                serve("/rest/*").with(GuiceContainer.class); // <<<<---

                bind(UserDao.class).to(UserJdbc.class);
                bind(SessionUtility.class);
            }
        });
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接