Is it possible to tell Guice to call some method (i.e. init()) after instantinating an object of given type?
I look for functionality similar to @PostConstruct annot
You can just add the @Inject
annotation to your init()
method. It will get run automatically after the object is instantiated.
If you'd like to call a method after the construction of an instance, it means the post-construct method call is actually a step of the instance creation. In this case, I would recommend abstract factory design pattern to solve this problem. The code may look like something like this:
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)
guiceyfruit does what you're after for methods annotated with @PostConstruct
or implementing spring's InitializingBean
. It's also possible to write your own listeners to do this. Here's an example that calls a public init()
method after objects are created.
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 includes a module (gwizard-services
) which provides Guava services in a Guice-friendly format. Guava services give you lifecycle management in parallel threads.
https://github.com/stickfigure/gwizard
In case you need to initialize an object using other objects and after both are ready (which is the case if you need to register one with the other and they also depend on each other) you can easily do it like this:
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);
}
}
Based on Geoff's answer you can "make callable" @PostConstruct
method:
public class GuiceExample {
@Inject
private IDataManager dataManager;
public GuiceExample() {
System.out.println("Constructor");
}
@PostConstruct
private void init() {
dataManager.printData();
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bind(IDataManager.class).to(DataManager.class);
bindListener(HasPostConstructAnnotationMatcher.INSTANCE, new TypeListener() {
@Override
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
encounter.register(PostConstructAnnotationInvoker.INSTANCE);
}
});
}
});
GuiceExample example = injector.getInstance(GuiceExample.class);
}
private static class HasPostConstructAnnotationMatcher extends AbstractMatcher<TypeLiteral<?>> {
private static final HasPostConstructAnnotationMatcher INSTANCE = new HasPostConstructAnnotationMatcher();
@Override
public boolean matches(TypeLiteral<?> t) {
return Arrays.stream(t.getRawType().getDeclaredMethods()).anyMatch(GuiceExample::hasPostConstructAnnotation);
}
}
private static boolean hasPostConstructAnnotation(Method method) {
Annotation[] declaredAnnotations = method.getAnnotations();
return Arrays.stream(declaredAnnotations).anyMatch(a -> a.annotationType().equals(PostConstruct.class));
}
private static class PostConstructAnnotationInvoker implements InjectionListener<Object> {
private static final PostConstructAnnotationInvoker INSTANCE = new PostConstructAnnotationInvoker();
@Override
public void afterInjection(Object injectee) {
//@formatter:off
Arrays.stream(injectee.getClass().getDeclaredMethods())
.filter(GuiceExample::hasPostConstructAnnotation)
.forEach(m -> {
try {
m.setAccessible(true);
m.invoke(injectee);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
});
//@formatter:on
}
}
public static interface IDataManager {
void printData();
}
public static class DataManager implements IDataManager {
@Override
public void printData() {
System.out.println("I print data.");
}
}
}
Also, you can have multiple @PostConstruct
method but you will not know in which order they are going to be invoked:
@PostConstruct
private void init() {
dataManager.printData();
}
@PostConstruct
private void init2() {
System.out.println("Other init method");
}