簡體   English   中英

Spring 4無法自動驗證自動裝配上的通用類型

[英]Spring 4 not automatically qualifying generic types on autowire

已識別問題,更新后 (滾動到底部)

我正在開發當前使用Spring( spring-context4.1.6.RELEASE )進行IoC和依賴注入的桌面應用程序。 我正在使用@ComponentScan使用批注配置。 我遇到的問題應該在4.XX作為一項功能4.XX ,正如這里此處所指出的那樣,但是我遇到了舊的3.XX異常。

我有一個代表通用存儲庫的參數化接口:

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手動指定Bean,就可以自動裝配這些鏈接。 但是,當我這樣做時:

@Autowired
private DomainRepository<Project> repository;

我得到以下異常(當然,在堆棧跟蹤之前):

由以下原因引起:org.springframework.beans.factory.NoUniqueBeanDefinitionException:未定義類型為[com.foo.bar.repositories.DomainRepository]的合格Bean:預期為單個匹配的Bean,但發現2:chunkRepositoryImpl,projectRepositoryImpl

任何人都可以發光為什么會發生這種情況嗎? 我希望此異常3.XX ,但它不應該在發生4.XX 我的情況與此處描述的情況有什么區別?

更新

我發現了問題的根源。 我的DomainRepository<T>接口中的方法之一被標記為@Async ,並利用了Spring的異步功能。 取消此操作意味着這些咖啡豆已正確合格。 我假設Spring將@Async具有@Async方法的類轉換為其他類,並且此過程剝離了類型信息,這意味着它無法區分bean。

這意味着我現在有兩個問題:

  1. 這是預期的行為嗎?
  2. 有人可以建議解決方法嗎?

是一個演示問題的項目。 只需從DomainRepository<T>接口中刪除@Async批注,問題就會消失。

我假設Spring將幕后具有@Async方法的類轉換為其他類,並且此過程剝離了類型信息,這意味着它無法區分bean。

是。 就是這樣。

Spring 4支持通過其完整的通用簽名來注入bean。 給定注射目標

@Autowired
private DomainRepository<Project> repository;

以及類型為ProjectRepositoryImpl的bean,Spring會正確解析該bean並將其注入到字段(或方法參數,或構造函數參數)中。

但是 ,在您的代碼中,實際上並沒有類型為ProjectRepositoryImpl的bean,甚至沒有類型為DomainRepository<Project>的bean。 實際上,您有一個類型為java.lang.Proxy的bean(實際上是它的動態子類),該bean實現DomainRepositoryorg.springframework.aop.SpringProxyorg.springframework.aop.framework.Advised

使用@Async ,Spring需要代理您的bean來添加異步調度行為。 默認情況下,此代理是JDK代理。 JDK代理只能繼承目標類型的接口。 JDK代理使用工廠方法Proxy#newProxyInstance(...) 請注意,它僅接受Class參數,而不接受Type 因此,它只能接收DomainRepository的類型描述符,而不能接收DomainRepository<Chunk>的類型描述符。

因此,您沒有實現參數化目標類型DocumentRepository<Project> bean。 Spring將回到原始類型DocumentRepository並找到兩個候選bean。 這是一個模棱兩可的比賽,所以失敗了。

解決方案是將CGLIB代理與

@EnableAsync(proxyTargetClass = true)

CGLIB代理允許Spring獲得完整的類型信息,而不僅僅是接口。 因此,您的代理實際上將具有一個類型,該類型是ProjectRepositoryImpl的子類型,例如,它帶有DocumentRepository<Project>類型信息。


上面的很多都是實現細節,並在許多單獨的地方( 官方文檔javadoc ,注釋等)定義。請謹慎使用它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM