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<simpleuser> {
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;
}
}</simpleuser>
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.