Assisted Injection bez Assisted

W Guice jest sobie taki mechanizm co nazywa się Assisted Injection. Generalnie jest on czymś pośrednim pomiędzy zwykłym wstrzykiwaniem zależności dokonywanym przez injector, a ręcznym ustawianiem pól za pomocą providera. Mechanizm jest całkiem fajny, ale postanowiłem wykonać podobną pracę bez jego użycia. Pytanie dlaczego? Mechanizm skrywający pod maską tej biblioteki (AI jest rozszerzeniem dla Guice) działa w ten sposób, że nie tworzy obiektów z pożądanej klasy, ale czasami (nie obczaiłem jeszcze kiedy dokładanie) nakłada na nie tzw. enchanted object, które są zwykłymi dynamicznymi proxy… tak psuje to współpracę z JPA.

Kod jest z serii fuj…

Co niestety musimy

na poziomie aplikacji, czy to web czy to EE czy to SE należy upublicznić instancję injectora. Bez tego ani rusz.
Następnie tworzymy sobie moduł w którym konfigurujemy providera naszych obiektów:

Listing 1. konfiguracja w modułu

SimpleUserProvider simpleUserProvider = new SimpleUser.SimpleUserProvider();
bind(SimpleUserProvider.class).toInstance(simpleUserProvider);
bind(SimpleUser.class).toProvider(simpleUserProvider);
requestStaticInjection(SimpleUser.class);

Sam provider jest stosunkowo prosty:

Listing 2. Priovider

static class SimpleUserProvider implements Provider { 
                                                                  
	private transient EntityManager em;                           
                                                                  
	@Override                                                     
	public SimpleUser get() {                                     
		final SimpleUser simpleUser = new SimpleUser();           
		App.injector.injectMembers(simpleUser);                   
		return simpleUser;                                        
	}                                                             
                                                                  
	public SimpleUser getByName(String name) {                    
		if (em == null)                                           
			em = App.injector.getInstance(EntityManager.class);   
		SimpleUser find = em.find(SimpleUser.class, name);        
		if (find != null)                                         
			App.injector.injectMembers(find);                     
		return find;                                              
	}                                                             
}

Jak widać dostęp do injectora jest niezbędny by uzyskać dostęp do EntityManagera. Generalnie jeżeli w takim providerze będziemy chcieli coś wstrzyknąć to dostaniemy CreationException + NPE bez podania przyczyny. Generalnie idzie w maliny.

Co możemy

Ano możemy sobie tak skonfigurować klasę SimpleUser, by była „samozarządzająca”:

Listing 3. Klasa SimpleUser

@Entity
public class SimpleUser {

	@Inject
	private static transient SimpleUserProvider provider;

	@Inject
	private transient EntityManager em;

	@Id
	private String name;

	SimpleUser() {

	}
	
	public String name(){
		return name;
	}

	public void save() {
		em.getTransaction().begin();
		em.persist(this);
		em.flush();
		em.getTransaction().commit();
	}

	public static SimpleUser get(String name) {
		SimpleUser simpleUser = provider.getByName(name);
		if (simpleUser == null) {
			simpleUser = provider.get();
			simpleUser.name = name;
		}
		return simpleUser;
	}

}

Podsumowanie

Wadą tego rozwiązania jest brzydota. Nie dość, że dajemy dostęp do injectora, to jeszcze musimy silnie związać się z frameworkiem używając klas z biblioteki Guice, a nie z javax.inject. Zaletą jest możliwość uniknięcia dziwnego zachowania Guice i tworzonych przez niego proxy.

Napisz odpowiedź

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax