Guice and Wicket: использование инъекций SessionScoped

У меня есть работающее приложение Wicket [v6] с Guice [v3] - я прямо сейчас использовал внедрение зависимостей для операций с репозиторием, и я хочу потратить его на использование сервисов, находящихся в области сеанса (по одному на сеанс пользователя). Я прочитал официальную документацию, различные сообщения в блогах и вопросы здесь, но я не уверен, что использую правильный подход.

У меня есть два вопроса: 1. Я использую правильный путь? 2. Нужно ли что-то особенное для запуска тестов TestNG в классах, основанных на инъекциях SessionScoped?

Моя настройка: 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>

MyApplication init:

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

WebModule:

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, либо вы, возможно, забыли применить com.google.inject.servlet.GuiceFilter? в качестве фильтра сервлетов для этого запроса.

Моя конфигурация в порядке, и мне нужно выполнить модульные тесты по-другому (я просто запускаю свое приложение с помощью WicketTester), или мой дизайн неисправен?

1 ответ

Это очень распространенная ошибка.

Все объекты в ServletScopes или же RequestScopes должен быть передан как Providers,

Итак, ваш код должен быть:

@Inject
Provider<IUserService> userServiceProvider

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

Почему так?! Все хорошо, пока вы используете его в Stage.DEVELOPMENT и родительский класс не создан с нетерпением. Если вы связываете родительский класс как asEagerSingleton или переключитесь на Stage.PRODUCTION ваши классы начинают создаваться с нетерпением во время запуска. В противном случае они создаются ленивым способом, только когда к ним обращаются (очень вероятно во время первого запроса).

И там ваша проблема выходит на сцену. Ваш WebApplication инициализируется с нетерпением во время запуска. Затем guice пытается внедрить все дочерние зависимости и находит IUserService который является полевой инъекцией в SessionScope, Проблема в том, что вы в данный момент не находитесь внутри GuiceFilter и нет запроса, поэтому guice не может определить текущий сеанс или создать новый. Таким образом, эти области не могут быть достигнуты. Вы в настоящее время в вашем ContextListener и ваша заявка создается с нетерпением. Все могло бы быть хорошо, если бы вы использовали просто Singleton вместо asEagerSingleton из-за ленивой загрузки.

В любом случае, передача объектов Session и Request в качестве провайдеров является наилучшей практикой. Вы можете узнать больше о провайдерах здесь и Scopes здесь (есть также хорошая таблица со сравнением загрузки с нетерпением и ленивостью)

Другие вопросы по тегам