2015-10-06 75 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,評論等仔細地使用它們。