问题已确定,已更新(滚动到底部)
我正在开发一个桌面应用程序,目前使用 Spring(spring-context
,4.1.6.RELEASE
)进行 IoC 和依赖注入(inject)。我正在使用注释配置,使用 @ComponentScan
.我遇到的问题应该作为 4.X.X
中的一项功能实现, 正如它所说 here和 here ,但我变老了3.X.X
异常。
我有一个代表通用存储库的参数化接口(interface):
public interface DomainRepository<T> {
T add(T entity) throws ServiceException, IllegalArgumentException;
// ...etc
}
然后我有两个具体的实现,ChunkRepositoryImpl
和 ProjectRepositoryImpl
,它们被相应地参数化。它们共享抽象类的一些通用实现,但声明如下:
@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。
这意味着我现在有两个问题:
- 这是有意为之的行为吗?
- 有人可以提出解决方法吗?
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/