本系列文章:
讀源碼,我們可以從第一行讀起
你知道Spring是怎麼解析配置類的嗎?
配置類為什麼要添加@Configuration註解?
推薦閱讀:
Spring官網閱讀 | 總結篇
Spring雜談
本系列文章將會帶你一行行的將Spring的源碼吃透,推薦閱讀的文章是閱讀源碼的基礎!
兩個問題
在開始探討源碼前,我們先思考兩個問題:
1、在Spring中,什麼是Bean?跟對象有什麼區別?
通過new關鍵字,反射,克隆等手段創建出來的就是對象。在Spring中,Bean一定是一個對象,但是對象不一定是一個Bean,一個被創建出來的對象要變成一個Bean要經過很多複雜的工序,例如需要被我們的BeanPostProcessor
處理,需要經過初始化,需要經過AOP
(AOP
本身也是由後置處理器完成的)等。
2、在創建對象前,Spring還做了其它什麼事情嗎?
我們還是回到流程圖中,其中相關的步驟如下:
在前面的三篇文章中,我們已經分析到了第3-5
步的源碼,而如果你對Spring源碼稍有了解的話,就是知道創建對象以及將對象變成一個Bean的過程發生在第3-11
步驟中。中間的五步分別做了什麼呢?
1、registerBeanPostProcessors
就像名字所說的那樣,註冊BeanPostProcessor
,這段代碼在Spring官網閱讀(八)容器的擴展點(三)(BeanPostProcessor)已經分析過了,所以在本文就直接跳過了,如果你沒有看過之前的文章也沒有關係,你只需要知道,在這裏Spring將所有的BeanPostProcessor
註冊到了容器中
2、initMessageSource
初始化容器中的messageSource
,如果程序員沒有提供,默認會創建一個org.springframework.context.support.DelegatingMessageSource
,Spring官網閱讀(十一)ApplicationContext詳細介紹(上) 已經介紹過了。
3、initApplicationEventMulticaster
初始化事件分發器,如果程序員沒有提供,那麼默認創建一個org.springframework.context.event.ApplicationEventMulticaster
,Spring官網閱讀(十二)ApplicationContext詳解(中)已經做過詳細分析,不再贅述
4、onRefresh
留給子類複寫擴展使用
5、registerListeners
註冊事件監聽器,就是將容器中所有實現了org.springframework.context.ApplicationListener
接口的對象放入到監聽器的集合中。
創建對象的源碼分析
在完成了上面的一些準備工作后,Spring開始來創建Bean了,按照流程,首先被調用的就是finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)
方法,我們就以這個方法為入口,一步步跟蹤源碼,看看Spring中的Bean到底是怎麼創建出來的,當然,本文主要關注的是創建對象的這個過程,對象變成Bean的流程我們在後續文章中再分析
1、finishBeanFactoryInitialization
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 初始化一個ConversionService用於類型轉換,這個ConversionService會在實例化對象的時候用到
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// 添加一個StringValueResolver,用於處理佔位符,可以看到,默認情況下就是使用環境中的屬性值來替代佔位符中的屬性
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// 創建所有的LoadTimeWeaverAware
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 靜態織入完成后將臨時的類加載器設置為null,所以除了創建LoadTimeWeaverAware時可能會用到臨時類加載器,其餘情況下都為空
beanFactory.setTempClassLoader(null);
// 將所有的配置信息凍結
beanFactory.freezeConfiguration();
// 開始進行真正的創建
beanFactory.preInstantiateSingletons();
}
上面的方法最終調用了org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
來創建Bean。
其源碼如下:
2、preInstantiateSingletons
public void preInstantiateSingletons() throws BeansException {
// 所有bd的名稱
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 遍歷所有bd,一個個進行創建
for (String beanName : beanNames) {
// 獲取到指定名稱對應的bd
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 對不是延遲加載的單例的Bean進行創建
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 判斷是否是一個FactoryBean
if (isFactoryBean(beanName)) {
// 如果是一個factoryBean的話,先創建這個factoryBean,創建factoryBean時,需要在beanName前面拼接一個&符號
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
// 判斷是否是一個SmartFactoryBean,並且不是懶加載的,就意味着,在創建了這個factoryBean之後要立馬調用它的getObject方法創建另外一個Bean
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
// 不是factoryBean的話,我們直接創建就行了
getBean(beanName);
}
}
}
// 在創建了所有的Bean之後,遍歷
for (String beanName : beanNames) {
// 這一步其實是從緩存中獲取對應的創建的Bean,這裏獲取到的必定是單例的
Object singletonInstance = getSingleton(beanName);
// 判斷是否是一個SmartInitializingSingleton,最典型的就是我們之前分析過的EventListenerMethodProcessor,在這一步完成了對已經創建好的Bean的解析,會判斷其方法上是否有 @EventListener註解,會將這個註解標註的方法通過EventListenerFactory轉換成一個事件監聽器並添加到監聽器的集合中
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
上面這段代碼整體來說應該不難,不過它涉及到了一個點就是factoryBean
,如果你對它不夠了解的話,請參考我之前的一篇文章:Spring官網閱讀(七)容器的擴展點(二)FactoryBean
3、doGetBean
從上面的代碼分析中我們可以知道,Spring最終都會調用到getBean
方法,而getBean
並不是真正幹活的,doGetBean
才是。另外doGetBean
可以分為兩種情況
- 創建的是一個
FactoryBean
,此時實際傳入的name = & + beanName
- 創建的是一個普通Bean,此時傳入的
name = beanName
其代碼如下:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 前面我們說過了,傳入的name可能時& + beanName這種形式,這裏做的就是去除掉&,得到beanName
final String beanName = transformedBeanName(name);
Object bean;
// 這個方法就很牛逼了,通過它解決了循環依賴的問題,不過目前我們只需要知道它是從單例池中獲取已經創建的Bean即可,循環依賴後面我單獨寫一篇文章
// 方法作用:已經創建的Bean會被放到單例池中,這裏就是從單例池中獲取
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 如果直接從單例池中獲取到了這個bean(sharedInstance),我們能直接返回嗎?
// 當然不能,因為獲取到的Bean可能是一個factoryBean,如果我們傳入的name是 & + beanName 這種形式的話,那是可以返回的,但是我們傳入的更可能是一個beanName,那麼這個時候Spring就還需要調用這個sharedInstance的getObject方法來創建真正被需要的Bean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 在緩存中獲取不到這個Bean
// 原型下的循環依賴直接報錯
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 核心要義,找不到我們就從父容器中再找一次
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// 如果不僅僅是為了類型推斷,也就是代表我們要對進行實例化
// 那麼就將bean標記為正在創建中,其實就是將這個beanName放入到alreadyCreated這個set集合中
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 檢查合併后的bd是否是abstract,這個檢查現在已經沒有作用了,必定會通過
checkMergedBeanDefinition(mbd, beanName, args);
// @DependsOn註解標註的當前這個Bean所依賴的bean名稱的集合,就是說在創建當前這個Bean前,必須要先將其依賴的Bean先完成創建
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// 遍歷所有申明的依賴
for (String dep : dependsOn) {
// 如果這個bean所依賴的bean又依賴了當前這個bean,出現了循環依賴,直接報錯
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 註冊bean跟其依賴的依賴關係,key為依賴,value為依賴所從屬的bean
registerDependentBean(dep, beanName);
try {
// 先創建其依賴的Bean
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 我們目前只分析單例的創建,單例看懂了,原型自然就懂了
if (mbd.isSingleton()) {
// 這裏再次調用了getSingleton方法,這裏跟方法開頭調用的getSingleton的區別在於,這個方法多傳入了一個ObjectFactory類型的參數,這個ObjectFactory會返回一個Bean
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 省略原型跟域對象的相關代碼
return (T) bean;
}
配合註釋看這段代碼應該也不難吧,我們重點關注最後在調用的這段方法即可
4、getSingleton(beanName,ObjectFactory)
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
// 從單例池中獲取,這個地方肯定也獲取不到
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 工廠已經在銷毀階段了,這個時候還在創建Bean的話,就直接拋出異常
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
// 在單例創建前,記錄一下正在創建的單例的名稱,就是把beanName放入到singletonsCurrentlyInCreation這個set集合中去
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 這裏調用了singletonFactory的getObject方法,對應的實現就是在doGetBean中的那一段lambda表達式
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
// 省略異常處理
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 在單例完成創建后,將beanName從singletonsCurrentlyInCreation中移除
// 標志著這個單例已經完成了創建
afterSingletonCreation(beanName);
}
if (newSingleton) {
// 添加到單例池中
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
分析完上面這段代碼,我們會發現,核心的創建Bean的邏輯就是在singletonFactory.getObject()
這句代碼中,而其實現就是在doGetBean
方法中的那一段lambda表達式,如下:
實際就是通過createBean
這個方法創建了一個Bean然後返回,createBean
又幹了什麼呢?
5、createBean
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
RootBeanDefinition mbdToUse = mbd;
// 解析得到beanClass,為什麼需要解析呢?如果是從XML中解析出來的標籤屬性肯定是個字符串嘛
// 所以這裏需要加載類,得到Class對象
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// 對XML標籤中定義的lookUp屬性進行預處理,如果只能根據名字找到一個就標記為非重載的,這樣在後續就不需要去推斷到底是哪個方法了,對於@LookUp註解標註的方法是不需要在這裏處理的,AutowiredAnnotationBeanPostProcessor會處理這個註解
try {
mbdToUse.prepareMethodOverrides();
}
// 省略異常處理...
try {
// 在實例化對象前,會經過後置處理器處理
// 這個後置處理器的提供了一個短路機制,就是可以提前結束整個Bean的生命周期,直接從這裏返回一個Bean
// 不過我們一般不會這麼做,它的另外一個作用就是對AOP提供了支持,在這裡會將一些不需要被代理的Bean進行標記,就本文而言,你可以暫時理解它沒有起到任何作用
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
// 省略異常處理...
try {
// doXXX方法,真正幹活的方法,doCreateBean,真正創建Bean的方法
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
// 省略異常處理...
}
6、doCreateBean
本文只探討對象是怎麼創建的,至於怎麼從一個對象變成了Bean,在後面的文章我們再討論,所以我們主要就關注下面這段代碼
// 這個方法真正創建了Bean,創建一個Bean會經過 創建對象 > 依賴注入 > 初始化 這三個過程,在這個過程中,BeanPostPorcessor會穿插執行,本文主要探討的是創建對象的過程,所以關於依賴注入及初始化我們暫時省略,在後續的文章中再繼續研究
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 這行代碼看起來就跟factoryBean相關,這是什麼意思呢?
// 在下文我會通過例子介紹下,你可以暫時理解為,這個地方返回的就是個null
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 這裏真正的創建了對象
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 省略依賴注入,初始化
}
這裏我先分析下this.factoryBeanInstanceCache.remove(beanName)
這行代碼。這裏需要說一句,我寫的這個源碼分析的系列非常的細節,之所以選擇這樣一個個扣細節是因為我自己在閱讀源碼過程中經常會被這些問題阻塞,那麼藉著這些文章將自己踩過的坑分享出來可以減少作為讀者的你自己在閱讀源碼時的障礙,其次也能夠提升自己閱讀源碼的能力。如果你對這些細節不感興趣的話,可以直接跳過,能把握源碼的主線即可。言歸正傳,我們回到這行代碼this.factoryBeanInstanceCache.remove(beanName)
。什麼時候factoryBeanInstanceCache
這個集合中會有值呢?這裏我還是以示例代碼來說明這個問題,示例代碼如下:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
}
}
// 沒有做什麼特殊的配置,就是掃描了需要的組件,測試時換成你自己的包名
@ComponentScan("com.dmz.source.instantiation")
@Configuration
public class Config {
}
// 這裏申明了一個FactoryBean,並且通過@DependsOn註解申明了這個FactoryBean的創建要在orderService之後,主要目的是為了在DmzFactoryBean創建前讓容器發生一次屬性注入
@Component
@DependsOn("orderService")
public class DmzFactoryBean implements FactoryBean<DmzService> {
@Override
public DmzService getObject() throws Exception {
return new DmzService();
}
@Override
public Class<?> getObjectType() {
return DmzService.class;
}
}
// 沒有通過註解的方式將它放到容器中,而是通過上面的DmzFactoryBean來管理對應的Bean
public class DmzService {
}
// OrderService中需要注入dmzService
@Component
public class OrderService {
@Autowired
DmzService dmzService;
}
在這段代碼中,因為我們明確的表示了DmzFactoryBean
是依賴於orderService
的,所以必定會先創建orderService
再創建DmzFactoryBean
,創建orderService
的流程如下:
其中的屬性注入階段,我們需要細化,也可以畫圖如下:
為orderService進行屬性注入可以分為這麼幾步
-
找到需要注入的注入點,也就是orderService中的dmzService字段
-
根據字段的類型以及名稱去容器中查詢符合要求的Bean
-
當遍歷到一個FactroyBean時,為了確定其getObject方法返回的對象的類型需要創建這個FactroyBean(只會到對象級別),然後調用這個創建好的FactroyBean的getObjectType方法明確其類型並與注入點需要的類型比較,看是否是一個候選的Bean,在創建這個FactroyBean時就將其放入了factoryBeanInstanceCache
中。
-
在確定了唯一的候選Bean之後,Spring就會對這個Bean進行創建,創建的過程又經過三個步驟
在創建對象時,因為此時factoryBeanInstanceCache
已經緩存了這個Bean對應的對象,所以直接通過this.factoryBeanInstanceCache.remove(beanName)
這行代碼就返回了,避免了二次創建對象。
7、createBeanInstance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 省略異常
// 通過bd中提供的instanceSupplier來獲取一個對象
// 正常bd中都不會有這個instanceSupplier屬性,這裏也是Spring提供的一個擴展點,但實際上不常用
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// bd中提供了factoryMethodName屬性,那麼要使用工廠方法的方式來創建對象,工廠方法又會區分靜態工廠方法跟實例工廠方法
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// 在原型模式下,如果已經創建過一次這個Bean了,那麼就不需要再次推斷構造函數了
boolean resolved = false; // 是否推斷過構造函數
boolean autowireNecessary = false; // 構造函數是否需要進行注入
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// 推斷構造函數
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// 調用無參構造函數創建對象
return instantiateBean(beanName, mbd);
}
上面這段代碼在Spring官網閱讀(一)容器及實例化 已經分析過了,但是當時我們沒有深究創建對象的細節,所以本文將詳細探討Spring中的這個對象到底是怎麼創建出來的,這也是本文的主題。
在Spring官網閱讀(一)容器及實例化 這篇文章中,我畫了下面這麼一張圖
從上圖中我們可以知道Spring在實例化對象的時候有這麼幾種方式
- 通過bd中的supplier屬性
- 通過bd中的factoryMethodName跟factoryBeanName
- 通過構造函數
我們接下來就一一分析其中的細節:
》通過bd中的supplier屬性實例化對象
在Spring官網閱讀(一)容器及實例化 文中介紹過這種方式,因為這種方式我們基本不會使用,並不重要,所以這裏就不再贅述,我這裏就直接給出一個使用示例,大家自行體會吧
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
// 直接註冊一個Bean,並且指定它的supplier就是Service::new
ac.registerBean("service", Service.class,Service::new,zhe'sh);
ac.refresh();
System.out.println(ac.getBean("service"));
}
》通過bd中的factoryMethodName跟factoryBeanName實例化對象
對應代碼如下:
protected BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}
上面這段代碼主要幹了兩件事
- 創建一個
ConstructorResolver
對象,從類名來看,它是一個構造器解析器
- 調用了這個構造器解析器的
instantiateUsingFactoryMethod
方法,這個方法見名知意,使用FactoryMethod
來完成實例化
基於此,我們解決一個問題,ConstructorResolver
是什麼?
ConstructorResolver是什麼?
在要研究一個類前,我們最先應該從哪裡入手呢?很多沒有經驗的同學可能會悶頭看代碼,但是實際上最好的學習方式是先閱讀類上的javaDoc
ConstructorResolver上的javaDoc如下:
上面這段javaDoc翻譯過來就是這個類就是用來解析構造函數跟工廠方法的代理者,並且它是通過參數匹配的方式來進行推斷構造方法或者工廠方法。
看到這裏不知道小夥伴們是否有疑問,就是明明這個類不僅負責推斷構造函數,還會負責推斷工廠方法,那麼為什麼類名會叫做ConstructorResolver
呢?我們知道Spring的代碼在業界來說絕對是最規範的,沒有之一,這樣來說的話,這個類最合適的名稱應該是ConstructorAndFactoryMethodResolver
才對,因為它不僅負責推斷了構造函數還負責推斷了工廠方法嘛!
這裏我需要說一下我自己的理解。對於一個Bean,它是通過構造函數完成實例化的,或者通過工廠方法實例化的,其實在這個Bean看來都沒有太大區別,這兩者都可以稱之為這個Bean的構造器,因為通過它們都能構造出一個Bean。所以Spring就把兩者統稱為構造器
了,所以這個類名也就被稱為ConstructorResolver
了。
Spring在很多地方體現了這種實現,例如在XML配置的情況下,不論我們是使用構造函數創建對象還是使用工廠方法創建對象,其參數的標籤都是使用constructor-arg
。比如下面這個例子
<bean id="dmzServiceGetFromStaticMethod"
factory-bean="factoryBean"
factory-method="getObject">
<constructor-arg type="java.lang.String" value="hello" name="s"/>
<constructor-arg type="com.dmz.source.instantiation.service.DmzFactory" ref="factoryBean"/>
</bean>
<!--測試靜態工廠方法創建對象-->
<bean id="service"
class="com.dmz.official.service.MyFactoryBean"
factory-method="staticGet">
<constructor-arg type="java.lang.String" value="hello"/>
</bean>
<bean id="dmzService" class="com.dmz.source.instantiation.service.DmzService">
<constructor-arg name="s" value="hello"/>
</bean>
在對這個類有了大概的了解后,我們就需要來分析它的源碼,這裏我就不把它單獨拎出來分析了,我們藉著Spring的流程看看這個類幹了什麼事情
instantiateUsingFactoryMethod方法做了什麼?
核心目的:推斷出要使用的factoryMethod以及調用這個FactoryMethod要使用的參數,然後反射調用這個方法實例化出一個對象
這個方法的代碼太長了,所以我們將它拆分成為一段一段的來分析
方法參數分析
在分析上面的代碼之前,我們先來看看這個方法的參數都是什麼含義
方法上關於參數的介紹如圖所示
beanName
:當前要實例化的Bean的名稱
mbd
:當前要實例化的Bean對應的BeanDefinition
explicitArgs
:這個參數在容器啟動階段我們可以認定它就是null,只有显示的調用了getBean方法,並且傳入了明確的參數,例如:getBean("dmzService","hello")
這種情況下才會不為null,我們分析這個方法的時候就直接認定這個參數為null即可。
第一段
public BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
// 第一段代碼:創建並初始話一個BeanWrapperImpl
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
// ......
}
BeanWrapperImpl是什麼呢?如果你看過我之前的文章:Spring官網閱讀(十四)Spring中的BeanWrapper及類型轉換,那麼你對這個類應該不會陌生,它就是對Bean進行了一層包裝,並且在創建Bean的時候以及進行屬性注入的時候能夠進行類型轉換。就算你沒看過之前的文章也沒關係,只要記住兩點
- BeanWrapperImpl包裝了一個實例化好的對象
- BeanWrapperImpl能夠對屬性進行類型轉換
其層級關係如下:
回到我們的源碼分析,我們先來看看new BeanWrapperImpl()
做了什麼事情?
對應代碼如下:
// 第一步:調用空參構造
public BeanWrapperImpl() {
// 調用另外一個構造函數,表示要註冊默認的屬性編輯器
this(true);
}
// 這個構造函數表明是否要註冊默認編輯器,上面傳入的值為true,表示需要註冊
public BeanWrapperImpl(boolean registerDefaultEditors) {
super(registerDefaultEditors);
}
// 調用到父類的構造函數,確定要使用默認的屬性編輯器
protected AbstractNestablePropertyAccessor(boolean registerDefaultEditors) {
if (registerDefaultEditors) {
registerDefaultEditors();
}
// 對typeConverterDelegate進行初始化
this.typeConverterDelegate = new TypeConverterDelegate(this);
}
總的來說創建的過程非常簡單。第一,確定要註冊默認的屬性編輯器;第二,對typeConverterDelegate屬性進行初始化。
緊接着,我們看看在初始化這個BeanWrapper
做了什麼?
// 初始化BeanWrapper,主要就是將容器中配置的conversionService賦值到當前這個BeanWrapper上
// 同時註冊定製的屬性編輯器
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
}
還記得conversionService
在什麼時候被放到容器中的嗎?就是在finishBeanFactoryInitialization的時候啦~!
對conversionService
屬性完成賦值后就開始註冊定製的屬性編輯器,代碼如下:
// 傳入的參數就是我們的BeanWrapper,它同時也是一個屬性編輯器註冊表
protected void registerCustomEditors(PropertyEditorRegistry registry) {
PropertyEditorRegistrySupport registrySupport =
(registry instanceof PropertyEditorRegistrySupport ? (PropertyEditorRegistrySupport) registry : null);
if (registrySupport != null) {
// 這個配置的作用就是在註冊默認的屬性編輯器時,可以增加對數組到字符串的轉換功能
// 默認就是通過","來切割字符串轉換成數組,對應的屬性編輯器就是StringArrayPropertyEditor
registrySupport.useConfigValueEditors();
}
// 將容器中的屬性編輯器註冊到當前的這個BeanWrapper
if (!this.propertyEditorRegistrars.isEmpty()) {
for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
registrar.registerCustomEditors(registry);
// 省略異常處理~
}
}
// 這裏我們沒有添加任何的自定義的屬性編輯器,所以肯定為空
if (!this.customEditors.isEmpty()) {
this.customEditors.forEach((requiredType, editorClass) ->
registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
}
}
第二段
public BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
// 省略已經分析的第一段代碼,到這裏已經得到了一個具有類型轉換功能的BeanWrapper
// 實例化這個Bean的工廠Bean
Object factoryBean;
// 工廠Bean的Class
Class<?> factoryClass;
// 靜態工廠方法或者是實例化工廠方法
boolean isStatic;
/*下面這段代碼就是為上面申明的這三個屬性賦值*/
String factoryBeanName = mbd.getFactoryBeanName();
// 如果創建這個Bean的工廠就是這個Bean本身的話,那麼直接拋出異常
if (factoryBeanName != null) {
if (factoryBeanName.equals(beanName)) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
"factory-bean reference points back to the same bean definition");
}
// 得到創建這個Bean的工廠Bean
factoryBean = this.beanFactory.getBean(factoryBeanName);
if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
throw new ImplicitlyAppearedSingletonException();
}
factoryClass = factoryBean.getClass();
isStatic = false;
}
else {
// factoryBeanName為null,說明是通過靜態工廠方法來實例化Bean的
// 靜態工廠進行實例化Bean,beanClass屬性必須要是工廠的class,如果為空,直接報錯
if (!mbd.hasBeanClass()) {
throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
"bean definition declares neither a bean class nor a factory-bean reference");
}
factoryBean = null;
factoryClass = mbd.getBeanClass();
isStatic = true;
}
// 省略後續代碼
}
小總結:
這段代碼很簡單,就是確認實例化當前這個Bean的工廠方法是靜態工廠還是實例工廠,如果是實例工廠,那麼找出對應的工廠Bean。
第三段
public BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
// 省略第一段,第二段代碼
// 到這裏已經得到了一個BeanWrapper,明確了實例化當前這個Bean到底是靜態工廠還是實例工廠
// 並且已經確定了工廠Bean
// 最終確定的要用來創建對象的方法
Method factoryMethodToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
// 參數分析時已經說過,explicitArgs就是null
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
// 下面這段代碼是什麼意思呢?
// 在原型模式下,我們會多次創建一個Bean,所以Spring對參數以及所使用的方法做了緩存
// 在第二次創建原型對象的時候會進入這段緩存的邏輯
// 但是這裡有個問題,為什麼Spring對參數有兩個緩存呢?
// 一:resolvedConstructorArguments
// 二:preparedConstructorArguments
// 這裏主要是因為,直接使用解析好的構造的參數,因為這樣會導致創建出來的所有Bean都引用同一個屬性
Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
// 緩存已經解析過的工廠方法或者構造方法
if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
// resolvedConstructorArguments跟preparedConstructorArguments都是對參數的緩存
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
if (argsToResolve != null) {
// preparedConstructorArguments需要再次進行解析
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve);
}
}
// 省略後續代碼
}
小總結:
上面這段代碼應該沒什麼大問題,其核心思想就是從緩存中取已經解析出來的方法以及參數,這段代碼只會在原型模式下生效,因為單例的話對象只會創建一次嘛~!最大的問題在於,為什麼在對參數進行緩存的時候使用了兩個不同的集合,並且緩存后的參數還需要再次解析,這個問題我們暫且放着,不妨帶着這個問題往下看。
因為接下來要分析的代碼就比較複雜了,所以為了讓你徹底看到代碼的執行流程,下面我會使用示例+流程圖+文字的方式來分析源碼。
示例代碼如下(這個例子覆蓋接下來要分析的所有流程):
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor"><!--這裏開啟自動注入,並且是通過構造函數進行自動注入-->
<!--factoryObject 提供了創建對象的方法-->
<bean id="factoryObject" class="com.dmz.spring.first.instantiation.service.FactoryObject"/>
<!--提供一個用於測試自動注入的對象-->
<bean class="com.dmz.spring.first.instantiation.service.OrderService" id="orderService"/>
<!--主要測試這個對象的實例化過程-->
<bean id="dmzService" factory-bean="factoryObject" factory-method="getDmz" scope="prototype">
<constructor-arg name="name" value="dmz"/>
<constructor-arg name="age" value="18"/>
<constructor-arg name="birthDay" value="2020-05-23"/>
</bean>
<!--測試靜態方法實例化對象的過程-->
<bean id="indexService" class="com.dmz.spring.first.instantiation.service.FactoryObject"
factory-method="staticGetIndex"/>
<!--提供這個轉換器,用於轉換dmzService中的birthDay屬性,從字符串轉換成日期對象-->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="conversionService">
<property name="converters">
<set>
<bean class="com.dmz.spring.first.instantiation.service.ConverterStr2Date"/>
</set>
</property>
</bean>
</beans>
測試代碼:
public class FactoryObject {
public DmzService getDmz(String name, int age, Date birthDay, OrderService orderService) {
System.out.println("getDmz with "+"name,age,birthDay and orderService");
return new DmzService();
}
public DmzService getDmz(String name, int age, Date birthDay) {
System.out.println("getDmz with "+"name,age,birthDay");
return new DmzService();
}
public DmzService getDmz(String name, int age) {
System.out.println("getDmz with "+"name,age");
return new DmzService();
}
public DmzService getDmz() {
System.out.println("getDmz with empty arg");
return new DmzService();
}
public static IndexService staticGetIndex() {
return new IndexService();
}
}
public class DmzService {
}
public class IndexService {
}
public class OrderService {
}
public class ConverterStr2Date implements Converter<String, Date> {
@Override
public Date convert(String source) {
try {
return new SimpleDateFormat("yyyy-MM-dd").parse(source);
} catch (ParseException e) {
return null;
}
}
}
/**
* @author 程序員DMZ
* @Date Create in 23:14 2020/5/21
* @Blog https://daimingzhi.blog.csdn.net/
*/
public class Main {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext();
cc.setConfigLocation("application.xml");
cc.refresh();
cc.getBean("dmzService");
// 兩次調用,用於測試緩存的方法及參數
// cc.getBean("dmzService");
}
}
運行上面的代碼會發現,程序打印:
getDmz with name,age,birthDay and orderService
具體原因我相信你看了接下來的源碼分析自然就懂了
第四段
public BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
// 第一段代碼:到這裏已經得到了一個BeanWrapper,並對這個BeanWrapper做了初始化
// 第二段代碼:明確了實例化當前這個Bean到底是靜態工廠還是實例工廠
// 第三段代碼:以及從緩存中取過了對應了方法以及參數
// 進入第四段代碼分析,執行到這段代碼說明是第一次實例化這個對象
if (factoryMethodToUse == null || argsToUse == null) {
// 如果被cglib代理的話,獲取父類的class
factoryClass = ClassUtils.getUserClass(factoryClass);
// 獲取到工廠類中的所有方法,接下來要一步步從這些方法中篩選出來符合要求的方法
Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
List<Method> candidateList = new ArrayList<>();
// 第一步篩選:之前 在第二段代碼中已經推斷了方法是靜態或者非靜態的
// 所以這裏第一個要求就是要滿足靜態/非靜態這個條件
// 第二個要求就是必須符合bd中定義的factoryMethodName的名稱
// 其中第二個要求請注意,如果bd是一個configurationClassBeanDefinition,也就是說是通過掃描@Bean註解產生的,那麼在判斷時還會添加是否標註了@Bean註解
for (Method candidate : rawCandidates) {
if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
candidateList.add(candidate);
}
}
// 將之前得到的方法集合轉換成數組
// 到這一步得到的其實就是某一個方法的所有重載方法
// 比如dmz(),dmz(String name),dmz(String name,int age)
Method[] candidates = candidateList.toArray(new Method[0]);
// 排序,public跟參數多的優先級越高
AutowireUtils.sortFactoryMethods(candidates);
// 用來保存從配置文件中解析出來的參數
ConstructorArgumentValues resolvedValues = null;
// 是否使用了自動注入,本段代碼中沒有使用到這個屬性,但是在後面用到了
boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
int minTypeDiffWeight = Integer.MAX_VALUE;
// 可能出現多個符合要求的方法,用這個集合保存,實際上如果這個集合有值,就會拋出異常了
Set<Method> ambiguousFactoryMethods = null;
int minNrOfArgs;
// 必定為null,不考慮了
if (explicitArgs != null) {
minNrOfArgs = explicitArgs.length;
}
else {
// 就是說配置文件中指定了要使用的參數,那麼需要對其進行解析,解析后的值就存儲在resolvedValues這個集合中
if (mbd.hasConstructorArgumentValues()) {
// 通過解析constructor-arg標籤,將參數封裝成了ConstructorArgumentValues
// ConstructorArgumentValues這個類在下文我們專門分析
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
// 解析標籤中的屬性,類似進行類型轉換,後文進行詳細分析
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
else {
// 配置文件中沒有指定要使用的參數,所以執行方法的最小參數個數就是0
minNrOfArgs = 0;
}
}
// 省略後續代碼....
}
小總結:
因為在實例化對象前必定要先確定具體要使用的方法,所以這裏先做的第一件事就是確定要在哪個範圍內去推斷要使用的factoryMethod呢?
最大的範圍就是這個factoryClass的所有方法,也就是源碼中的rawCandidates
其次需要在rawCandidates
中進一步做推斷,因為在前面第二段代碼的時候已經確定了是靜態方法還是非靜態方法,並且BeanDefinition
也指定了factoryMethodName,那麼基於這兩個條件這裏就需要對rawCandidates
進一步進行篩選,得到一個candidateList
集合。
我們對示例的代碼進行調試會發現
確實如我們所料,rawCandidates
是factoryClass中的所有方法,candidateList
是所有getDmz的重載方法。
在確定了推斷factoryMethod的範圍后,那麼接下來要根據什麼去確定到底使用哪個方法呢?換個問題,怎麼區分這麼些重載的方法呢?肯定是根據方法參數嘛!
所以接下來要做的就是去解析要使用的參數了~
對於Spring而言,方法的參數會分為兩種
- 配置文件中指定的
- 自動注入模式下,需要去容器中查找的
在上面的代碼中,Spring就是將配置文件中指定的參數做了一次解析,對應方法就是resolveConstructorArguments
。
在查看這個方法的源碼前,我們先看看ConstructorArgumentValues
這個類
public class ConstructorArgumentValues {
// 通過下標方式指定的參數
private final Map<Integer, ValueHolder> indexedArgumentValues = new LinkedHashMap<>();
// 沒有指定下標
private final List<ValueHolder> genericArgumentValues = new ArrayList<>();
// 省略無關代碼.....
}
在前文的註釋中我們也說過了,它主要的作用就是封裝解析constructor-arg
標籤得到的屬性,解析標籤對應的方法就是org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseConstructorArgElement
,這個方法我就不帶大家看了,有興趣的可以自行閱讀。
它主要有兩個屬性
- indexedArgumentValues
- genericArgumentValues
對應的就是我們兩種指定參數的方法,如下:
<bean id="dmzService" factory-bean="factoryObject" factory-method="getDmz" scope="prototype">
<constructor-arg name="name" value="dmz"/>
<constructor-arg name="age" value="18"/>
<constructor-arg index="2" value="2020-05-23"/>
<!-- <constructor-arg name="birthDay" value="2020-05-23"/>-->
</bean>
其中的name跟age屬性會被解析為genericArgumentValues
,而index=2
會被解析為indexedArgumentValues
。
在對ConstructorArgumentValues
有一定認知之後,我們再來看看resolveConstructorArguments
的代碼:
// 方法目的:解析配置文件中指定的方法參數
// beanName:bean名稱
// mbd:beanName對應的beanDefinition
// bw:通過它進行類型轉換
// ConstructorArgumentValues cargs:解析標籤得到的屬性,還沒有經過解析(類型轉換)
// ConstructorArgumentValues resolvedValues:已經經過解析的參數
// 返回值:返回方法需要的最小參數個數
private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
// 是否有定製的類型轉換器,沒有的話直接使用BeanWrapper進行類型轉換
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);
// 構造一個BeanDefinitionValueResolver,專門用於解析constructor-arg中的value屬性,實際上還包括ref屬性,內嵌bean標籤等等
BeanDefinitionValueResolver valueResolver =
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
// minNrOfArgs 記錄執行方法要求的最小參數個數,一般情況下就是等於constructor-arg標籤指定的參數數量
int minNrOfArgs = cargs.getArgumentCount();
for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
int index = entry.getKey();
if (index < 0) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Invalid constructor argument index: " + index);
}
// 這是啥意思呢?
// 這個代碼我認為是有問題的,並且我給Spring官方已經提了一個issue,官方將會在5.2.7版本中修復
// 暫且你先這樣理解
// 假設A方法直接在配置文件中指定了index=3上要使用的參數,那麼這個時候A方法至少需要4個參數
// 但是其餘的3個參數可能不是通過constructor-arg標籤指定的,而是直接自動注入進來的,那麼在配置文件中我們就只配置了index=3上的參數,也就是說 int minNrOfArgs = cargs.getArgumentCount()=1,這個時候 index=3,minNrOfArgs=1, 所以 minNrOfArgs = 3+1
if (index > minNrOfArgs) {
minNrOfArgs = index + 1;
}
ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
// 如果已經轉換過了,直接添加到resolvedValues集合中
if (valueHolder.isConverted()) {
resolvedValues.addIndexedArgumentValue(index, valueHolder);
}
else {
// 解析value/ref/內嵌bean標籤等
Object resolvedValue =
valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
// 將解析后的resolvedValue封裝成一個新的ValueHolder,並將其source設置為解析constructor-arg得到的那個ValueHolder,後期會用到這個屬性進行判斷
ConstructorArgumentValues.ValueHolder resolvedValueHolder =
new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
resolvedValueHolder.setSource(valueHolder);
resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
}
}
// 對getGenericArgumentValues進行解析,代碼基本一樣,不再贅述
return minNrOfArgs;
}
可以看到,最終的解析邏輯就在resolveValueIfNecessary
這個方法中,那麼這個方法又做了什麼呢?
// 這個方法的目的就是將解析constructor-arg標籤得到的value值進行一次解析
// 在解析標籤時ref屬性會被封裝為RuntimeBeanReference,那麼在這裏進行解析時就會去調用getBean
// 在解析value屬性會會被封裝為TypedStringValue,那麼這裡會嘗試去進行一個轉換
// 關於標籤的解析大家有興趣的話可以去看看org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parsePropertyValue
// 這裏不再贅述了
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
// 解析constructor-arg標籤中的ref屬性,實際就是調用了getBean
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
// ......
/**
* <constructor-arg>
* <set value-type="java.lang.String">
* <value>1</value>
* </set>
* </constructor-arg>
* 通過上面set標籤中的value-type屬性對value進行類型轉換,
* 如果value-type屬性為空,那麼這裏不會進行類型轉換
*/
else if (value instanceof TypedStringValue) {
TypedStringValue typedStringValue = (TypedStringValue) value;
Object valueObject = evaluate(typedStringValue);
try {
Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
if (resolvedTargetType != null) {
return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
}
else {
return valueObject;
}
}
catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Error converting typed String value for " + argName, ex);
}
}
// 省略後續代碼....
}
就我們上面的例子而言,經過resolveValueIfNecessary
方法並不能產生實際的影響,因為在XML中我們沒有配置ref屬性或者value-type屬性。
畫圖如下:
第五段
public BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
// 第一段代碼:到這裏已經得到了一個BeanWrapper,並對這個BeanWrapper做了初始化
// 第二段代碼:明確了實例化當前這個Bean到底是靜態工廠還是實例工廠
// 第三段代碼:以及從緩存中取過了對應了方法以及參數
// 第四段代碼:明確了方法需要的最小的參數數量並對配置文件中的標籤屬性進行了一次解析
// 進入第五段代碼分析
// 保存在創建方法參數數組過程中發生的異常,如果最終沒有找到合適的方法,那麼將這個異常信息封裝后拋出
LinkedList<UnsatisfiedDependencyException> causes = null;
// 開始遍歷所有在第四段代碼中查詢到的符合要求的方法
for (Method candidate : candidates) {
// 方法的參數類型
Class<?>[] paramTypes = candidate.getParameterTypes();
// 候選的方法的參數必須要大於在第四段這推斷出來的最小參數個數
if (paramTypes.length >= minNrOfArgs) {
ArgumentsHolder argsHolder;
// 必定為null,不考慮
if (explicitArgs != null) {
// Explicit arguments given -> arguments length must match exactly.
if (paramTypes.length != explicitArgs.length) {
continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}
else {
// Resolved constructor arguments: type conversion and/or autowiring necessary.
try {
// 獲取參數的具體名稱
String[] paramNames = null;
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if (pnd != null) {
paramNames = pnd.getParameterNames(candidate);
}
// 根據方法的參數名稱以及配置文件中配置的參數創建一個參數數組用於執行工廠方法
argsHolder = createArgumentArray(
beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring);
}
// 在創建參數數組的時候可能發生異常,這個時候的異常不能直接拋出,要確保所有的候選方法遍歷完成,只要有一個方法符合要求即可,但是如果遍歷完所有方法還是沒找到合適的構造器,那麼直接拋出這些異常
catch (UnsatisfiedDependencyException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
}
// Swallow and try next overloaded factory method.
if (causes == null) {
causes = new LinkedList<>();
}
causes.add(ex);
continue;
}
// 計算類型差異
// 首先判斷bd中是寬鬆模式還是嚴格模式,目前看來只有@Bean標註的方法解析得到的Bean會使用嚴格模式來計算類型差異,其餘都是使用寬鬆模式
// 嚴格模式下,
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// 選擇一個類型差異最小的方法
if (typeDiffWeight < minTypeDiffWeight) {
factoryMethodToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousFactoryMethods = null;
}
// 省略後續代碼.......
}
小總結:這段代碼的核心思想就是根據第四段
代碼從配置文件中解析出來的參數構造方法執行所需要的實際參數數組。如果構建成功就代表這個方法可以用於實例化Bean,然後計算實際使用的參數跟方法上申明的參數的”差異值“,並在所有符合要求的方法中選擇一個差異值最小的方法
接下來,我們來分析方法實現的細節
- 構建方法使用的參數數組,也就是
createArgumentArray
方法,其源碼如下:
/* beanName:要實例化的Bean的名稱
* mbd:對應Bean的BeanDefinition
* resolvedValues:從配置文件中解析出來的並嘗試過類型轉換的參數
* bw:在這裏主要就是用作類型轉換器
* paramTypes:當前遍歷到的候選的方法的參數類型數組
* paramNames:當前遍歷到的候選的方法的參數名稱
* executable:當前遍歷到的候選的方法
* autowiring:是否時自動注入
*/
private ArgumentsHolder createArgumentArray(
String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable,
boolean autowiring) throws UnsatisfiedDependencyException {
TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
TypeConverter converter = (customConverter != null ? customConverter : bw);
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);
Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
// 遍歷候選方法的參數,跟據方法實際需要的類型到resolvedValues中去匹配
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
Class<?> paramType = paramTypes[paramIndex];
String paramName = (paramNames != null ? paramNames[paramIndex] : "");
ConstructorArgumentValues.ValueHolder valueHolder = null;
if (resolvedValues != null) {
// 首先,根據方法參數的下標到resolvedValues中找對應的下標的屬性
// 如果沒找到再根據方法的參數名/類型去resolvedValues查找
valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
// 如果都沒找到
// 1.是自動注入並且方法的參數長度正好跟配置中的參數數量相等
// 2.不是自動注入
// 那麼按照順序一次選取
if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) {
valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
}
}
// 也就是說在配置的參數中找到了合適的值可以應用於這個方法上
if (valueHolder != null) {
// 防止同一個參數被應用了多次
usedValueHolders.add(valueHolder);
Object originalValue = valueHolder.getValue();
Object convertedValue;
// 已經進行過類型轉換就不會需要再次進行類型轉換
if (valueHolder.isConverted()) {
convertedValue = valueHolder.getConvertedValue();
args.preparedArguments[paramIndex] = convertedValue;
}
else {
// 嘗試將配置的值轉換成方法參數需要的類型
MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
try {
// 進行類型轉換
convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam);
}
catch (TypeMismatchException ex) {
// 拋出UnsatisfiedDependencyException,在調用該方法處會被捕獲
}
Object sourceHolder = valueHolder.getSource();
// 只要是valueHolder存在,到這裏這個判斷必定成立
if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
Object sourceValue = ((ConstructorArgumentValues.ValueHolder) sourceHolder).getValue();
args.resolveNecessary = true;
args.preparedArguments[paramIndex] = sourceValue;
}
}
args.arguments[paramIndex] = convertedValue;
args.rawArguments[paramIndex] = originalValue;
}
else {
// 方法執行需要參數,但是resolvedValues中沒有提供這個參數,也就是說這個參數是要自動注入到Bean中的
MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
// 不是自動注入,直接拋出異常
if (!autowiring) {
// 拋出UnsatisfiedDependencyException,在調用該方法處會被捕獲
}
try {
// 自動注入的情況下,調用getBean獲取需要注入的Bean
Object autowiredArgument =
resolveAutowiredArgument(methodParam, beanName, autowiredBeanNames, converter);
// 把getBean返回的Bean封裝到本次方法執行時需要的參數數組中去
args.rawArguments[paramIndex] = autowiredArgument;
args.arguments[paramIndex] = autowiredArgument;
// 標誌這個參數是自動注入的
args.preparedArguments[paramIndex] = new AutowiredArgumentMarker();
// 自動注入的情況下,在第二次調用時,需要重新處理,不能直接緩存
args.resolveNecessary = true;
}
catch (BeansException ex) {
// 拋出UnsatisfiedDependencyException,在調用該方法處會被捕獲
}
}
}
// 註冊Bean之間的依賴關係
for (String autowiredBeanName : autowiredBeanNames) {
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName +
"' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
" to bean named '" + autowiredBeanName + "'");
}
}
return args;
}
上面這段代碼說難也難,說簡單也簡單,如果要徹底看懂它到底幹了什麼還是很有難度的。簡單來說,它就是從第四段代碼解析出來的參數中查找當前的這個候選方法需要的參數。如果找到了,那麼嘗試對其進行類型轉換,將其轉換成符合方法要求的類型,如果沒有找到那麼還需要判斷當前方法的這個參數能不能進行自動注入,如果可以自動注入的話,那麼調用getBean得到需要的Bean,並將其注入到方法需要的參數中。
第六段
public BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
// 第一段代碼:到這裏已經得到了一個BeanWrapper,並對這個BeanWrapper做了初始化
// 第二段代碼:明確了實例化當前這個Bean到底是靜態工廠還是實例工廠
// 第三段代碼:以及從緩存中取過了對應了方法以及參數
// 第四段代碼:明確了方法需要的最小的參數數量並對配置文件中的標籤屬性進行了一次解析
// 第五段代碼:到這裏已經確定了可以使用來實例化Bean的方法是哪個
// 省略拋出異常的代碼,就是在對推斷出來的方法做驗證
// 1.推斷出來的方法不能為null
// 2.推斷出來的方法返回值不能為void
// 3.推斷出來的方法不能有多個
// 對參數進行緩存
if (explicitArgs == null && argsHolderToUse != null) {
argsHolderToUse.storeCache(mbd, factoryMethodToUse);
}
}
try {
Object beanInstance;
if (System.getSecurityManager() != null) {
final Object fb = factoryBean;
final Method factoryMethod = factoryMethodToUse;
final Object[] args = argsToUse;
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, beanFactory, fb, factoryMethod, args),
beanFactory.getAccessControlContext());
}
else {
// 反射調用對應方法進行實例化
// 1.獲取InstantiationStrategy,主要就是SimpleInstantiationStrategy跟CglibSubclassingInstantiationStrategy,其中CglibSubclassingInstantiationStrategy主要是用來處理beanDefinition中的lookupMethod跟replaceMethod。通常來說我們使用的就是SimpleInstantiationStrateg
// 2.SimpleInstantiationStrateg就是單純的通過反射調用方法
beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse);
}
// beanWrapper在這裏對Bean進行了包裝
bw.setBeanInstance(beanInstance);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean instantiation via factory method failed", ex);
}
}
上面這段代碼的主要目的就是
- 緩存參數,原型可能多次創建同一個對象
- 反射調用推斷出來的factoryMethod
》通過構造函數實例化對象
如果上面你對使用factoryMethd進行實例化對象已經足夠了解的話,那麼下面的源碼分析基本沒有什麼很大區別,我們接着看看代碼。
首先,我們回到createBeanInstance
方法中,
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 上面的代碼已經分析過了
// 1.使用supplier來得到一個對象
// 2.通過factotryMethod方法實例化一個對象
// 看起來是不是有點熟悉,在使用factotryMethod創建對象時也有差不多這樣的一段代碼,看起來就是使用緩存好的方法直接創建一個對象
boolean resolved = false;
boolean autowireNecessary = false;
// 不對這個參數進行討論,就認為一直為null
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
// bd中的resolvedConstructorOrFactoryMethod不為空,說明已經解析過構造方法了
if (mbd.resolvedConstructorOrFactoryMethod != null) {
// resolved標誌是否解析過構造方法
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
// 構造函數已經解析過了,並且這個構造函數在調用時需要自動注入參數
if (autowireNecessary) {
// 此時部分解析好的參數已經存在了beanDefinition中,並且構造函數也在bd中
// 那麼在這裏只會從緩存中去取構造函數以及參數然後反射調用
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 這裏就是直接反射調用空參構造
return instantiateBean(beanName, mbd);
}
}
// 推斷出能夠使用的需要參數的構造函數
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 在推斷出來的構造函數中選取一個合適的方法來進行Bean的實例化
// ctors不為null:說明存在1個或多個@Autowired標註的方法
// mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR:說明是自動注入
// mbd.hasConstructorArgumentValues():配置文件中配置了構造函數要使用的參數
// !ObjectUtils.isEmpty(args):外部傳入的參數,必定為null,不多考慮
// 上面的條件只要滿足一個就會進入到autowireConstructor方法
// 第一個條件滿足,那麼通過autowireConstructor在推斷出來的構造函數中再進一步選擇一個差異值最小的,參數最長的構造函數
// 第二個條件滿足,說明沒有@Autowired標註的方法,但是需要進行自動注入,那麼通過autowireConstructor會去遍歷類中申明的所有構造函數,並查找一個差異值最小的,參數最長的構造函數
// 第三個條件滿足,說明不是自動注入,那麼要通過配置中的參數去類中申明的所有構造函數中匹配
// 第四個必定為null,不考慮
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// 反射調用空參構造
return instantiateBean(beanName, mbd);
}
因為autowireConstructor
方法的執行邏輯跟instantiateUsingFactoryMethod
方法的執行邏輯基本一致,只是將Method
對象換成了Constructor
對象,所以對這個方法我不再做詳細的分析。
我們主要就看看determineConstructorsFromBeanPostProcessors
這個方法吧,這個方法的主要目的就是推斷出候選的構造方法。
determineConstructorsFromBeanPostProcessors方法做了什麼?
// 實際調用的就是AutowiredAnnotationBeanPostProcessor中的determineCandidateConstructors方法
// 這個方法看起來很長,但實際確很簡單,就是通過@Autowired註解確定哪些構造方法可以作為候選方法,其實在使用factoryMethod來實例化對象的時候也有這種邏輯在其中,後續在總結的時候我們對比一下
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)
throws BeanCreationException {
// 這裏做的事情很簡單,就是將@Lookup註解標註的方法封裝成LookupOverride添加到BeanDefinition中的methodOverrides屬性中,如果這個屬性不為空,在實例化對象的時候不能選用SimpleInstantiationStrateg,而要使用CglibSubclassingInstantiationStrategy,通過cglib代理給方法加一層攔截了邏輯
// 避免重複檢查
if (!this.lookupMethodsChecked.contains(beanName)) {
try {
ReflectionUtils.doWithMethods(beanClass, method -> {
Lookup lookup = method.getAnnotation(Lookup.class);
if (lookup != null) {
Assert.state(this.beanFactory != null, "No BeanFactory available"); // 將@Lookup註解標註的方法封裝成LookupOverride
LookupOverride override = new LookupOverride(method, lookup.value());
try {
// 添加到BeanDefinition中的methodOverrides屬性中
RootBeanDefinition mbd = (RootBeanDefinition)
this.beanFactory.getMergedBeanDefinition(beanName);
mbd.getMethodOverrides().addOverride(override);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(beanName,
"Cannot apply @Lookup to beans without corresponding bean definition");
}
}
});
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName, "Lookup method resolution failed", ex);
}
this.lookupMethodsChecked.add(beanName);
}
// 接下來要開始確定到底哪些構造函數能被作為候選者
// 先嘗試從緩存中獲取
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
// Fully synchronized resolution now...
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);、
// 緩存中無法獲取到,進入正式的推斷過程
if (candidateConstructors == null) {
Constructor<?>[] rawCandidates;
try {
// 第一步:先查詢這個類所有的構造函數,包括私有的
rawCandidates = beanClass.getDeclaredConstructors();
}
catch (Throwable ex) {
// 省略異常信息
}
List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);
// 保存添加了Autowired註解並且required屬性為true的構造方法
Constructor<?> requiredConstructor = null;
// 空參構造
Constructor<?> defaultConstructor = null;
// 看方法註釋上說明的,這裏除非是kotlin的類,否則必定為null,不做過多考慮,我們就將其當作null
Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);
int nonSyntheticConstructors = 0;
// 對類中的所有構造方法進行遍歷
for (Constructor<?> candidate : rawCandidates) {
// 非合成方法
if (!candidate.isSynthetic()) {
nonSyntheticConstructors++;
}
else if (primaryConstructor != null) {
continue;
}
// 查詢方法上是否有Autowired註解
AnnotationAttributes ann = findAutowiredAnnotation(candidate);
if (ann == null) {
// userClass != beanClass說明這個類進行了cglib代理
Class<?> userClass = ClassUtils.getUserClass(beanClass);
if (userClass != beanClass) {
try {
// 如果進行了cglib代理,那麼在父類上再次查找Autowired註解
Constructor<?> superCtor =
userClass.getDeclaredConstructor(candidate.getParameterTypes());
ann = findAutowiredAnnotation(superCtor);
}
catch (NoSuchMethodException ex) {
// Simply proceed, no equivalent superclass constructor found...
}
}
}
// 說明當前的這個構造函數上有Autowired註解
if (ann != null) {
if (requiredConstructor != null) {
// 省略異常拋出
}
// 獲取Autowired註解中的required屬性
boolean required = determineRequiredStatus(ann);
if (required) {
// 類中存在多個@Autowired標註的方法,並且某個方法的@Autowired註解上被申明了required屬性要為true,那麼直接報錯
if (!candidates.isEmpty()) {
// 省略異常拋出
}
requiredConstructor = candidate;
}
// 添加到集合中,這個集合存儲的都是被@Autowired註解標註的方法
candidates.add(candidate);
}
// 空參構造函數
else if (candidate.getParameterCount() == 0) {
defaultConstructor = candidate;
}
}
if (!candidates.isEmpty()) {
// 存在多個被@Autowired標註的方法
// 並且所有的required屬性被設置成了false (默認為true)
if (requiredConstructor == null) {
// 存在空參構造函數,注意,空參構造函數可以不被@Autowired註解標註
if (defaultConstructor != null) {
// 將空參構造函數也加入到候選的方法中去
candidates.add(defaultConstructor);
}
// 省略日誌打印
}
candidateConstructors = candidates.toArray(new Constructor<?>[0]);
}
// 也就是說,類中只提供了一個構造函數,並且這個構造函數不是空參構造函數
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
candidateConstructors = new Constructor<?>[] {rawCandidates[0]};
}
// 省略中間兩個判斷,primaryConstructor必定為null,不考慮
// .....
}
else {
// 說明無法推斷出來
candidateConstructors = new Constructor<?>[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
這裏我簡單總結下這個方法的作用
-
獲取到類中的所有構造函數
-
查找到被@Autowired
註解標註的構造函數
- 如果存在多個被
@Autowired
標註的構造函數,並且其required屬性沒有被設置為true,那麼返回這些被標註的函數的集合(空參構造即使沒有添加@Autowired
也會被添加到集合中)
- 如果存在多個被
@Autowired
標註的構造函數,並且其中一個的required屬性被設置成了true,那麼直接報錯
- 如果只有一個構造函數被
@Autowired
標註,並且其required屬性被設置成了true,那麼直接返回這個構造函數
-
如果沒有被@Autowired
標註標註的構造函數,但是類中有且只有一個構造函數,並且這個構造函數不是空參構造函數,那麼返回這個構造函數
-
上面的條件都不滿足,那麼determineCandidateConstructors
這個方法就無法推斷出合適的構造函數了
可以看到,通過AutowiredAnnotationBeanPostProcessor
的determineCandidateConstructors
方法可以處理構造函數上的@Autowired
註解。
但是,請注意,這個方法並不能決定到底使用哪個構造函數來創建對象(即使它只推斷出來一個,也不一定能夠使用),它只是通過@Autowired
註解來確定構造函數的候選者,在構造函數都沒有添加@Autowired
註解的情況下,這個方法推斷不出來任何方法。真正確定到底使用哪個構造函數是交由autowireConstructor
方法來決定的。前文已經分析過了instantiateUsingFactoryMethod
方法,autowireConstructor
的邏輯基本跟它一致,所以這裏不再做詳細的分析。
factoryMethod跟構造函數的比較
整體邏輯比較
從上圖中可以看到,整體邏輯上它們並沒有什麼區別,只是查找的對象從factoryMethod換成了構造函數
執行細節比較
細節的差異主要體現在推斷方法上
它們之間的差異我已經在圖中標識出來了,主要就是兩點
- 通過構造函數實例化對象,多了一層處理,就是要處理構造函數上的@Autowired註解以及方法上的@LookUp註解(要決定選取哪一種實例化策略,
SimpleInstantiationStrategy
/CglibSubclassingInstantiationStrategy
)
- 在最終的選取也存在差異,對於facotyMehod而言,在寬鬆模式下(
除ConfigurationClassBeanDefinition
外,也就是掃描@Bean得到的BeanDefinition,都是寬鬆模式),會選取一個最精準的方法,在嚴格模式下,會選取一個參數最長的方法
- 對於構造函數而言,會必定會選取一個參數最長的方法
關於計算類型差異的補充內容
思考了很久,我還是決定再補充一些內容,就是關於上面兩幅圖的最後一步,對應的核心代碼如下:
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
if (typeDiffWeight < minTypeDiffWeight) {
factoryMethodToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousFactoryMethods = null;
}
-
判斷bd是嚴格模式還是寬鬆模式,上面說過很多次了,bd默認就是寬鬆模式,只要在ConfigurationClassBeanDefinition
中使用嚴格模式,也就是掃描@Bean標註的方法註冊的bd(對應的代碼可以參考:org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod
方法)
我們再看看嚴格模式跟寬鬆模式在計算差異值時的區別
public int getTypeDifferenceWeight(Class<?>[] paramTypes) {
// 計算實際使用的參數跟方法申明的參數的差異值
int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments);
// 計算沒有經過類型轉換的參數跟方法申明的參數的差異值
int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024;
return (rawTypeDiffWeight < typeDiffWeight ? rawTypeDiffWeight : typeDiffWeight);
}
public static int getTypeDifferenceWeight(Class<?>[] paramTypes, Object[] args) {
int result = 0;
for (int i = 0; i < paramTypes.length; i++) {
// 在出現類型轉換時,下面這個判斷才會成立,也就是在比較rawArguments跟paramTypes的差異時才可能滿足這個條件
if (!ClassUtils.isAssignableValue(paramTypes[i], args[i])) {
return Integer.MAX_VALUE;
}
if (args[i] != null) {
Class<?> paramType = paramTypes[i];
Class<?> superClass = args[i].getClass().getSuperclass();
while (superClass != null) {
// 如果我們傳入的值是方法上申明的參數的子類,那麼每多一層繼承關係,差異值加2
if (paramType.equals(superClass)) {
result = result + 2;
superClass = null;
}
else if (ClassUtils.isAssignable(paramType, superClass)) {
result = result + 2;
superClass = superClass.getSuperclass();
}
else {
superClass = null;
}
}
// 判斷方法的參數是不是一個接口,如果是,那麼差異值加1
if (paramType.isInterface()) {
result = result + 1;
}
}
}
return result;
}
- 嚴格模式(主要應用於@Bean標註的方法對應的BeanDefinition)
public int getAssignabilityWeight(Class<?>[] paramTypes) {
// 嚴格模式下,只有三種返回值
// 1.Integer.MAX_VALUE,經過類型轉換后還是不符合要求,返回最大的類型差異
// 因為解析后的參數可能返回一個NullBean(創建對象的方法返回了null,Spring會將其包裝成一個NullBean),不過一般不會出現這種情況,所以我們可以當這種情況不存在
for (int i = 0; i < paramTypes.length; i++) {
if (!ClassUtils.isAssignableValue(paramTypes[i], this.arguments[i])) {
return Integer.MAX_VALUE;
}
}
// 2.Integer.MAX_VALUE - 512,進行過了類型轉換才符合要求
for (int i = 0; i < paramTypes.length; i++) {
if (!ClassUtils.isAssignableValue(paramTypes[i], this.rawArguments[i])) {
return Integer.MAX_VALUE - 512;
}
}
// 3.Integer.MAX_VALUE - 1024,沒有經過類型轉換就已經符合要求了,返回最小的類型差異
return Integer.MAX_VALUE - 1024;
}
首先,不管是factoryMethod還是constructor,都是採用上面的兩個方法來計算類型差異,但是正常來說,只有factoryMethod會採用到嚴格模式(除非程序員手動干預,比如通過Bean工廠後置處理器修改了bd中的屬性,這樣通常來說沒有很大意義)
所以我們分為三種情況討論
1、factoryMethod+寬鬆模式
這種情況下,會選取一個最精確的方法,同時方法的參數要盡量長
測試代碼:
public class FactoryObject {
public DmzService getDmz() {
System.out.println(0);
return new DmzService();
}
public DmzService getDmz(OrderService indexService) {
System.out.println(1);
return new DmzService();
}
public DmzService getDmz(OrderService orderService, IndexService indexService) {
System.out.println(2);
return new DmzService();
}
public DmzService getDmz(OrderService orderService, IndexService indexService,IA ia) {
System.out.println(3);
return new DmzService();
}
}
public class ServiceImpl implements IService {
}
public class IAImpl implements IA {
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor"><!--必須要開啟自動注入,並且是通過構造函數進行自動注入,否則選用無參構造-->
<!--factoryObject 提供了創建對象的方法-->
<bean id="factoryObject" class="com.dmz.spring.instantiation.service.FactoryObject"/>
<bean class="com.dmz.spring.instantiation.service.OrderService" id="orderService"/>
<bean id="dmzService" factory-bean="factoryObject" factory-method="getDmz" />
<bean class="com.dmz.spring.instantiation.service.ServiceImpl" id="iService"/>
<bean class="com.dmz.spring.instantiation.service.IndexService" id="indexService"/>
</beans>
/**
* @author 程序員DMZ
* @Date Create in 23:59 2020/6/1
* @Blog https://daimingzhi.blog.csdn.net/
*/
public class XMLMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext cc =
new ClassPathXmlApplicationContext("application.xml");
}
}
運行程序發現,選用了第三個(getDmz(OrderService orderService, IndexService indexService)
)構造方法。雖然最後一個方法的參數更長,但是因為其方法申明的參數上存在接口,所以它的差異值會大於第三個方法,因為不會被選用
2、factoryMethod+嚴格模式
這種情況下,會選取一個參數盡量長的方法
測試代碼:
/**
* @author 程序員DMZ
* @Date Create in 6:28 2020/6/1
* @Blog https://daimingzhi.blog.csdn.net/
*/
@ComponentScan("com.dmz.spring.instantiation")
@Configuration
public class Config {
@Bean
public DmzService dmzService() {
System.out.println(0);
return new DmzService();
}
@Bean
public DmzService dmzService(OrderService indexService) {
System.out.println(1);
return new DmzService();
}
@Bean
public DmzService dmzService(OrderService orderService, IndexService indexService) {
System.out.println(2);
return new DmzService();
}
@Bean
public DmzService dmzService(OrderService orderService, IndexService indexService, IA ia) {
System.out.println("config " +3);
return new DmzService();
}
@Bean
public DmzService dmzService(OrderService orderService, IndexService indexService, IA ia, IService iService) {
System.out.println("config " +4);
return new DmzService();
}
}
/**
* @author 程序員DMZ
* @Date Create in 6:29 2020/6/1
* @Blog https://daimingzhi.blog.csdn.net/
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Config.class);
ac.refresh();
}
}
運行程序,發現選用了最後一個構造函數,這是因為在遍歷候選方法時,會先遍歷參數最長的,而在計算類型差異時,因為嚴格模式下,上面所有方法的差異值都是一樣的,都會返回Integer.MAX_VALUE - 1024
。實際上,在不進行手動干預的情況下,都會返滬這個值。
3、構造函數+寬鬆模式
這種情況下,也會選取一個參數盡量長的方法
之所以會這樣,主要是因為在autowireConstructor
方法中進行了一次短路判斷,如下所示:
在上圖中,如果已經找到了合適的方法,那麼直接就不會再找了,而在遍歷的時候是從參數最長的方法開始遍歷的,測試代碼如下:
@Component
public class DmzService {
// 沒有添加@Autowired註解,也會被當作候選方法
public DmzService(){
System.out.println(0);
}
@Autowired(required = false)
public DmzService(OrderService orderService) {
System.out.println(1);
}
@Autowired(required = false)
public DmzService(OrderService orderService, IService iService) {
System.out.println(2);
}
@Autowired(required = false)
public DmzService(OrderService orderService, IndexService indexService, IService iService,IA ia) {
System.out.println("DmzService "+3);
}
}
/**
* @author 程序員DMZ
* @Date Create in 6:29 2020/6/1
* @Blog https://daimingzhi.blog.csdn.net/
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Config.class);
ac.refresh();
}
}
這篇文章就到這裏啦~~!
文章很長,希望你耐心看完,碼字不易,如果有幫助到你的話點個贊吧~!
掃描下方二維碼,關注我的公眾號,更多精彩文章在等您!~~
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※為什麼 USB CONNECTOR 是電子產業重要的元件?
※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!
※台北網頁設計公司全省服務真心推薦
※想知道最厲害的網頁設計公司"嚨底家"!
※推薦評價好的iphone維修中心