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

3

我有一个小问题。这可能很琐碎,但我以前从未遇到过。

我有一个通用接口和一个通用实现。我想自动装配它,但出现了一个错误。以下是详细信息:

接口

@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放在构造函数中会怎样? - Lino
它不适用于构造函数。我也将其删除,只是为了确保(因为现在只有一个实现),但仍然没有找到任何东西。 - Peteef
你的项目结构是什么样子的?组件扫描器能够找到Bean吗? - krismath
@Peteef 我的意思是在 serializer 参数上 -> @Qualifier("jsonSerializer") Serializing<UserComment> serializer - Lino
是的,它能够在同一个包中找到bean。 - Peteef
3个回答

10
在您的具体情况下,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不使用相同的泛型: 在Foo中使用Serializing<UserComment>,在Bar中使用Serializing<UserQuestion>,例如:

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 4开始,它拥有一个解析器来解析类型:ResolvableType类提供了处理泛型类型的逻辑。
在幕后,新的ResolvableType类提供了处理泛型类型的逻辑。您可以自己使用它轻松地导航和解析类型信息。ResolvableType上的大多数方法本身将返回一个ResolvableType 在Spring 4之前,您还有其他解决方案来接受bean依赖项中的泛型类型。
真正的问题是您用@Service注释了一个泛型类,使其成为一个bean,而实际上需要配置该泛型类的实例作为bean。 因此,要实现您想要做的事情,请在@Configuration类中声明要实例化的JsonSerializer beans:
@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;

    }
}

太好了,非常感谢您的回答! - Max

4

2

你需要告诉Spring关于所有你想让它自动装配的bean,没有例外。

如果你事先无法明确说明,则我认为它不适合注入。

有些情况下,你需要完全控制并调用new。也许你的情况就是其中之一。这并没有错;这只意味着Spring DI不适用于该用例。


唯一的解决方法是创建一个子类,对吧?class UserCommentSerializer extends JsonSerializer<UserComment> { ... } 然后将其注入到构造函数中。 - Lino
1
就像他们上面说的一样:类型擦除是你的问题。我想知道为什么你要费这么大劲,当Jackson JSON库可以为你处理所有这些问题时。这是Spring Boot中JSON序列化/反序列化的标准。 - duffymo
1
你比我更了解你的问题。我不禁认为你正在设计一个非常不可能发生的情况。一旦你正确注释了你的类,Jackson会对它们进行排序,你就不需要再更改它们了。做简单的事情,YAGNI。 - duffymo
我很高兴。祝你好运。 - duffymo
1
@Lino,在Spring 4之前,这是一个可以接受的解决方法。在配置类中使用泛型类型声明bean是一种更灵活的方式。 - davidxxx
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接