2015-10-06 76 views
1

问题预选赛泛型类型已经确定,公布更新后的(滚动到底)弹簧4不会自动自动装配

我开发目前使用Spring(spring-context4.1.6.RELEASE)为国际奥委会和依赖注入的桌面应用程序。我正在使用注释配置,使用@ComponentScan。我遇到的问题应该作为4.X.X中的一项功能实施,因为它表示herehere,但我收到了旧的3.X.X异常。

我有一个参数化接口,它表示一个通用的存储库:

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 

} 

我上面的链接的理解使我相信,我应该能够自动装配这些无需通过@Qualifier手动指定豆。然而,当我这样做:

@Autowired 
private DomainRepository<Project> repository; 

我得到下面的异常(前面加场的长堆栈跟踪):

产生的原因:org.springframework.beans.factory.NoUniqueBeanDefinitionException:无资格类型的豆[com.foo.bar.repositories.DomainRepository]定义:预期单个匹配的bean,但发现2:chunkRepositoryImpl,projectRepositoryImpl

任何人可以照亮一盏灯,为什么这可能发生?我希望这个例外在3.X.X,但它不应该发生在4.X.X。我的情况和描述的here之间有什么区别?

UPDATE

我已经发现了问题的根源。我的DomainRepository<T>接口中的一种方法被标记为@Async,并且利用了Spring的异步功能。删除这意味着这些bean是正确的。我假设Spring将类下的@Async方法转换为其他类,并且这个过程去除了类型信息,这意味着它不能将bean分开。

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

  1. 这是预期的行为?
  2. 任何人都可以提出解决方法吗?

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

+0

这对我在4.x.x上正常工作。请发布完整且可重现的示例。 –

+0

@SotiriosDelimanolis在一个不同的项目中创建一个可重复的例子,但到目前为止它也适用于我。但是,在原始项目中,异常仍然存在。这很混乱。 –

+0

名称为'Project'的类在'repository'字段中被映射到类中吗? –

回答

4

我推测,春季转换与@Async方法的类 引擎盖下到一些其他类,而这个过程剥去 类型的信息,这意味着它不能告诉豆分开。

是。这正是发生的情况。

Spring 4支持通过它们的全部通用签名来注入bean。给注射对象

@Autowired 
private DomainRepository<Project> repository; 

ProjectRepositoryImpl类型的豆,Spring会妥善解决和豆注入到字段(或方法参数或构造函数参数)。

然而,在你的代码,你实际上并不甚至还没有的DomainRepository<Project>类型ProjectRepositoryImpl类型的豆。实际上,您有一个类型为java.lang.Proxy(实际上是它的动态子类)的bean,它实现DomainRepository,org.springframework.aop.SpringProxyorg.springframework.aop.framework.Advised

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

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

的解决方案是使用CGLIB代理与

@EnableAsync(proxyTargetClass = true) 

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


很多上述的是实施细则,并在许多不同的地方定义,official documentationjavadoc,评论等仔细地使用它们。