如何在Guice中为“Class <! - ? - > clazz”注入一个课程?

时间:2013-05-08 08:23:17

标签: java dependency-injection guice

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

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

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

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

3 个答案:

答案 0 :(得分:1)

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

基本上,您可以按原样定义ClassTest,但将“不同”依赖关系标记为@Assisted

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

然后为ClassTest个对象创建一个工厂界面,接受Class参数并返回ClassTest s:

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

最后,您可以使用它为所需的每个类对象创建ClassTest

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

但如果我是你,我会真的重新考虑整个班级的架构,以避免在这些事情上的需要。

答案 1 :(得分:0)

要随时间更改注入值,您可以使用提供商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

答案 2 :(得分:0)

即使在没有辅助注入的情况下,也可以通过创建绑定时使用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<? extends ITestInterface>(我怀疑这是OP的目的)。