java - Spring 4 不会自动限定 Autowiring 上的泛型类型

标签 java spring generics dependency-injection spring-4

问题已确定,已更新(滚动到底部)

我正在开发一个桌面应用程序,目前使用 Spring(spring-context4.1.6.RELEASE)进行 IoC 和依赖注入(inject)。我正在使用注释配置,使用 @ComponentScan .我遇到的问题应该作为 4.X.X 中的一项功能实现, 正如它所说 herehere ,但我变老了3.X.X异常。

我有一个代表通用存储库的参数化接口(interface):

public interface DomainRepository<T> {

    T add(T entity) throws ServiceException, IllegalArgumentException;

    // ...etc

}

然后我有两个具体的实现,ChunkRepositoryImplProjectRepositoryImpl ,它们被相应地参数化。它们共享抽象类的一些通用实现,但声明如下:

@Repository
public class ChunkRepositoryImpl extends AbstractRepositoryImpl<Chunk> implements DomainRepository<Chunk> {

    // ...+ various method implementations

}

@Repository
public class ProjectRepositoryImpl extends AbstractRepositoryImpl<Project> implements DomainRepository<Project> {

    // ...+ various method implementations

}

我对上述链接的理解使我相信我应该能够 Autowiring 这些链接而无需通过 @Qualifier 手动指定 bean| .但是,当我这样做时:

@Autowired
private DomainRepository<Project> repository;

我得到以下异常(当然前面有很长的堆栈跟踪):

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.foo.bar.repositories.DomainRepository] is defined: expected single matching bean but found 2: chunkRepositoryImpl,projectRepositoryImpl

任何人都可以阐明为什么会发生这种情况吗?我希望在 3.X.X 中出现此异常, 但它不应该发生在 4.X.X 中.我的情况和描述的情况有什么区别 here

更新

我已经找到问题的根源了。我的方法之一 DomainRepository<T>界面标记为@Async ,并利用 Spring 的异步功能。删除它意味着 bean 是正确限定的。我假设 Spring 使用 @Async 转换类引擎盖下的方法进入其他一些类,这个过程剥离了类型信息,这意味着它无法区分 bean。

这意味着我现在有两个问题:

  1. 这是有意为之的行为吗?
  2. 有人可以提出解决方法吗?

Here是一个演示问题的项目。只需删除 @Async来自 DomainRepository<T> 的注释界面,问题消失。

最佳答案

I hypothesize that Spring transforms classes with @Async methods under the hood into some other class, and this process strips the type information, meaning that it can't tell the beans apart.

是的。这正是发生的事情。

Spring 4 支持通过完整的通用签名注入(inject) bean。给定注入(inject)目标

@Autowired
private DomainRepository<Project> repository;

和一个 类型为 ProjectRepositoryImpl 的 bean ,Spring 将正确解析该 bean 并将其注入(inject)字段(或方法参数或构造函数参数)。

但是,在您的代码中,您实际上并没有 ProjectRepositoryImpl 类型的 bean , 甚至不是 DomainRepository<Project> 类型.您实际上有一个 java.lang.Proxy 类型的 bean (实际上是它的一个动态子类)实现了 DomainRepository , org.springframework.aop.SpringProxy , 和 org.springframework.aop.framework.Advised .

@Async , Spring 需要代理你的 bean 来添加异步调度行为。默认情况下,此代理是 JDK 代理。 JDK 代理只能继承目标类型的接口(interface)。 JDK 代理是使用工厂方法生成的 Proxy#newProxyInstance(...) .注意它是如何只接受 Class 的参数,不是 Type .所以它只能接收 DomainRepository 的类型描述符, 不适用于 DomainRepository<Chunk> .

因此,您没有实现参数化目标类型的 bean DocumentRepository<Project> . Spring 将回落到原始类型 DocumentRepository并找到两个候选 bean。这是一个不明确的匹配,所以它失败了。

解决方案是使用 CGLIB 代理

@EnableAsync(proxyTargetClass = true)

CGLIB 代理允许 Spring 获取完整的类型信息,而不仅仅是接口(interface)。所以你的代理实际上有一个类型是 ProjectRepositoryImpl 的子类型,例如,它带有 DocumentRepository<Project>类型信息。


上面的很多都是实现细节,并且在许多不同的地方定义,official documentation , javadoc 、评论等。请谨慎使用。

关于java - Spring 4 不会自动限定 Autowiring 上的泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32980668/

相关文章:

java - 设置不同对象具有相同的属性

c# - 无法确定条件表达式的类型 (Func)

java - MVC - 使用 Java 中的 Swing/Graphics 设计和显示数组

java - 如何正确使用MapStruct @Mapping和@Mappings?

java - Spring中使用RestTemplate将ISO日期字符串解析为ZoneDateTime

java - 如何从 Spring Web Flow XML 获取 session ?

c# - 为什么 C# 编译器使用隐式运算符返回的值的父类型来调用重载运算符

c# - 有没有更好的方法来 self 引用类型?

java - Git:查找仅更改了serialVersionUID的文件

java - 部署在Tomcat 7.0.57上的Spring Boot 1.2.2应用程序不启动