Guice Chame o método init depois de instanciar um objeto
-
21-09-2019 - |
Pergunta
É possível dizer ao Guice chamar algum método (ou seja, init ()) depois de instanciar um objeto do tipo determinado?
Procuro funcionalidade semelhante à anotação @PostConstruct no EJB 3.
Solução
Na verdade, é possível.
Você precisa definir um TypeListener
Para iniciar a funcionalidade. Algo como as seguintes seguintes na definição do seu módulo:
bindListener(Matchers.subclassesOf(MyInitClass.class), new TypeListener() {
@Override
public <I> void hear(final TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
typeEncounter.register(new InjectionListener<I>() {
@Override
public void afterInjection(Object i) {
MyInitClass m = (MyInitClass) i;
m.init();
}
});
}
});
Outras dicas
Você pode simplesmente adicionar o @Inject
anotação para o seu init()
método. Ele será executado automaticamente depois que o objeto for instanciado.
Eu gosto http://code.google.com/p/mycila/wiki/mycilaguice. Isso suporta Guice 3, exceto http://code.google.com/p/guiceyfruit.
Guiceyfruit faz o que você procura por métodos anotados com @PostConstruct
ou implementando a primavera InitializingBean
. Também é possível escrever seus próprios ouvintes para fazer isso. Aqui está um exemplo que chama um público init()
método após os objetos são criados.
import com.google.inject.*;
import com.google.inject.matcher.*;
import com.google.inject.spi.*;
public class MyModule extends AbstractModule {
static class HasInitMethod extends AbstractMatcher<TypeLiteral<?>> {
public boolean matches(TypeLiteral<?> tpe) {
try {
return tpe.getRawType().getMethod("init") != null;
} catch (Exception e) {
return false;
}
}
public static final HasInitMethod INSTANCE = new HasInitMethod();
}
static class InitInvoker implements InjectionListener {
public void afterInjection(Object injectee) {
try {
injectee.getClass().getMethod("init").invoke(injectee);
} catch (Exception e) {
/* do something to handle errors here */
}
}
public static final InitInvoker INSTANCE = new InitInvoker();
}
public void configure() {
bindListener(HasInitMethod.INSTANCE, new TypeListener() {
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
encounter.register(InitInvoker.INSTANCE);
}
});
}
}
Gwizard inclui um módulo (gwizard-services
) que fornece serviços de goiaba em um formato adequado para guia. Os serviços de goiaba oferecem gerenciamento de ciclo de vida em threads paralelos.
Se você deseja chamar um método após a construção de uma instância, significa que a chamada do método pós-consumo é realmente uma etapa da criação da instância. Nesse caso, eu recomendaria um padrão abstrato de design de fábrica para resolver esse problema. O código pode parecer algo assim:
class A {
public A(Dependency1 d1, Dependency2 d2) {...}
public postConstruct(RuntimeDependency dr) {...}
}
interface AFactory {
A getInstance(RuntimeDependency dr);
}
class AFactoryImpl implements AFactory {
@Inject
public AFactoryImpl(Dependency1 d1, Dependency2 d2) {...}
A getInstance(RuntimeDependency dr) {
A a = new A(d1, d2);
a. postConstruct(dr);
return a;
}
}
// in guice module
bind(AFactory.class).to(AFactoryImpl.class)
Caso você precise inicializar um objeto usando outros objetos e depois que ambos estiverem prontos (o que é o caso se você precisar registrar um um com o outro e eles também dependem um do outro), você pode facilmente fazer assim:
public final class ApplicationModule extends AbstractModule {
@Override
protected void configure() {
requestStaticInjection(ApplicationModule.class);
}
@Inject
static void injectApplication(
ReslSession reslSession,
Set<Saga> sagas,
Set<Reaction> reactions
) {
sagas.forEach(reslSession::registerSaga);
reactions.forEach(reslSession::registerReaction);
}
}