Spring版本:
<version>5.2.1.RELEASE</version>
上一篇:15-Spring源碼解析之refresh(8)——【finishBeanFactoryInitialization】
本篇我們來解決上一篇中遺留的問題,即finishBeanFactoryInitialization
中是如何通過getBean
來創建Bean
實例的。
爲了更好更清晰的理解Bean
的生命週期。我首先給出一個很簡單的例子。這個例子包含:
- 實體類(
User
)- 實現了
InitializingBean
接口,實現這個接口的目的是更好的看afterPropertiesSet
方法的執行時機 - 這個類不包含
@Autowired
、@Value
、@Resource
等註解,因爲這些註解需要在Bean
創建的時候進行解析,爲了更好的理解Bean
生命週期的整體邏輯,這裏先不額外增加以上註解,後續文章會講解這些註解在Bean
創建中的哪一步起作用。
- 實現了
- 自定義
BeanPostProcessor
類MyBeanPostProcessor
。- 實現了
BeanPostProcessor
接口,重寫postProcessBeforeInitialization
方法和postProcessAfterInitialization
方法。 - 例子中包含這個接口的目的是爲了看
postProcessBeforeInitialization
和postProcessAfterInitialization
的執行時機
- 實現了
- 配置類(
MainConfig
)- 這個類只有兩個註解:
@Configuration
:標註當前類是配置類的註解,用於標註Spring
首先解析的類@ComponentScan
:包掃描註解,用於掃描 實現類User
和自定義類MyBeanPostProcessor
- 這個類只有兩個註解:
下面給出這個例子的源代碼
一、例子
包結構
1. 實體類User
package com.spring.csdn;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class User implements InitializingBean {
private String name;
public User() {
System.out.println("User類的 【無參構造器】");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("User類的 【afterPropertiesSet】 方法");
}
}
2. 自定義BeanPostProcessor
類MyBeanPostProcessor
package com.spring.postprocessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization -> 當前類爲:" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization -> " + beanName);
return bean;
}
}
3. 配置類MainConfig
package com.spring.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("com.spring")
public class MainConfig {
}
4. 測試類Test
package com.spring;
import com.spring.config.MainConfig;
import com.spring.csdn.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
User user = applicationContext.getBean(User.class);
}
}
5. 輸出結果
我們知道BeanPostProcessor
會攔截每個Bean
的創建,然後在每個Bean
的初始化方法執行前執行postProcessBeforeInitialization
,在初始化方法執行後執行postProcessAfterInitialization
。所以可以看到輸出結果中,回對Spring
中的每一個類都執行了postProcessBeforeInitialization
和postProcessAfterInitialization
方法。 而有關構造器和afterPropertiesSet
的執行時機,我們下面來慢慢分析。
二、getBean
獲取Bean
實例
2.1 當前beanFactory
中的值
上一篇文章,我們講到了finishBeanFactoryInitialization
方法在最後會調用getBean
方法獲取Bean
實例。那麼接下我們就具體看一下它是如何獲取到Bean
實例的。
在看這個方法之前,我們有必要看一下當前BeanDefinition
的情況,以及beanFactory
已經創建Bean
的情況。
beanDefintion
的情況:
beanFactory
的registeredSingletons
屬性保存已經創建的Bean
的情況。
從上面圖片可以看出當前容器中有12個Bean
已經被創建了,其中只有registeredSingletons[9]
是我們自己的Bean
,其他的都是Spring
內部自己創建的Bean
,我們暫時不考慮Spring
內部自己創建的Bean
的作用(因爲涉及的內容太多了,有一些已經在前面的文章中講過了)。
接下來是真正到了看一下finishBeanFactoryInitialization
是如何獲取到Bean
實例的時候了。
2.2 getBean
方法
我們以獲取User
類爲例子進行講解。接着上篇文章,上篇文章在最後finishBeanFactoryInitialization
方法調用preInstantiateSingletons
方法,preInstantiateSingletons
方法在每一個for
循環裏面調用getBean
方法。現在我們就進入getBean
方法。
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
它調用的是AbstractBeanFactory
類的getBean
方法,我們可以看一下AbstractBeanFactory
類實際上有4個getBean
方法。
注意,在初始化容器的時候,調用的是getBean(String)
方法。但是這裏還需要注意的另外一個事情是:雖然AbstractBeanFactory
類有4個getBean
方法,但是每個方法都調用了doGetBean
方法,且AbstractBeanFactory
的doGetBean
方法只有一個。
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, null, args, false);
}
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
throws BeansException {
return doGetBean(name, requiredType, args, false);
}
2.3 doGetBean
高能預警:這個doGetBean
做了好多事情,而且代碼有點長。
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// ------------------------------------------【功能一】--2.3.1詳細介紹----------------------------------------
// ------------------------------------------轉換對應 beanName----------------------------------------
final String beanName = transformedBeanName(name);
Object bean;
// ------------------------------------------【功能二】--2.3.2詳細介紹----------------------------------------
// ------------------------------------------嘗試從緩存中加載單例----------------------------------------
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
// ------------------------------------------根據【功能二】返回的結果進行------------------------------------------
// ------------------------------------------【功能三】------------------------------------------
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// -------------------------------------------------------------------------
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// ------------------------------------------根據【功能二】返回的結果進行------------------------------------------
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// ------------------------------------------【功能三】------------------------------------------
// 只有singleton情況下才會嘗試解決循環依賴
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// ------------------------------------------【功能四】------------------------------------------
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
// ------------------------------------------【功能五】---2.3.3詳細介紹---------------------------------------
if (!typeCheckOnly) {
// 將創建的Bean加入到beanFactory的alreadyCreated屬性中
markBeanAsCreated(beanName);
}
// ------------------------------------------【功能六】------------------------------------------
try {
// BeanDfinitiaon定義公共的抽象累是AbstractBeanDefinition。
// 普通的Bean在Spring註冊BeanDefinition的時候,實例化出來的是GenericBeanDefinition
// Spring內置的Bean在註冊BeanDefinition的時候,實例化出來的是RootBeanDefinition,
// 這時候,就要用getMergedLocalBeanDefinition將所有的BeanDefinition都轉換爲RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// ------------------------------------------【功能七】--尋找依賴----------------------------------------
// Guarantee initialization of beans that the current bean depends on.
// 若當前Bean有依賴,則先創建依賴Bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// ------------------------------------------【功能八】核心方法:下一篇文章重點介紹---------------------------------------
// 針對不同的scope進行bean的創建
// ---------------------------【Singleton】類型---------------------------
if (mbd.isSingleton()) {
// 核心方法:下一篇文章重點介紹!!!
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// ---------------------------【Prototype】類型---------------------------
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// ---------------------------【其他Scope】類型---------------------------
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// ------------------------------------------【功能九】------------------------------------------
// Check if required type matches the type of the actual bean instance.
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
在本文的最後會總結doGetBean
方法的功能,我們先一步步拆解這裏面的每個功能所作的事情。
2.3.1 【功能一】 transformedBeanName
根據這個方法的名字可以知道,該方法的作用是:轉換對於的beanName
,那這裏爲什麼要轉換呢?
實際上,在創建Spring
容器的過程中是不需要轉換beanName
的,因此在創建Spring
容器的時候,該方法還是將我們原來的beanName
返回。
但是當我們在獲取Spring
容器之後,自己在程序中調用getBean
方法的時候,就可能會用到這個方法。比如我們在程序中寫:
public class Test {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
PeopleFactory peopleFactory = (PeopleFactory)applicationContext.getBean("&people");
}
}
在寫applicationContext.getBean();
的時候,我們會給getBean
方法傳入String
類型的參數。當獲取的bean
是一個實現了FactoryBean
接口的類的時候,transformedBeanName
方法就起作用了。
下面看一下transformedBeanName
方法的實現:
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
從上面方法可以看出,transformedBeanName
首先調用了BeanFactoryUtils.transformedBeanName
。
// BeanFactoryUtils類的transformedBeanName方法
// String FACTORY_BEAN_PREFIX = "&";
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
return name;
}
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}
可以看出,先去除FactoryBean
的修飾符&
,即如果name = &people
,那麼會首先除去&
而使name = people
。
返回後transformedBeanName
方法又調用canonicalName
方法
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
2.3.2 【功能二】getSingleton
從緩存中加載單例
單例在Spring
的同一個容器中只會被創建一次,創建完成後,會把創建好的Bean
加入到緩存中。
因此Spring
每次在getSingleton
獲取Bean
的時候會先嚐試從緩存中加載,如果加載不成功再次從singletonFactories
中加載。因爲在創建單例Bean
的時候會存在依賴注入的情況,而在創建依賴的時候爲了避免循環依賴,在Spring
中創建Bean
的時候,執行createBean
-> doCreateBean
-> createBeanInstance
之後,就會將剛實例化好還沒有進行屬性注入(populate
)的Bean
加入到緩存中。一旦下一個Bean
創建的時候需要依賴上一個Bean
,則直接使用ObjectFactory
。
我們先來看看getSingleton
方法所在的類:
這個類有3個getSingleton
方法。而我們從緩存中加載單例是調用的getSingleton(beanName)
方法。
下面我們來具體看一下Spring
是怎麼從緩存中加載單例的。
public Object getSingleton(String beanName) {
// 第二個參數設置爲true表示:允許早期依賴
return getSingleton(beanName, true);
}
它又調用了getSingleton(beanName, true)
方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 檢查緩存singletonObjects中是否存在beanName的實例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 如果此Bean正在加載則不處理
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
singletonObjects
:用於保存beanName
和bean
實例之間的關係singletonFactories
:用於保存beanName
和創建bean
工廠之間的關係earlySingletonObjects
:用於保存beanName
和bean
實例之間的關係,與singletonObjects
不同之處在於:當一個單利bean
被放到這裏面之後,那麼當bean
還在創建過程種,就可以通過getBean
方法獲取到了,目的是用來檢查循環依賴。registeredSingletons
:用於保存當前所有已經創建的bean
。
2.3.3 【功能五】markBeanAsCreated
走到這一步說明緩存中沒有beanName
的單例,所以下面要開始創建Bean
了,那麼在創建之前,我們需要將即將創建的Bean
加入到beanFactory
的alreadyCreated
屬性中。
protected void markBeanAsCreated(String beanName) {
if (!this.alreadyCreated.contains(beanName)) {
synchronized (this.mergedBeanDefinitions) {
if (!this.alreadyCreated.contains(beanName)) {
// 將RootBeanDefinition的re-merge屬性設置爲true
// 因爲我們要創建這個bean了,所以以放在創建過程中它的metadata發生變化
clearMergedBeanDefinition(beanName);
this.alreadyCreated.add(beanName);
}
}
}
}
三、總結
doGetBean
方法的工作流程:
doGetBean
方法是爲了獲取bean
,首先從緩存中獲取,緩存中沒有就開始創建Bean
,而本篇文章只講到了從緩存中獲取,下一篇文章講解重頭戲,
-
getSingleton -> createBean
來創建Bean
。