Spring版本:
<version>5.2.1.RELEASE</version>
目錄
上一篇:17-Spring源碼解析之Bean的生命週期(2)——【getSingleton】和【createBean】
上一篇我們講到了getSingleton
方法調用createBean
方法來準備創建Bean
。通過上一篇文章,我們也瞭解到,createBean
方法首先調用resolveBeforeInstantiation
方法,resolveBeforeInstantiation
方法的返回值決定程序的後續執行步驟。即在執行resolveBeforeInstantiation
後,程序有以下兩個選擇:
- 如果創建了代理或者重寫了
InstantiationAwareBeanPostProcessor
的postProcessBeforeInstantiation
方法並在方法postProcessBeforeInstantiation
中改變了Bean
,則直接返回。 - 如果沒有改變
Bean
,就需要進行常規Bean
的創建。
而常規Bean
的創建就是createBean
調用doCreateBean
實現的。
一、doCreateBean
創建Bean
我們跟蹤了這麼多Spring
的代碼,或多或少也發現了一個規律:一個真正幹活的函數其實是以do
開頭的,比如doCreateBean
,而給我們錯覺的函數,比如getBean
,其實是從全局角度去做一些統籌工作。
廢話不多說,直接上doCreateBean
源碼
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
//-------------------------------------------------【功能一】-------------------------------------------------
// 如果是單例,則需要首先清除緩存,然後再開始創建
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
//-------------------------------------------------【功能二】--二節 詳細講解-----------------------------------------------
if (instanceWrapper == null) {
// 實例化Bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
//-------------------------------------------------【功能三】--1.1 詳細講解-----------------------------------------------
// 第二個BeanPostProcessor
// @Autowired 和 @Value註解就是再這裏被解析的
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}
//-------------------------------------------------【功能四】--1.2 詳細講解-----------------------------------------------
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 依賴處理
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//-------------------------------------------------【功能五】--依賴注入篇講解-----------------------------------------------
// 屬性填充
populateBean(beanName, mbd, instanceWrapper);
//-------------------------------------------------【功能六】--下一篇講解-----------------------------------------------
// 調用初始化方法
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
//-------------------------------------------------【功能七】---------------------------------------------------
// 解決循環依賴問題
if (earlySingletonExposure) {
// 從緩存中獲取當前beanName,因爲現在當前bean還沒有將自己放到緩存裏,
// 如果現在可以從緩存中獲取到該bean,那就說明一定是其他的bean依賴了這個bean
Object earlySingletonReference = getSingleton(beanName, false);
// 只有在檢測到循環依賴的情況下earlySingletonReference纔不爲空
if (earlySingletonReference != null) {
// 如果exposedObject 沒有在初始化方法中被改變,即沒有被增強
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
// 檢測依賴
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
// 因爲beanc創建後其所依賴的bean一定是已經創建的,
// actualDependentBeans不爲空,說明當前Bean創建後其依賴的且沒有全部創建完,也就是說存在循環依賴
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
//-------------------------------------------------【功能八】--註冊DisposableBean---------------------------------------
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
從上面代碼可以看出,doCreateBean
方法包含8個功能。
先不談Spring
是如何創建對象的,我們想一下如果我們自己創建一個對象的時候,程序都是怎麼執行的,首先調用構造器,然後調用setXXX()
方法設置屬性值,然後就可以開始使用了。
實際上Spring
也是按照這個思路去創建Bean
實例的。那我們就來看看他每一步都做了什麼並且是怎麼做的吧。
1.1 【功能三】applyMergedBeanDefinitionPostProcessors
方法
這是我們在Spring
創建Bean
的過程中第二次遇到Spring
執行我們的擴展接口了!我們看一下applyMergedBeanDefinitionPostProcessors
方法的實現。
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof MergedBeanDefinitionPostProcessor) {
MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
}
}
}
我們可以看出,該方法會執行MergedBeanDefinitionPostProcessor
類型的後置處理器的postProcessMergedBeanDefinition
方法。
我們看一下哪些BeanPostProcessor
是這個類型的:
看到了兩個熟悉的類:
-
AutowiredAnnotationBeanPostProcessor
- 解析
@Autowire
和@Value
註解 - 解析
@Inject
註解
- 解析
-
CommonAnnotationBeanPostProcessor
- 解析
javax.annotation
包下的JSR-250
註解:@Resource
註解 - 解析
@PostConstruct
註解 和@PreDestroy
註解
- 解析
說明,程序中的@Autowire
、@Value
、@Inject
、@Resource
、@PostConstruct
、 @PreDestroy
註解在這裏被解析。這裏不詳細講解,這裏只是對這個後置處理器有一個印象,我們還是要先看一下整個函數的概要思路,之後的文章會舉一個簡單的例子講解Spring
是如何解析以上註解的。
1.2 【功能四】依賴處理
doCreateBean
的【功能四】是爲了處理依賴的。因爲該處代碼判斷條件比較多,在下面又貼出來一遍方便講解
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
我們可以看到earlySingletonExposure
的值由3個條件決定。
earlySingletonExposure
:從字面的意思理解就是提早曝光的單例。下面我們看一下哪三個條件影響這個值- 【條件一】
mbd.isSingleton()
:此RootBeanDefinition
是否是單例 - 【條件二】
this.allowCircularReferences
:beanFactory
是否允許循環依賴。默認:允許(true
) - 【條件三】
isSingletonCurrentlyInCreation
:該Bean
是否在創建中。在Spring
中,會有一個專門的屬性默認爲DefaultSingletonBeanRegistry
的singletonCurrentlyInCreation
來記錄Bean
的加載狀態。- 在
Bean
開始創建前會將beanName
記錄在屬性中(詳見:上篇文章:1.1getSingleton
的【功能二】beforeSingletonCreation方法) - 在
Bean
創建結束後會將BeanName
從屬性中移除(詳見:上篇文章:1.2getSingleton
的【功能四】afterSingletonCreation方法)
- 在
- 【條件一】
經過以上分析,我們瞭解了變量earlySingletonExposure
是是否單例、是否允許循環依賴、是否對應的Bean
正在創建的條件的綜合。
當這三個條件都滿足時會執行addSingletonFactory
方法,那麼我們要知道將這個剛實例化完還沒有賦屬性值的Bean
提早暴露到beanFactory
的作用又是什麼呢?這裏就涉及到了循環依賴問題。 Spring
中的依賴注入和循環依賴問題是一個比較重要的問題,後續會有一篇單獨的文章介紹這兩個問題,這裏先不詳細介紹。 先有一個印象:這裏是爲了處理依賴而增加的操作。
二、 【功能二】createBeanInstance
創建Bean
實例
doCreateBean
是通過調用createBeanInstance
來實現創建Bean
實例功能的,然後他把創建好的實例放到了BeanWrapper
類型的對象中。想一想,創建實例是什麼?創建實例就是:調用構造方法啊!
但是我們在仔細看他是如何調用構造方法之前,我們需要了解一下BeanWrapper
類型是什麼,爲什麼把返回值放到BeanWrapper
類型的對象中。
2.1 BeanWrapper
接口
public interface BeanWrapper extends ConfigurablePropertyAccessor {
void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);
int getAutoGrowCollectionLimit();
Object getWrappedInstance();
Class<?> getWrappedClass();
PropertyDescriptor[] getPropertyDescriptors();
PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
}
這個接口的方法不是很多,我們可以通過看他的註解知道該類主要的一個功能就是:
提供分析和操作標準JavaBean
的操作:獲取和設置屬性值(單獨或批量),獲取屬性描述符以及查詢屬性的可讀性/可寫性的能力
Spring
對該接口的唯一實現類是:BeanWrapperImpl
,這個類的實現很多都是在調用cache
有關的類獲取對應的值,有興趣的同學可以自行查看源碼,這裏就不詳細介紹了。
下面我們就開始執行createBeanInstance
方法,這個方法我們就記住他相當於new
一個對象的時候,執行的構造函數方法。
2.2 createBeanInstance
方法
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
// 解析Class,實際上在createBean的時候已經解析過一次class了,這爲什麼又解析了一次。。
// 我還沒搞明白,待研究??
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// //確保class不爲空,並且訪問權限爲public 因此Spring創建不了Class訪問權限不是public的,
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
// 這個,也不知道怎麼回事,待研究??
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
//-------------------------------------------------【功能一】------------------------------------------
// 如果工廠方法不爲空則使用工廠方法初始化策略
// 若在配置類中利用@Bean的方式註冊Bean,那麼在創建該Bean的時候使用就是工廠方法初始化策略
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
//-------------------------------------------------【功能二】------------------------------------------
// 如果傳遞進來的構造函數的參數爲null
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
// 判斷緩存中是否已經解析過構造函數
// 如果解析過則將當前方法的解析標誌字段設置爲true
// resolvedConstructorOrFactoryMethod屬性 : 用於緩存已解析構造函數或工廠方法的包可見字段
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
// constructorArgumentsResolved屬性:將構造函數參數標記爲已解析的包可見字段。
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
//-------------------------------------------------【功能三】------------------------------------------
// 如果已經解析過則使用解析好的構造函數方法
if (resolved) {
if (autowireNecessary) {
// 構造函數自動注入
return autowireConstructor(beanName, mbd, null, null);
}
else {
// 使用默認構造函數構造
return instantiateBean(beanName, mbd);
}
}
//-------------------------------------------------【功能四】------------------------------------------
// Candidate constructors for autowiring?
// determineConstructorsFromBeanPostProcessors:查找Bean的所有有參的構造函數
// 並返回一個構造函數用於創建Bean
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
//-------------------------------------------------【功能五】------------------------------------------
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
//----------------------------------【功能六】-2.2.1 詳細講解--------------------------------------
// 使用無參構造函數構造
return instantiateBean(beanName, mbd);
}
以上可以看出createBeanInstance
方法包含了6個功能,實際上就是根據不同的策略來實例化Bean
實例。
其中有三種策略:(本文只講解使用註解的情況下遇到的三種策略)
-
【策略一】:
instantiateUsingFactoryMethod
工廠方法策略- 若在配置類中通過
@Bean
註解來註冊Bean
- 若在配置類中通過
-
【策略二】:
determineConstructorsFromBeanPostProcessors
方法解析構造函數後通過autowireConstructor
構造函數注入策略,項目中不常用。- 若在類中的構造函數上標註了
@Autowired
屬性會走這個方法,且該類是通過@Service
、@Repository
註冊到容器中的
- 若在類中的構造函數上標註了
-
【策略三】:
instantiateBean
- 使用無參的構造函數
我們知道,當我們的項目通過註解裝配的方式來構建時,我們是不會寫構造函數的,我們只是在相應的類中寫需要使用的屬性,而這些屬性賦值任務是通過依賴注入的方式實現的。因此,在這一步,我們只分析項目中會走到的方法instantiateBean
。
2.2.1 instantiateBean
方法
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
getInstantiationStrategy().instantiate(mbd, beanName, parent),
getAccessControlContext());
}
else {
//-------------------------------------------------核 心------------------------------------------
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
實例化策略:
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// 如果有需要覆蓋或者動態替換的方法,則需要使用CGLIB進行動態代理
// 這種情況是:用戶使用了 replace 或者 lookup 的配置方法
if (!bd.hasMethodOverrides()) {
// 進入這裏說明使用的是我們自己的無參構造器
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(
(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);
}
else {
// 獲取無參構造器
constructorToUse = clazz.getDeclaredConstructor();
}
// resolvedConstructorOrFactoryMethod :緩存已解析構造函數
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Throwable ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
// 執行無參構造器
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
調用BeanUtils.instantiateClass(constructorToUse);
來執行無參構造器
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
return KotlinDelegate.instantiateClass(ctor, args);
}
else {
Class<?>[] parameterTypes = ctor.getParameterTypes();
Assert.isTrue(args.length <= parameterTypes.length, "Can't specify more arguments than constructor parameters");
Object[] argsWithDefaultValues = new Object[args.length];
for (int i = 0 ; i < args.length; i++) {
if (args[i] == null) {
Class<?> parameterType = parameterTypes[i];
argsWithDefaultValues[i] = (parameterType.isPrimitive() ? DEFAULT_TYPE_VALUES.get(parameterType) : null);
}
else {
argsWithDefaultValues[i] = args[i];
}
}
//--------------------------------執行我們自己類的無參構造方法-----------------------------------------
return ctor.newInstance(argsWithDefaultValues);
}
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
以上,就完成了創建實例Bean
的過程,其中有一些項目中不會走到的方法,這裏就沒有詳細講解,比如如果解析有參構造器(determineConstructorsFromBeanPostProcessors
),如何執行有參構造器(autowireConstructor
)
三、總結
- 【
doCreateBean
】功能- 【功能一】 如果是單例則需要首先清除緩存,然後開始創建,防止在併發時同時創建
- 【功能二】 調用
createBeanInstance
方法實例化Bean
,將BeanDefinition
轉換爲BeanWrapper
- 【功能三】 調用
applyMergedBeanDefinitionPostProcessors
方法來做實例化Bean
後利用AutowiredAnnotationBeanPostProcessor
類型的後置處理器解析@Autowired
註解。 - 【功能四】依賴處理
- 【功能五】
populateBea
屬性填充 - 【功能六】
initializeBean
初始化 - 【功能七】循環依賴檢查
- 【功能八】註冊
DisposableBean
- 【
createBeanInstance
】功能:根據不同的情況執行不同的實例化策略-
【策略一】:
instantiateUsingFactoryMethod
工廠方法策略- 若在配置類中通過
@Bean
註解來註冊Bean
- 若在配置類中通過
-
【策略二】:
determineConstructorsFromBeanPostProcessors
方法解析構造函數後通過autowireConstructor
構造函數注入策略,項目中不常用。- 若在類中的構造函數上標註了
@Autowired
屬性會走這個方法,且該類是通過@Service
、@Repository
註冊到容器中的
- 若在類中的構造函數上標註了
-
【策略三】:
instantiateBean
- 使用無參的構造函數
-
doCreateBean
方法中還有兩個比較重要的方法:
-
populateBean
屬性注入- 這裏也會涉及循環依賴的情況,因此是一個比較重要的話題,在依賴注入篇會詳細講解,這裏不再詳述,只是記得在實例化
Bean
(createBeanInstance
)之後,初始化Bean
(initializeBean
)之前會執行屬性注入
- 這裏也會涉及循環依賴的情況,因此是一個比較重要的話題,在依賴注入篇會詳細講解,這裏不再詳述,只是記得在實例化
-
initializeBean
初始化Spring
給我們留了很多擴展點,其中在初始化前後就留了3個擴展點,非常重要。
下一篇我們繼續講解initializeBean
方法的實現。