Guice在实例化对象后调用init方法。

72

有没有可能让Guice在实例化给定类型的对象后调用某个方法(如init())?

我正在寻找类似于EJB 3(和Spring)中的@PostConstruct注释的功能。


1
不幸的是,看起来Guice的作者们没有打算添加@PostConstruct https://github.com/google/guice/issues/62#issuecomment-115452493,这确实限制了Guice的适用性(虽然有解决方法,但那些方法相当冗长)。您可能需要考虑一些其他框架,比如Spring或JEE CDI(例如Weld)。 - arcuri82
8个回答

65

您只需将@Inject注解添加到init()方法即可。对象实例化后,它会自动运行。


16
问题是,如果你有可选的依赖项,这种方法就行不通,因为据我所知,没有办法告诉Guice将您的init()方法作为最后一个方法调用。在我看来,它们需要@PostConstruct支持。 - bogdan.mustiata
我正在使用构造函数注入,其中我必须进行一些初始化工作,这取决于其他依赖项。 - Ortwin Angermeier
1
@OrtwinAngermeier,如果我理解正确的话,您可以在构造函数和初始化方法上都放置@Inject注释。 - Mansoor Siddiqui
对于带有@Inject的抽象方法来说,使用TypeListener方法显然是坚持Guice实现的正确选择。而并非最佳选项。 - Marc Magon

46

实际上,这是可能的。

你需要定义一个TypeListener来启用功能。在你的模块定义中可以按照以下方式进行:

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

5
还有一个选择是使用GuicyFruit,据称支持@PostConstruct(请参见http://code.google.com/p/guiceyfruit/)。虽然它并没有回答这个问题,但我认为值得提到的是,如果你仅使用构造函数注入,你不需要这样的功能,因为你可以在构造函数中完成所有初始化。 - Eelco
10
如果使用Matchers.subclassesOf(MyInitClass.class)会导致编译时错误:"The method bindListener(Matcher<? super TypeLiteral<?>>, TypeListener) in the type AbstractModule is not applicable for the arguments (Matcher<Class>, new TypeListener(){})"我认为你需要扩展AbstractMatcher才能使代码正常运行。 - Andrey
7
同意,这个例子包含编译错误。下面的博客文章详细介绍了如何绑定监听器:http://developer.vz.net/2012/02/08/extending-guice-2/。 - pestrella
在这里,您可以下载GuiceSubclassMatcher实用程序类以使其正常工作:http://moi.vonos.net/java/guice-listeners/ - Daniel Hári
1
或者你可以简单地改为Matchers.any(),并在调用init()之前检查类型。 - Daniel Hári
显示剩余2条评论

9

guiceyfruit 可以为使用 @PostConstruct 注解或实现Spring的InitializingBean接口的方法提供所需的功能。也可以编写自己的监听器来实现此功能。以下是一个示例,它在对象创建后调用公共的init()方法。

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

感谢提供这个好例子,只是想知道 getMethod("") 是否会返回 null,因为当它找不到指定的方法时,它会抛出 NoSuchMethodException 异常,而 javadoc 也没有对此进行注释。 - zeratul021

7

请注意,mycila-guice 3.6仅适用于Guice 4.0而不是4.1;请参见https://github.com/mycila/guice/issues/11。 - vorburger

2

如果您想在实例构建后调用方法,这意味着后置构造方法调用实际上是实例创建的一步。在这种情况下,我建议使用抽象工厂设计模式来解决这个问题。代码可能类似于:


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)

1

GWizard包含一个模块(gwizard-services),该模块以Guice友好的格式提供Guava服务。 Guava服务可让您在并行线程中进行生命周期管理。

https://github.com/stickfigure/gwizard


0
如果您需要使用其他对象初始化一个对象,并且在两个对象都准备好之后(如果您需要将一个对象注册到另一个对象并且它们也彼此依赖),您可以轻松地这样做:
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);
  }

}

-1

根据Geoff的回答,您可以“调用”@PostConstruct方法:

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

    }
}

此外,您可以拥有多个@PostConstruct方法,但您不会知道它们将以何种顺序被调用

@PostConstruct
private void init() {
    dataManager.printData();
}

@PostConstruct
private void init2() {
    System.out.println("Other init method");
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接