简体   繁体   English

Spring - 自动装配通用接口的通用实现

[英]Spring - autowiring generic implementation of generic interface

I've got small question. 我有一个小问题。 It's probably trivial, but I've never faced it before. 这可能是微不足道的,但我以前从未面对过它。

I have generic interface and a generic implementation of it. 我有通用接口和它的通用实现。 I wanted to autowire it, but an error occured. 我想自动装配它,但发生了错误。 Here are the details: 以下是详细信息:

Interface 接口

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

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

Implementation 履行

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

Autowire attempt 自动尝试

private NoteDAO noteDAO;

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

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

Error 错误

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

I want to keep it as simple as possible. 我想尽量保持简单。 I've check the web, but I've found only about defining my exact beans in configuration. 我已经检查了网络,但我发现只是在配置中定义我的确切bean。 I prefer to avoid it if possible. 如果可能的话,我宁愿避免它。

In your specific case , Spring doesn't allow to use a generic type to be wired as dependency such as : 您的特定情况下 ,Spring不允许使用泛型类型作为依赖项连接,例如:

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

for a very simple reason : consistency. 原因很简单:一致性。
This dependency that you made a Spring bean with @Service : 您使用@Service创建Spring bean的依赖关系:

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

can be wired into other beans. 可以连接到其他bean。

Imagine that the beans that depend on the Serializing instance don't use the same generic : Serializing<UserComment> in Foo and Serializing<UserQuestion> in Bar such as : 试想一下,依赖于豆类Serializing的实例不使用相同的通用: Serializing<UserComment>FooSerializing<UserQuestion>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;
    }

}

Here the Serializing object is the same but each bean declares a distinct generic on. 这里的Serializing对象是相同的,但每个bean声明一个不同的泛型。
So it would break the type safety of the generic type. 因此它会打破泛型类型的类型安全性。


In fact, erasure of the generics is not the real problem there as Spring (from Spring 4) owns a Resolver able to resolve the type : 事实上,由于Spring(从Spring 4开始)拥有一个能够解析类型的Resolver ,因此擦除泛型并不是真正的问题:

behind the scenes, the new ResolvableType class provides the logic of actually working with generic types. 在幕后,新的ResolvableType类提供了实际使用泛型类型的逻辑。 You can use it yourself to easily navigate and resolve type information. 您可以自己使用它来轻松导航和解析类型信息。 Most methods on ResolvableType will themselves return a ResolvableType ResolvableType上的大多数方法本身都会返回ResolvableType

And before Spring 4 you also had other workaround to accept generic types in the bean dependencies. 在Spring 4之前,您还有其他解决方法来接受bean依赖项中的泛型类型。
The real problem is that you annotated a generic class with @Service to make it a bean while it is an instance of this generic class that has to be configured as a bean. 真正的问题是你用@Service注释了一个泛型类,使它成为一个bean,而它是一个必须配置为bean的泛型类的实例。

So to achieve what you want to do, declare the JsonSerializer beans that you want to instantiate in a @Configuration class : 因此,要实现您想要执行的操作,请声明要在@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>();
    }
}

You can now wire the dependency as a generic type : 您现在可以将依赖项连接为泛型类型:

@Service
public class NoteController {

    private Serializing<UserComment> userCommentSerializer;

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

    }
}

It's related to java's type erasure : https://docs.oracle.com/javase/tutorial/java/generics/erasure.html 它与java的类型擦除有关: https//docs.oracle.com/javase/tutorial/java/generics/erasure.html

In java, you can'nt know the generic type at runtime. 在java中,您无法在运行时知道泛型类型。 The only workarround is to use anonymous classes or inherited classes with non-generic type : Inject anonymous classes with spring 唯一的工作原理是使用匿名类或非泛型类的继承类: 使用spring注入匿名类

You have to tell Spring about all the beans you want it to autowire. 您必须告诉Spring您希望它自动装配的所有bean。 No exceptions. 没有例外。

If you cannot spell it out beforehand, then I'd say it's not a candidate for injection. 如果你不能事先拼出来,那么我会说它不是注射的候选人。

Somethings you need to have full control and call new . 有些事情你需要完全掌控并打电话给new Maybe your case is one of those. 也许你的情况就是其中之一。 There's nothing wrong with that; 这没有错; it just means that Spring DI isn't appropriate for that use case. 它只是意味着Spring DI不适合该用例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM