为什么Spring 4无法正确地计算出这些泛型?

时间:2017-04-28 08:17:23

标签: java spring

我正在使用Spring 4.3.7.RELEASE并且我正在尝试实现一个通用工厂,它使用作为参数提供的值来装饰它生成的对象。以前使用Spring时,我不会尝试下面的内容,因为它会明显遇到类型擦除问题。但是,since Spring 4 已经为实例化泛型类型提供了适当的(几乎是神奇的)支持。

所以,这是代码。这是一个人为的例子来说明这一点,而不是我正在使用的实际代码。

首先,这是通用接口,它是我希望随工厂生成的对象层次结构的根目录:

public interface Nameable {
    String getName();
    void setName(String name);
}

接下来,有2个实现接口的简单(相同的示例)类:

@Component
@Scope("prototype")
public class Foo implements Nameable {
    public Foo(){
        super();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name;
}

@Component
@Scope("prototype")
public class Widget implements Nameable {
    public Widget(){
        super();
    }
    public Widget(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String name;
}

最后,这是所有内容的关键所在,这是Nameable实例的通用工厂:

@Component
public class NameableFactory<T extends Nameable> {
    @Autowired
    private ObjectFactory<T> objectFactory;

    public <T extends Nameable> T getObject(String name){
        T newObject = (T) this.objectFactory.getObject();

        newObject.setName(name);

        return newObject;
    }
}

我还省略了一些额外的客户端类等。

在运行时,当调用getObject()NameableFactory<Foo>的{​​{1}}方法时,出现以下错误:

NameableFactory<Widget>

我也尝试使用org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'java.lang.Object' available: expected single matching bean but found 2: foo, widget 代替FactoryBean,结果相同。

我很确定这个问题的答案是:你不能这样做因为类型擦除,可能是因为Spring 4无法处理另一个泛型中包含的泛型(它不是魔法,只是聪明的错觉)。但是我想在放弃并手工实施20多家工厂之前,我会问社区。

此外,我对完全实现相同目标的不同方法持开放态度,因此感谢各种建议和指针。

编辑:

  1. 在回应StanislavL评论时添加了ObjectFactory
  2. 删除了捕获的异常(回溯到FactoryBean版本)。

2 个答案:

答案 0 :(得分:2)

我认为问题是,如果你根本没有指明它,那么spring无法找出你的NameableFactory的泛型类型

我对这家工厂有另一个想法:

public class NameableFactory {
    private Map<Class<? extends Nameable>, Supplier<Nameable>> map;

    public NameableFactory(Map<Class<? extends Nameable>, Supplier<Nameable>> map) {
        this.map = map;
    }

    public Nameable get(Class<? extends Nameable> type, String name) {
        Nameable nameable = map.get(type)
        .get();
        nameable.setName(name);
        return nameable;
    }
}

并以这种方式配置。

@Configuration
public class FactoryConfig implements ApplicationContextAware {
    private ApplicationContext ctx;

    @Bean
    public NameableFactory nameableFactory() {
        Map<Class<? extends Nameable>, Supplier<Nameable>> map = new HashMap<>();
        map.put(Foo.class, () -> ctx.getBean(Foo.class));
        map.put(Widget.class, () -> ctx.getBean(Widget.class));
        return new NameableFactory(map);
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        this.ctx = ctx;

    }
}

答案 1 :(得分:0)

我不得不退后一步,然后愚蠢(最后期限......)。

基本上,我已经采取了澳大利亚答案的一部分(并因此得到了支持)。在我需要$("#formname").on('submit',function(){ $.ajax({ url: "xxxxxxxxxx/xxxxx/xxxx/xxxx.php", type: "POST", data: new FormData(this), contentType: false, cache: false, processData:false, success: function(data){ } }); }); Widget的所有课程中,我添加了:

Foo

所以我可以利用Spring实例化bean及其依赖项,但是我失去了命名的抽象。这也是很多冗余打字,但它在概念上很简单并且有效。

非常感谢那些提出建议的人,非常感谢。