Guice和Wicket: 使用SessionScoped注入

3
我有一个使用Guice [v3]的Wicket [v6]应用程序 - 我目前在仓库操作中使用依赖注入,现在我想将其扩展到使用每个用户会话的服务(每个用户的会话一个服务)。我已经阅读了官方文档、各种博客文章和问题,但我不确定我是否使用了正确的方法。
我的两个问题: 1. 我是否使用了正确的方式? 2. 我需要为依赖于SessionScoped注入的类运行TestNG测试,还需要什么特殊设置吗?
我的设置: web.xml:
<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.xxx.CustomServletConfig</listener-class>

我的应用程序初始化:

@Override
protected void init()
{
    super.init();
    getResourceSettings().setResourcePollFrequency(null);
    getMarkupSettings().setStripWicketTags(true);
    getDebugSettings().setDevelopmentUtilitiesEnabled(true);
    GuiceComponentInjector injector = new GuiceComponentInjector(this, new WebModule(), new GuiceModule());;
}

CustomServletConfig:

public class CustomServletConfig  extends GuiceServletContextListener {

    @Override
    protected Injector getInjector() {
        return Guice.createInjector(new GuiceModule(), new WebModule());
    }

Web模块:

public static class WebModule extends ServletModule {

    @Override
    protected void configureServlets() {
        bind(WebApplication.class).toProvider(WicketGuiceAppProvider.class).asEagerSingleton();  

        bind(IUserService.class).to(UserService.class).in(ServletScopes.SESSION);

        Map<String, String> params = new HashMap<String, String>();    
        params.put(WicketFilter.FILTER_MAPPING_PARAM, "/*");  

        filter("/*").through(WicketGuiceFilter.class, params);  
    }
}

在一个示例页面中,我有以下内容:
@Inject
IUserService userService

...

userService.doSomething

在进行单元测试的userService.doSomething期间,我遇到了Guice OutOfScopeException错误,指向我的ServletModule中的绑定: 在自定义提供者中出现错误,com.google.inject.OutOfScopeException?:无法访问作用域对象。我们当前不在HTTP Servlet请求内,或者您可能已经忘记为此请求应用com.google.inject.servlet.GuiceFilter作为servlet过滤器。 我的配置是否正确,需要以不同的方式运行单元测试(我只是使用WicketTester启动我的应用程序),还是我的设计有问题?

1
你能添加单元测试的代码吗?你在服务器上试过了吗? - mrak
1个回答

3
这是一个非常常见的错误。
所有在 ServletScopes 或 RequestScopes 中的实体都应该作为 Providers 传递。
因此,您的代码应该是:
@Inject
Provider<IUserService> userServiceProvider

public IUserService getUserService() {
  userServiceProvider.get(); 
}

为什么会这样?只要在 Stage.DEVELOPMENT 中使用并且父类没有被急切地创建,一切都很好。如果你将父类绑定为 asEagerSingleton 或者切换到 Stage.PRODUCTION,那么你的类就会在启动时急切地被创建。否则,它们只有在访问时才会以懒加载的方式被创建(很可能是在第一次请求时)。
然后你的问题就出现了。你的 WebApplication 在启动时被急切地初始化,然后 guice 尝试注入所有子依赖项,并找到 SessionScope 中的 IUserService。问题在于你当前不在 GuiceFilter 中,也没有请求,因此 guice 无法确定当前会话或创建新会话。因此这些作用域不能被访问。你现在在 ContextListener 中,应用程序正在急切地实例化。如果你只使用 Singleton 而不是 asEagerSingleton,由于惰性加载,一切都可能正常运行。
无论如何,将Session和Request作为Providers传递是最佳实践。您可以在这里了解更多关于Providers这里(还有一个很好的急切vs.懒加载比较表)了解Scopes

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