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

时间:2018-06-22 11:25:01

标签: java spring generics autowired

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

我有通用接口及其通用实现。我想为其自动接线,但是发生了错误。详细信息如下:

接口

@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.

我想使其尽可能简单。我已经检查过Web,但是只发现了有关在配置中定义确切的bean的信息。我更愿意避免这种情况。

3 个答案:

答案 0 :(得分:4)

您的特定情况下,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<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;
    }

}

这里Serializing对象是相同的,但是每个bean在其上声明了不同的泛型。
这样会破坏泛型的类型安全性。


实际上,像Spring (from Spring 4) owns a Resolver able to resolve the type那样,擦除泛型并不是真正的问题:

  

在后台,新的ResolvableType类提供了以下逻辑:   实际上使用泛型类型。您可以自己轻松地使用它   浏览并解决类型信息。 ResolvableType上的大多数方法   自己将返回ResolvableType

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

因此,要实现您要执行的操作,请在JsonSerializer类中声明要实例化的@Configuration 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;

    }
}

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

您必须告诉Spring您要它自动连线的所有bean。没有例外。

如果您不能事先将其拼写清楚,那么我想说它不是注射的候选者。

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