Spring中初始化泛型類的方法實(shí)例
首先來看下在 Java 中對于泛型類型,比如這樣簡單的類定義
class Processor<T> {}
如果直接初始化時(shí)要指定具體類型的話,我們可以這么寫
Processor<String> processor = new Processor<>(); //Java 7 及以上版本
Spring 對基本泛型的初始化
如果我們要用 Spring 容器來初始化這個(gè)類,比如給上面那個(gè)類加個(gè) @Named 注解
@Named class Processor<T> { }
這時(shí)候我們通過 beanFactory.getBean(Processor.class)
得到的是一個(gè)什么樣的實(shí)例呢?Spring 怎么知道要指定什么具體類型呢?很簡單,任何不確定的情況都是 Object。所以通過容器得到的 Processor 實(shí)例相當(dāng)于用下面代碼構(gòu)造出來的
Processor processor = new Processor(); //更準(zhǔn)確來講是 Processor<Object> processor = new Processor<>();
再進(jìn)一步,對于有上限約束的泛型定義,Spring 才如何應(yīng)對呢?像
@Named class Processor<T extends Number> { }
類似的,class Processor<T>
相當(dāng)于 class Processor<T extends Object>
, 因此 Spring 在具體類型未明的情況下也是要用最頂層可接受類型,Spring 將會(huì)針對上面的代碼實(shí)例出下面的對象
Processor<Number> processor = new Processor<>();
再復(fù)雜一些,泛型的子類型仍然是泛型的情況,如下代碼
首先定義了一個(gè)泛型接口
public interface Service<T> { String process(T t); }
然后要求 Spring 容器來初始下面的 NumberService 實(shí)例
@Named public class NumberService<R extends Number> implements Service<R> { @Override public String process(R number) { return "Process Number: " + number; } }
Spring 在初始化 NumberService 實(shí)例同樣是要取用最頂層可接受類型,通過下面的代碼來初始化
NumberService<Number> numberService = new NumberService<>();
再終極一些,泛型類型并且類型也是泛型的,Spring 該如何拿捏?
@Named public class Processor<T> { @Inject Private Service<T> service; }
此時(shí) Spring 該如何確定上面的類型 T 呢?因?yàn)橛辛?Service<T> service
屬性的存在而不能再籠統(tǒng)的想像 Spring 會(huì)采用下面的代碼來初始化 Processor 實(shí)例
Processor<Object> processor = new Processor<>();
而是 Processor 的具體類型必須通過被注入的 Service<T>
實(shí)例的具體類型來推斷的,這就取決于在 Spring 容器中存在什么樣的 Service<T>
實(shí)例。舉兩個(gè)例子
如果 Spring 中有初始化
@Named public class StringService implements Service<String> { @Override public String process(String string) { return "Process String: " + string; } }
那么前面的 Processor<T>
實(shí)例就相當(dāng)于
Processor<String> processor = new Processor<>(); processor.service = new StringService();
如果 Spring 中初始化的 Service<T>
是前面那個(gè) NumberService<R extends Number> implements Service<R>
, 那么 Spring 容器中的 Processor<T>
實(shí)例相當(dāng)于
Processor<Number> processor = new Processor<>(); processor.service = new NumberService<Number>();
那如果前面的 NumberService 和 StringService 同時(shí)在 Spring 容器中注冊了呢?Spring 同樣要為難了,在沒有 @Primary 的情況下無法確定使用哪個(gè)實(shí)例來注入 Service<T> service
屬性了,出現(xiàn)類似錯(cuò)誤
2016-12-09 00:56:50.922 WARN 4950 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'processor': Unsatisfied dependency expressed through field 'service'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'cc.unmi.Service<?>' available: expected single matching bean but found 2: numberService,stringService 2016-12-09 00:56:50.941 ERROR 4950 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: Field service in cc.unmi.Processor required a single bean, but 2 were found: - numberService: defined in file [/Users/Yanbin/Workspaces/github/spring-generic-demo/target/classes/cc/unmi/NumberService.class] - stringService: defined in file [/Users/Yanbin/Workspaces/github/spring-generic-demo/target/classes/cc/unmi/StringService.class] Action: Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
這和普通屬性的注入時(shí)有多個(gè)可選實(shí)例時(shí)是一樣的錯(cuò)誤。
總結(jié)一下
如果 Spring 在初始化泛型類時(shí),未提供任何具體類型則會(huì)采用最上限的類型來初始化實(shí)例
@Named class Processor<T> -> new Processor<Object>()
@Named class Processor<T extends Number> -> new Processor<Number>();
如果泛型類型與被注入的屬性的具體類型有關(guān)聯(lián),則由屬性類型推斷出主類型
@Named class Processor<T> { @Inject Service<T> service; }
此時(shí) Spring 容器中存在 class StringService implements Service<String>
的實(shí)例,則會(huì)由屬性 service(StringService 實(shí)例)
推斷出 Processor 的具體類型是 Processor<String>
當(dāng)然這個(gè) Processor 類也是可以定義的稍復(fù)雜一些,如
@Named class Processor<T extends Number> { @Inject Service<T> service; }
關(guān)于本文的示例代碼可參考 https://github.com/yabqiu/spring-generic-demo, 請運(yùn)行 mvn spring-boot:run 查看輸出結(jié)果來理解 Spring 怎么去初始化泛型類實(shí)例的。
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家學(xué)習(xí)或者使用Spring能帶來一定的幫助,如果有疑問大家可以留言交流。
相關(guān)文章
Java8?stream流的map()方法你會(huì)使用了嗎
在日常的開發(fā)工作中經(jīng)常碰到要處理list中數(shù)據(jù)的問題。本文主要帶大家了解下Java8?stream流中map()方法的使用,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-12-12java實(shí)現(xiàn)讀取txt文件中的內(nèi)容
本文通過一個(gè)具體的例子向大家展示了如何使用java實(shí)現(xiàn)讀取TXT文件里的內(nèi)容的方法以及思路,有需要的小伙伴可以參考下2016-03-03SpringMVC中的DispatcherServlet結(jié)構(gòu)和初始化詳解
這篇文章主要介紹了SpringMVC中的DispatcherServlet結(jié)構(gòu)和初始化詳解,SpringMVC中Spring容器的關(guān)系是通過監(jiān)聽方式啟動(dòng)的,那么Spring與Servlet的Web容器(如:Tomcat、jetty)的關(guān)系則是通過DispatcherServlet進(jìn)行關(guān)聯(lián),需要的朋友可以參考下2024-01-01關(guān)于intellij idea打開就閃退或關(guān)閉詳細(xì)解決辦法
這篇文章主要介紹了關(guān)于intellij idea打開就閃退或關(guān)閉詳細(xì)解決辦法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03SpringBoot的jar包如何啟動(dòng)的實(shí)現(xiàn)
本文主要介紹了SpringBoot的jar包如何啟動(dòng)的實(shí)現(xiàn),文中根據(jù)實(shí)例編碼詳細(xì)介紹的十分詳盡,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03SpringBoot2.0+阿里巴巴Sentinel動(dòng)態(tài)限流實(shí)戰(zhàn)(附源碼)
這篇文章主要介紹了SpringBoot2.0+阿里巴巴Sentinel動(dòng)態(tài)限流實(shí)戰(zhàn)(附源碼),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11