Spring是你可靠的大管家。
先看demo,放碼過來
- 有一個(gè)用戶實(shí)體類
User
疹娶,包含姓名name
、年齡age
伦连、電話mobile
三個(gè)屬性雨饺。
/**
* 用戶實(shí)體
*
* @author weixiaoyu
* @date 2018/4/27
*/
public class User {
/**
* 姓名
*/
private String name;
/**
* 年齡
*/
private Integer age;
/**
* 電話
*/
private String mobile;
/**
* 構(gòu)造方法
*
* @param name
* @param age
* @param mobile
*/
public User(String name, Integer age, String mobile) {
this.name = name;
this.age = age;
this.mobile = mobile;
}
/* setter & getter */
}
- 有一個(gè)接口
BasePrintService
,是打印信息基礎(chǔ)服務(wù)類除师,接口中有一個(gè)print
打印信息方法沛膳,方法的參數(shù)為上面定義的User
用戶類扔枫。
/**
* 打印信息基礎(chǔ)服務(wù)類
*
* @author weixiaoyu1
* @date 2019/1/16
*/
public interface BasePrintService {
/**
* 打印
*
* @param user
*/
void print(User user);
}
-
BasePrintService
接口有三個(gè)實(shí)現(xiàn)類PrintNameServiceImpl
、PrintAgeServiceImpl
和PrintMobileServiceImpl
短荐,分別實(shí)現(xiàn)了打印名稱倚舀、打印年齡和打印電話的功能。
注意:這三個(gè)實(shí)現(xiàn)類上方都標(biāo)明了@Service
注解忍宋,表明服務(wù)Bean交給Spring進(jìn)行管理痕貌。
/**
* 打印名稱服務(wù)實(shí)現(xiàn)類
*
* @author weixiaoyu1
* @date 2019/1/16
*/
@Service
public class PrintNameServiceImpl implements BasePrintService {
/**
* 打印名稱
*
* @param user
*/
@Override
public void print(User user) {
System.out.println("打印名稱 ---> User.name = " + user.getName());
}
}
/**
* 打印年齡服務(wù)實(shí)現(xiàn)類
*
* @author weixiaoyu1
* @date 2019/1/16
*/
@Service
public class PrintAgeServiceImpl implements BasePrintService {
/**
* 打印年齡
*
* @param user
*/
@Override
public void print(User user) {
System.out.println("打印年齡 ---> User.age = " + user.getAge().toString());
}
}
/**
* 打印電話服務(wù)實(shí)現(xiàn)類
*
* @author weixiaoyu1
* @date 2019/1/16
*/
@Service
public class PrintMobileServiceImpl implements BasePrintService {
/**
* 打印電話
*
* @param user
*/
@Override
public void print(User user) {
System.out.println("打印電話 ---> User.mobile = " + user.getMobile());
}
}
- 測試類,注釋都寫的很清楚哈
/**
* 測試打印類
*
* @author weixiaoyu1
* @date 2019/1/16
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PpValidatorApplication.class)
public class PrintServiceTest {
/**
* '將所有類型為BasePrintService的Bean注入為一個(gè)List
*/
@Autowired
private List<BasePrintService> printServiceList;
/**
* '將所有類型為BasePrintService的Bean注入為一個(gè)Map
* key為Bean的name
*/
@Autowired
private Map<String, BasePrintService> printServiceMap;
/**
* 測試方法糠排,遍歷List舵稠,執(zhí)行每一個(gè)Bean的print方法
*/
@Test
public void testPrintList() {
User user = new User("LittleRain", 25, "13010101100");
for (BasePrintService printService : printServiceList) {
printService.print(user);
}
}
/**
* 測試方法,遍歷Map入宦,先打印Map的key(也就是Bean的name)哺徊,再執(zhí)行每一個(gè)Bean的print方法
*/
@Test
public void testPrintMap() {
User user = new User("LittleRain", 25, "13010101100");
for (String beanName : printServiceMap.keySet()) {
System.out.println("打印實(shí)現(xiàn)類beanName = " + beanName);
printServiceMap.get(beanName).print(user);
}
}
}
- 通過
@Autowired
將所有類型為BasePrintService
的Bean注入為ListList<BasePrintService>
。 - 將所有類型為
BasePrintService
的Bean注入為MapMap<String, BasePrintService>
乾闰,
Map的 key 為Bean的 name落追。 - 調(diào)用測試類的
testPrintList()
方法,遍歷List涯肩,依次執(zhí)行每一個(gè)類型為BasePrintService
的Bean的print
方法轿钠,執(zhí)行結(jié)果如下:
打印年齡 ---> User.age = 25
打印電話 ---> User.mobile = 13010101100
打印名稱 ---> User.name = LittleRain
- 調(diào)用測試類的
testPrintMap()
方法巢钓,遍歷Map,先打印Map的key(也就是類型為BasePrintService
的Bean的name)疗垛,再依次執(zhí)行每一個(gè)Bean的print
方法症汹,執(zhí)行結(jié)果如下:
打印實(shí)現(xiàn)類beanName = printAgeServiceImpl
打印年齡 ---> User.age = 25
打印實(shí)現(xiàn)類beanName = printMobileServiceImpl
打印電話 ---> User.mobile = 13010101100
打印實(shí)現(xiàn)類beanName = printNameServiceImpl
打印名稱 ---> User.name = LittleRain
demo總結(jié):
- 所有實(shí)現(xiàn)了
BasePrintService
接口的實(shí)現(xiàn)類PrintNameServiceImpl
、PrintAgeServiceImpl
和PrintMobileServiceImpl
贷腕,通過@Service
注解烈菌,將Bean交給Spring進(jìn)行管理。
此時(shí)Bean的name為類名的首字母小寫
花履,type為BasePrintService
芽世。- 通過Spring提供的
@Autowired
注解,可以將所有類型為BasePrintService
的Bean注入為一個(gè)List诡壁,也可以注入為一個(gè)Map济瓢,此時(shí)Map的 key 為Bean的 name。- 所有Bean的配置和注入都交給Spring去管理妹卿,通過
@Autowired
注解可以一次性的將所有滿足條件的Bean注入到一個(gè)集合中旺矾。- 這種方式提供了代碼設(shè)計(jì)上更多花樣的可行性,咱們下篇文章再細(xì)說(提前占個(gè)坑)~
提問
為什么可以通過
@Autowired
注解可以一次性的將所有滿足條件的Bean注入到一個(gè)集合中呢夺克?具體Spring又是如何實(shí)現(xiàn)的呢箕宙?為神馬?铺纽?柬帕?
那么,開始扒源碼吧
- 我們知道可以通過注解
@Service
等將Bean注冊到Spring容器中狡门,交給Spring進(jìn)行管理陷寝。
具體處理過程可以看這篇文章:Spring對(duì)注解(Annotation)處理源碼分析1——掃描和讀取Bean定義
畫重點(diǎn),管理注解Bean定義的容器AnnotationConfigApplicationContext
直接注冊其馏、或掃描指定的包及其子包后調(diào)用注冊方法凤跑,將Bean進(jìn)行一系列處理過后注冊到BeanFactory
的HashMap中。
- 又知道可以通過使用
getBean
方法向Spring容器的BeanFactory
索取被管理的Bean叛复。
具體處理過程可以看這篇文章:《Spring技術(shù)內(nèi)幕》學(xué)習(xí)筆記5——IoC容器的依賴注入
追蹤實(shí)現(xiàn)了BeanFactory
的getBean
方法的AbstractApplicationContext
@Override
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
調(diào)用了AbstractBeanFactory
的doGetBean
方法
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
doGetBean
方法是實(shí)際干活的仔引,里面重要的一步就是createBean
方法創(chuàng)建Bean。
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
我們跳進(jìn)AbstractAutowireCapableBeanFactory
的createBean
方法褐奥,里面真正干活的是 doCreateBean
方法咖耘,創(chuàng)建了Bean實(shí)例。
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
doCreateBean
方法里面有兩個(gè)重要的步驟抖僵, createBeanInstance
和populateBean
鲤看。
// 生成Bean所包含的java對(duì)象實(shí)例
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 對(duì)Bean屬性的依賴注入進(jìn)行處理
populateBean(beanName, mbd, instanceWrapper);
createBeanInstance
方法生成Bean所包含的java對(duì)象實(shí)例,我們就不往里深扒了耍群。
重點(diǎn) 看populateBean
方法义桂,給剛創(chuàng)建的實(shí)例處理屬性的依賴注入找筝。
我們直接定位到關(guān)鍵代碼。
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
我們知道容器對(duì)Bean對(duì)象依賴注入時(shí)慷吊,是通過容器中注冊的 Bean后置處理器 處理這些注解的袖裕。而對(duì)于@Autowired
注解,則是通過AutowiredAnnotationBeanPostProcessor
這個(gè)Bean后置處理器進(jìn)行處理的溉瓶。
所以直接定位到AutowiredAnnotationBeanPostProcessor
的postProcessPropertyValues
方法急鳄,終于找到注入inject
相關(guān)的痕跡啦!
metadata.inject(bean, beanName, pvs);
跳進(jìn)InjectionMetadata
繼續(xù)跟進(jìn)inject
方法堰酿。
element.inject(target, beanName, pvs);
又跳回到了AutowiredAnnotationBeanPostProcessor
疾宏,里面有兩個(gè)內(nèi)部類AutowiredFieldElement
和AutowiredMethodElement
都實(shí)現(xiàn)了inject
注入方法。根據(jù)名字很容易想到触创,一個(gè)是Field的注入坎藐,一個(gè)是Method的注入。
兩個(gè)注入都實(shí)現(xiàn)了DefaultListableBeanFactory
的resolveDependency
解析依賴方法哼绑。
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
終于到了本文最關(guān)鍵的位置啦Q意伞!抖韩!睜大眼睛V鳌!茂浮!
resolveDependency
方法中真正干活的是doResolveDependency
方法双谆。
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
注意了!里面有一段調(diào)用resolveMultipleBeans
方法励稳,如果解析到了多個(gè)匹配條件的Bean佃乘,就直接返回解析結(jié)果囱井。
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
解析結(jié)果又是什么呢驹尼?我們進(jìn)去看看代碼咋說的,也到了此次解剖的最后一步庞呕。
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {
Class<?> type = descriptor.getDependencyType();
if (type.isArray()) {
Class<?> componentType = type.getComponentType();
ResolvableType resolvableType = descriptor.getResolvableType();
Class<?> resolvedArrayType = resolvableType.resolve();
if (resolvedArrayType != null && resolvedArrayType != type) {
type = resolvedArrayType;
componentType = resolvableType.getComponentType().resolve();
}
if (componentType == null) {
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType,
new MultiElementDescriptor(descriptor));
if (matchingBeans.isEmpty()) {
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
if (getDependencyComparator() != null && result instanceof Object[]) {
Arrays.sort((Object[]) result, adaptDependencyComparator(matchingBeans));
}
return result;
}
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
if (elementType == null) {
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
new MultiElementDescriptor(descriptor));
if (matchingBeans.isEmpty()) {
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
if (getDependencyComparator() != null && result instanceof List) {
((List<?>) result).sort(adaptDependencyComparator(matchingBeans));
}
return result;
}
else if (Map.class == type) {
ResolvableType mapType = descriptor.getResolvableType().asMap();
Class<?> keyType = mapType.resolveGeneric(0);
if (String.class != keyType) {
return null;
}
Class<?> valueType = mapType.resolveGeneric(1);
if (valueType == null) {
return null;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
new MultiElementDescriptor(descriptor));
if (matchingBeans.isEmpty()) {
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
return matchingBeans;
}
else {
return null;
}
}
由上面代碼得出結(jié)論:
- 首先判斷注入的類型新翎,如果是數(shù)組、Collection住练、Map地啰,則注入的是元素?cái)?shù)據(jù),即查找與元素類型相同的Bean讲逛,注入到集合中亏吝。
- 強(qiáng)調(diào)下Map類型,Map的 key 為Bean的 name盏混,value 為 與定義的元素類型相同的Bean蔚鸥。
到此惜论,真相大白了!V古纭馆类!
總結(jié)
/**
* '將所有類型為BasePrintService的Bean注入為一個(gè)List
*/
@Autowired
private List<BasePrintService> printServiceList;
/**
* '將所有類型為BasePrintService的Bean注入為一個(gè)Map
* key為Bean的name
*/
@Autowired
private Map<String, BasePrintService> printServiceMap;
如上述代碼所示,Spring本身就支持弹谁,通過使用@Autowired
注解乾巧,將所有相同類型(實(shí)現(xiàn)了同一個(gè)接口)的Bean,一次性注入到集合類型中预愤。
有理有據(jù)沟于,請(qǐng)大家 放心 使用~