Guice call init method after instantinating an object

GuiceInitPostconstruct

Guice Problem Overview


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 (and Spring).

Guice Solutions


Solution 1 - Guice

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

Solution 2 - Guice

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

Solution 3 - Guice

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

Solution 4 - Guice

Solution 5 - Guice

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)

Solution 6 - Guice

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

Solution 7 - Guice

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

}

Solution 8 - Guice

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmgamerView Question on Stackoverflow
Solution 1 - GuiceSimon NickersonView Answer on Stackoverflow
Solution 2 - GuicegpamparaView Answer on Stackoverflow
Solution 3 - GuiceGeoff ReedyView Answer on Stackoverflow
Solution 4 - GuiceChristian UllenboomView Answer on Stackoverflow
Solution 5 - GuiceRussell BieView Answer on Stackoverflow
Solution 6 - GuicestickfigureView Answer on Stackoverflow
Solution 7 - GuicekboomView Answer on Stackoverflow
Solution 8 - GuiceGeorge Z.View Answer on Stackoverflow