【问题标题】:Spring - autowiring generic implementation of generic interfaceSpring - 通用接口的自动装配通用实现
【发布时间】:2018-06-22 11:25:01
【问题描述】:

我有一个小问题。这可能是微不足道的,但我以前从未遇到过。

我有通用接口和它的通用实现。我想自动接线,但发生了错误。以下是详细信息:

界面

@Service
public interface Serializing<T extends Serializable> {
    String serialize(T toBeSerialized);

    T deserialize(String toBeDeserialized, Class<T> resultType);
}

实施

@Service
public class JsonSerializer<T extends Serializable> implements Serializing<T> {
   /** code **/
}

自动接线尝试

private NoteDAO noteDAO;

@Qualifier("jsonSerializer")
private Serializing<UserComment> serializer;

@Autowired
public NoteController(NoteDAO noteDAO, Serializing<UserComment> serializer) {
    this.noteDAO = noteDAO;
    this.serializer = serializer;
}

错误

Parameter 1 of constructor in somepackagepath.NoteController required a bean of type 'anotherpackagepath.Serializing' that could not be found.

我想让它尽可能简单。我已经检查了网络,但我发现只有关于在配置中定义我的确切 bean。如果可能的话,我宁愿避免它。

【问题讨论】:

  • 如果你把@Qualifier放在构造函数中会怎样?
  • 不适用于构造函数。我也删除了它,只是为了确定(因为现在只有一个实现)但仍然没有找到任何东西。
  • 你的项目结构是什么?组件扫描器是否能够找到 bean?
  • @Peteef 我的意思是 serializer 参数 -> @Qualifier("jsonSerializer") Serializing&lt;UserComment&gt; serializer
  • 是的,它能够在同一个包中找到 bean。

标签: java spring generics autowired


【解决方案1】:

您的具体情况中,Spring 不允许使用泛型类型作为依赖项,例如:

@Autowired
public NoteController(NoteDAO noteDAO, Serializing<UserComment> serializer) {
    this.noteDAO = noteDAO;
    this.serializer = serializer;
}

原因很简单:一致性。
您使用 @Service 创建了 Spring bean 的这种依赖关系:

@Service
public class JsonSerializer<T extends Serializable> implements Serializing<T> {
   /** code **/
}

可以连接到其他bean。

想象一下,依赖于 Serializing 实例的 bean 不使用相同的泛型: Serializing&lt;UserComment&gt;FooSerializing&lt;UserQuestion&gt;Bar 如:

public class Foo{

    @Autowired
    public Foo(NoteDAO noteDAO, Serializing<UserComment> serializer) {
        this.noteDAO = noteDAO;
        this.serializer = serializer;
    }

}
public class Bar{

    @Autowired
    public Bar(NoteDAO noteDAO, Serializing<UserQuestion> serializer) {
        this.noteDAO = noteDAO;
        this.serializer = serializer;
    }

}

这里的 Serializing 对象是相同的,但每个 bean 都声明了一个不同的泛型。
所以它会破坏泛型类型的类型安全。


事实上,删除泛型并不是真正的问题,就像Spring (from Spring 4) owns a Resolver able to resolve the type

在幕后,新的 ResolvableType 类提供了 实际使用泛型类型。您可以自己轻松使用 导航和解析类型信息。 ResolvableType 上的大多数方法 自己会返回一个 ResolvableType

在 Spring 4 之前,您还有其他解决方法来接受 bean 依赖项中的泛型类型。
真正的问题是您使用 @Service 注释了一个泛型类以使其成为一个 bean,而它是这个泛型类的实例,必须配置为 bean。

因此,要实现您想要做的事情,请在 @Configuration 类中声明您要实例化的 JsonSerializer bean:

@Configuration
public class SerializingBeans {

    @Bean
    public JsonSerializer<UserComment> userCommentSerializer() {
        return new JsonSerializer<UserComment>();
    }

    @Bean
    public JsonSerializer<UserAnswer> userAnswerSerializer() {
        return new JsonSerializer<UserAnswer>();
    }
}

您现在可以将依赖项连接为泛型类型:

@Service
public class NoteController {

    private Serializing<UserComment> userCommentSerializer;

    @Autowired
    public NoteController(Serializing<UserComment> serializer) {
        this.userCommentSerializer = serializer;

    }
}

【讨论】:

  • 这么好的答案!谢谢楼主!
【解决方案2】:

这和java的类型擦除有关:https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

在 java 中,您无法在运行时知道泛型类型。 唯一的解决方法是使用匿名类或具有非泛型类型的继承类:Inject anonymous classes with spring

【讨论】:

    【解决方案3】:

    您必须告诉 Spring 您希望它自动装配的所有 bean。没有例外。

    如果你不能事先把它拼出来,那么我会说它不是注射的候选者。

    您需要完全控制并致电new。也许你的情况就是其中之一。这没有什么问题;这只是意味着 Spring DI 不适合该用例。

    【讨论】:

    • 唯一的解决方法是创建一个子类对吗? class UserCommentSerializer extends JsonSerializer&lt;UserComment&gt; { ... } 然后将其注入构造函数
    • 也许吧。根据发布的内容,我不确定。
    • 就像他们上面说的:类型擦除是你的问题。我想知道当杰克逊 JSON 库会为您处理所有这些时,您为什么会遇到所有这些麻烦。这就是 Spring Boot 中 JSON 序列化/反序列化的标准。
    • 你比我更了解你的问题。我不禁认为您正在为极不可能的情况进行设计。一旦你正确地注释了你的类,杰克逊就会把它们整理出来,你就不会改变它们了。做简单的事。雅格尼。
    • @Lino 这是 Spring 4 之前可以接受的解决方法。在配置类中声明具有泛型类型的 bean 是一种更灵活的方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多