Question

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 annotation in EJB 3.

Was it helpful?

Solution

Actually, it is possible.

You need to define a TypeListener to get the functionality going. Something along the lines of the following in your module definition:

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();
            }
        });
    }
});

OTHER TIPS

You can just add the @Inject annotation to your init() method. It will get run automatically after the object is instantiated.

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

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)

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);
  }

}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top