Guice call init method after instantinating an object

前端 未结 8 1069
半阙折子戏
半阙折子戏 2020-11-28 06:21

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

相关标签:
8条回答
  • 2020-11-28 06:50

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

    0 讨论(0)
  • 2020-11-28 06:52

    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)
    
    0 讨论(0)
  • 2020-11-28 06:54

    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);
          }
        });
      }
    }
    
    0 讨论(0)
  • 2020-11-28 06:54

    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

    0 讨论(0)
  • 2020-11-28 07:07

    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);
      }
    
    }
    
    0 讨论(0)
  • 2020-11-28 07:13

    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");
    }
    
    0 讨论(0)
提交回复
热议问题