【问题标题】:How can I inject a class to 'Class<?> clazz' in Guice?如何在 Guice 中为“Class<?> clazz”注入一个类?
【发布时间】:2020-11-15 05:30:38
【问题描述】:

我有一个带有构造函数的类,例如:

@Inject
public ClassTest(ITestInterface testInterface, Class<?> clazz){
    ...
}  

问题是我如何将一个类绑定到一个可以在这个构造函数中注入的实现,ClassTest 绑定会选择正确的类吗?

我想在不同的时间点注入不同的类。当我试图解决它时,Guice 给出了一个错误,它在 java.lang.Class 上找不到任何合适的构造函数。

【问题讨论】:

    标签: java dependency-injection guice


    【解决方案1】:

    我认为你必须使用 Guice 的 assisted inject 扩展名。

    基本上,您按原样定义您的ClassTest,但将“可变”依赖项标记为@Assisted

    @Inject
    public ClassTest(ITestInterface testInterface, @Assisted Class<?> clazz){
        ...
    }  
    

    然后为ClassTest 对象创建一个工厂接口,该接口将接受Class 参数并返回ClassTests:

    public interface ClassTestFactory {
        ClassTest create(Class<?> clazz);
    }
    

    然后你安装特殊类型的模块,它会为你创建工厂:

    // Inside your module
    install(new FactoryModuleBuilder().build(ClassTestFactory.class));
    

    然后,无论您需要ClassTest 实例,您都应该注入ClassTestFactory 接口:

    @Inject
    YourLogicClass(ClassTestFactory ctFactory) {
        this.ctFactory = ctFactory;
    }
    

    最后你用它为你想要的每个类对象创建ClassTests:

    ClassTest ct1 = ctFactory.create(SomeClass.class);
    ClassTest ct2 = ctFactory.create(AnotherClass.class);
    

    但如果我是你,我真的会重新考虑整个类的架构以避免此类事情的需要。

    【讨论】:

      【解决方案2】:

      即使没有辅助注入也可以解决这个问题,只需在创建绑定时使用TypeLiteral

      import javax.inject.Inject;
      import com.google.inject.AbstractModule;
      import com.google.inject.Guice;
      import com.google.inject.Injector;
      import com.google.inject.Module;
      import com.google.inject.TypeLiteral;
      
      public class ClassTest
      {
          static interface ITestInterface {}
      
          @Inject
          public ClassTest(ITestInterface testInterface, Class<?> clazz)
          {
              System.err.println("testInterface=" + testInterface);
              System.err.println("clazz=" + clazz);
          }
      
          public static void main(String... argument)
          {
              ITestInterface testObject = new ITestInterface() {};
              Module module = new AbstractModule()
              {
                  @Override
                  protected void configure()
                  {
                      binder().bind(ITestInterface.class).toInstance(testObject);
                      binder().bind(new TypeLiteral<Class<?>>() {}).toInstance(testObject.getClass());
                      //            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  }
              };
              Injector injector = Guice.createInjector(module);
              injector.getInstance(ClassTest.class);
          }
      }
      

      运行此代码时的输出类似于:

      testInterface=ClassTest$1@3d921e20
      clazz=class ClassTest$1
      

      我不得不同意@VladimirMatveev 的观点,这是一个有点不寻常的用例,需要注入java.lang.Class 对象可能表明存在设计缺陷。我遇到的这种类型注入的唯一看似有效的情况是用于类型检查,其中注入的类需要Class 对象来检查其他对象的类型(通过Class.isInstance(...))但这是不可取的注入该类的实例(!)(例如,因为它不是单例并且可能产生各种其他不希望的对象创建)。尽管如此,即使是这种情况也有些棘手,可能会以更好的方式解决。 至少,我会使用更具体的类型参数,例如 Class&lt;? extends ITestInterface&gt;(我怀疑这是 OP 的意图)。

      【讨论】:

        【解决方案3】:

        要随时间更改注入值,您可以使用 Provider bindings。然后它可能看起来像这样:

        模块配置:

        public class SomeModule extends AbstractModule{
        
            @Override
            protected void configure() {
                bind(Class.class).toProvider(SomeProvider.class);
        
            }
        }
        

        提供者(不是很优雅,但可能是一个开始......):

        public class SomeProvider implements Provider<Class<?>>{
        
            private static Class<?> myClazz;
        
            public static void setClass(Class<?> clazz){
                myClazz = clazz;
            }
        
            @Override
            public Class<?> get() {
                return myClazz;
            }
        
        }
        

        一些不同的类:

        public class SomeClass{
            public int itWorks;
        
        }
        public class SomeOtherClass{
            public int itWorksGreat;
        
        }
        

        示例客户端代码:

        public static void main(String[] args){
            SomeProvider.setClass(SomeClass.class);
            Injector injector = Guice.createInjector(new SomeModule());
            printFields(injector.getInstance(Class.class));
            SomeProvider.setClass(SomeOtherClass.class);
            printFields(injector.getInstance(Class.class));
        }
        private static void printFields(Class clazz) {
            Field[] declaredFields = clazz.getDeclaredFields();
            for(Field field : declaredFields){
                System.out.println(field.getName());
            }
        }
        

        最后是结果:

        itWorks
        itWorksGreat
        

        【讨论】:

        • 你应该永远编写这样的提供程序!它违背了 DI 概念的全部原因,而且确实是 Guice 试图删除的东西。
        • 嗯......你是对的(但它仍然是可能的)。 +1 对您的回答,这真的很好,我不知道这样的解决方案。我想我可以利用它来解决我遇到的几个问题
        猜你喜欢
        • 1970-01-01
        • 2013-02-15
        • 2016-02-15
        • 1970-01-01
        • 1970-01-01
        • 2011-11-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多