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.