【问题标题】:Guice call init method after instantinating an object实例化对象后Guice调用init方法
【发布时间】:2011-01-06 19:01:02
【问题描述】:

是否可以告诉 Guice 在之后调用某个方法(即 init()) 实例化给定类型的对象?

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

【问题讨论】:

  • 不幸的是,Guice 的作者似乎无意添加 @PostConstruct github.com/google/guice/issues/62#issuecomment-115452493 ,这确实严重限制了 Guice 的适用性(有一些变通方法,但这些都非常冗长)。您可能想研究一些其他框架,例如 Spring 或 JEE CDI(例如 Weld)

标签: guice init postconstruct


【解决方案1】:

其实是可以的。

您需要定义一个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();
            }
        });
    }
});

【讨论】:

  • 另外一种选择是使用GuicyFruit,它声称支持@PostConstruct(参见code.google.com/p/guiceyfruit),虽然它没有回答这个问题,但我认为值得一提的是,如果你(仅) 使用构造函数注入,你不需要这样的功能,你可以在构造函数中进行所有初始化。
  • 拯救了我的一天。 guiceyfruit 还不支持@PostConstruct
  • Matchers.subclassesOf(MyInitClass.class) 实际上会导致编译时错误:“AbstractModule 类型中的方法 bindListener(Matcher super TypeLiteral>>, TypeListener) 不适用于参数 (Matcher, new TypeListener(){})" 我认为您必须扩展 AbstractMatcher 才能使您的代码正常工作
  • 同意,此示例包含编译错误。以下博客文章详细介绍了如何绑定侦听器:developer.vz.net/2012/02/08/extending-guice-2
  • 或者您可以简单地更改为 Matchers.any(),然后在调用 init() 之前检查类型。
【解决方案2】:

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 也不会对此发表评论。
【解决方案3】:

【讨论】:

【解决方案4】:

您可以将@Inject 注释添加到您的init() 方法中。对象实例化后会自动运行。

【讨论】:

  • 问题是,如果你有可选的依赖项,这种方法不起作用,因为据我所知,没有办法告诉 guice 调用你的 init() 方法作为最后一个方法。恕我直言,他们需要@PostConstruct 支持。
  • 我正在使用构造函数注入,我必须做一些依赖于其他依赖项的初始化工作。
  • @OrtwinAngermeier,如果我理解正确,您可以在构造函数上添加@Inject 注释。
  • 对于使用 @Inject 的抽象方法也不是一个很好的选择,TypeListener 方法绝对是坚持使用 Guice 实现的方法
【解决方案5】:

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

https://github.com/stickfigure/gwizard

【讨论】:

    【解决方案6】:

    如果您需要使用其他对象初始化一个对象,并且在两者都准备好之后(如果您需要将一个对象注册到另一个对象并且它们也相互依赖),您可以轻松地这样做:

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

    【讨论】:

      【解决方案7】:

      如果你想在实例构建后调用方法,则表示构建后方法调用实际上是实例创建的一个步骤。在这种情况下,我会推荐抽象工厂设计模式来解决这个问题。 代码可能如下所示:

      
      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)
      

      【讨论】:

        【解决方案8】:

        基于Geoff's answer,您可以“使可调用”@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");
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-11-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多