IoC Guice, a ręczne tworzenie (specyficznych) obiektów
Pracuję dzielnie nad archetypem – stosem technologicznym dla mojej przyszłej działalności twórczej. Przy okazji naciąłem się na coś co już co najmniej dwa razy przerabiałem i nie powinienem mieć problemów. Na problem ręcznego tworzenia obiektów, których zależności są wstrzykiwane.
Po co ręcznie tworzysz obiekty?
Nie są to zwykłe obiekty, ale bardzo specyficzne – interceptory. Tworzymy je w ramach metody configure z klasy AbstractModule. Musimy je stworzyć ręcznie i nasze pole manewru w tej dziedzinie jest ograniczone. Problem zaczyna się w momencie gdy:
- Interceptor ma pewne zależności, które powinny być zarządzane przez kontener IoC.
- Zależności te mają inny zasięg niż aplikacja.
By wam nie było smutno – przykładzik:
Listing 1. Interceptor sprawdzający uprawnienia
class SecuredMethodInterceptor implements MethodInterceptor {
@Inject
private Subject subject;
public Object invoke(MethodInvocation invocation) throws Throwable {
if(subject.isAuthenticated()){
System.out.println("OK");
}
return invocation.proceed();
}
}
Jeżeli zrobimy takie coś w aplikacji webowej gdzie subject ma ustawiony zakres na sesję to dostaniemy
Listing 2. Błąd konfiguracji kontenera IoC
Error in custom provider, com.google.inject.OutOfScopeException: Cannot access scoped object. Either we are not currently inside an HTTP Servlet request, or you may have forgotten to apply com.google.inject.servlet.GuiceFilter as a servlet filter for this request.
Jak temu zaradzić?
W takim wypadku należy zastosować wstrzyknięcie providera. Oczywiście problemem może być brak takowego, ale w takim przypadku należy zastosować Assisted Injection plus providera w ramach obiektu objętego danym zakresem. Sprawa skomplikowana i nie do końca przeze mnie obczajona.
Zatem prawidłowa klasa interceptora będzie wyglądać tak:
Listing 3. Interceptor sprawdzający uprawnienia v2
class SecuredMethodInterceptor implements MethodInterceptor {
@Inject
private Provider<Subject> subjectProvider;
public Object invoke(MethodInvocation invocation) throws Throwable {
Subject subject = subjectProvider.get();
if(subject.isAuthenticated()){
System.out.println("OK");
}
return invocation.proceed();
}
}
W takim wypadku wstrzyknięty provider sam zadba o odpowiedni zakres.