一、前言
最近在看spring
源碼,發現之前看的很多細節已經忘了,於是決定在看源碼的過程中也把主要的流程用博客記載下來,希望自己能堅持下來吧。
spring
已經發展很久,整個體系已經變得很龐大了。爲了能更好的把源碼看下去,我決定從最基礎也是最核心的IOC
開始切入,並且從最原始的xml
解析開始看。面對這樣一個龐大的體系,我認爲從最原始的方式開始學習,才能更好的看懂它的設計和實現思路。
這一系列文章會默認你對於spring
的使用已經熟悉,並且不抗拒讀源碼。因爲很多的文字會在源碼片段上註釋-對於源碼解析的文章,我暫時也找不到更好的表述方法了。
二、一個簡單的示例
首先我們配置一個bean
:
<bean class="com.xiaoxizi.spring.service.AccountServiceImpl"
id="accountService" scope="singleton" primary="true"/>
對應的類:
public class AccountServiceImpl implements AccountService {
@Override
public String queryAccount(String id) {
return null;
}
}
測試類:
@Test
public void test1() {
applicationContext = new ClassPathXmlApplicationContext("spring.xml");
AccountService bean = applicationContext.getBean(AccountService.class);
System.out.println(bean);
}
運行結果:
com.xiaoxizi.spring.service.AccountServiceImpl@3e78b6a5
三、源碼解析
1. beanDefinition註冊流程
我們知道,spring
容器啓動的邏輯在refresh()
方法裏面。所以,話不多說,直接點進refresh()邏輯,具體位置是 org.springframework.context.support.AbstractApplicationContext#refresh
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
// 本篇博文主要講這個邏輯
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
...
}
本篇博文主要講xml解析的邏輯,暫時我們只關注 obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
繼續往下跟refreshBeanFactory()
,實際上方法位置在org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 創建一個beanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
// 加載所有的BeanDefinitions,實際解析xml的位置
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
繼續往下 org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 這裏使用了委託模式,把BeanDefinition的解析委託給了 BeanDefinitionReader
// 由於我們當前是解析xml,所以是委託給Xml...Reader。合理想象,註解方式將會委託給Anno...Reader
// 需要注意的是,我們把beanFactory引用傳遞給了Reader,之後會用到
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// ... 爲BeanDefinitionReader設置了一些不重要的屬性,略過。
// 加載BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
}
// XmlBeanDefinitionReader對應構造器,注意 beanFactory 是作爲 BeanDefinitionRegistry 傳入的
public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
super(registry);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
// 可以看到,是委託給Reader來加載BeanDefinition的
reader.loadBeanDefinitions(configResources);
}
// 配置文件位置實際上就是我們 new ClassPathXmlApplicationContext("xxx") 時傳入的
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// 可以看到,是委託給Reader來加載BeanDefinition的
reader.loadBeanDefinitions(configLocations);
}
}
經過一系列解析、包裝、加載邏輯之後… org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 配置文件的輸入流被加載成了Document --> XML解析知識,詳細可搜素 SAX解析
Document doc = doLoadDocument(inputSource, resource);
// 解析並註冊BeanDefinitions
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
// 異常處理,省略...
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 又來了,熟悉的委託模式,Document的解析被委託給了BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 委託documentReader解析註冊BeanDefinition,注意這裏傳入了一個 XmlReaderContext
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
// 可以看到XmlReaderContext的構造器傳入了當前類-XmlBeanDefinitionReader
// 而當前類持有BeanDefinitionRegistry,所以XmlReaderContext中持有了一個BeanDefinitionRegistry
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
細看DocumentReader
解析過程 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
// 代理的邏輯,我們暫時不看
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
// ...
}
// 解析xml之前的鉤子,暫時是空實現
preProcessXml(root);
// 解析邏輯
parseBeanDefinitions(root, this.delegate);
// 解析xml之前的鉤子,暫時是空實現
postProcessXml(root);
this.delegate = parent;
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 判斷是否是默認的命名空間
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 解析默認標籤
parseDefaultElement(ele, delegate);
}
else {
// 可以看到代理主要進行自定義標籤的解析 - CustomElement
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 可以看到代理主要進行自定義標籤的解析 - CustomElement
delegate.parseCustomElement(root);
}
}
這裏解釋一下 自定義標籤、自定義命名空間、默認標籤、默認命名空間的含義
<!-- 標籤前面有 xxx:即是spring的自定義標籤,我們也可以自己定義一個xiaozize:的標籤-之後會講到 -->
<context:component-scan base-package="com.xiaoxizi.spring"/>
<!-- 該標籤對應的命名空間在xml文件頭部beans標籤中聲明 -->
<beans xmlns:context="http://www.springframework.org/schema/context" ... />
<!-- 默認標籤沒有 xx: 前綴 -->
<bean class="com.xiaoxizi.spring.service.AccountServiceImpl"
id="accountService" scope="singleton" primary="true"/>
<!-- 對應的命名空間也在xml文件頭部beans標籤中聲明 -->
<beans xmlns="http://www.springframework.org/schema/beans" ... />
我們先看默認標籤的解析過程
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析import標籤,其實就是一個遞歸解析import導入的xml的過程
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 解析alias標籤,一般很少用這個功能,我們不看這個邏輯
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 解析bean標籤,重頭戲
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 解析beans標籤, 其實就是遞歸走了一次解析流程
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 這個方法眼熟吧?實際上我們就是從這個方法跟下來的
doRegisterBeanDefinitions(ele);
}
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 具體的解析過程,將會把bean標籤解析並封裝到BeanDefinition中
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 對bean標籤解析出來的BeanDefinition進行裝飾,用的很少
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 註冊BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
...
}
}
2. bean標籤解析
再正真解析bean標籤前,我們先看一下spring
的bean
標籤都有哪些屬性和默認子標籤
<bean
class="com.xiaoxizi.spring.service.AccountServiceImpl"
id="accountService"
name="aaa"
scope="singleton"
abstract="false"
parent="parent"
autowire="byType"
autowire-candidate="true"
primary="true"
depends-on="depends"
init-method="init"
destroy-method="destroy"
factory-bean="factoryBean"
factory-method="factoryMethod"
lazy-init="false"
>
<description>一些描述</description>
<constructor-arg ref="bean" value="固定值" type="參數類型" name="參數名稱" index="索引"/>
<property name="key1" value="固定值" ref="beanRef"/>
<meta key="key1" value="固定值"/>
<qualifier type="bean類型" value="限定的bean的名稱"/>
<lookup-method name="方法名" bean="bean名稱"/>
<replaced-method name="方法名" replacer="bean名稱">
<arg-type>參數類型,用於缺人唯一的方法</arg-type>
</replaced-method>
</bean>
逐一說一下bean
標籤中屬性作用:
屬性 | 作用 |
---|---|
class | 指明bean所屬的類 |
id | bean在ioc容器中的唯一標識,如果不填將取別名中的第一個 |
name | bean的別名 |
scope | bean的scope,一般日常開發都是使用默認的singleton單例,還有prototype多例。事實上web環境還有request和session。而且我們也可以自定義scope(之後會講到的) |
abstract | 是否是抽象的,抽象的bean不會被實例化,只能被繼承,用的很少 |
parent | 指定父bean,可以結合abstract一起使用,當然parent指向的bean並不一定要是抽象的 |
autowire | 被自動裝配的模式,有byType,byName等可選 |
autowire-candidate | 是否能被其他bean自動裝配,false的話該bean將不能被其他bean注入,讀者可以自行嘗試一下 |
primary | 如果自動裝配時匹配到多個bean,標記爲primary的bean將被優先注入。 |
depends-on | 依賴,依賴的bean將會先被實例化 |
init-method | bean實例化之後將會調用的方法 |
destroy-method | bean銷燬時將會調用的方法,需要主要的是,只有單例的bean,IOC容器才持有其引用,IOC容器銷燬 --> bean銷燬時纔會觸發這個方法。 |
factory-bean | 工廠bean的名稱,需要與factory-method結合使用,創建bean時將會調用factory-bean.factory-method()來獲取當前類實例。 |
factory-method | 工廠bean方法,其實@Bean註解就是通過factory-bean、factory-method屬性的功能實現的。 |
lazy-init | 是否是懶加載的 |
bean
標籤的默認子標籤的作用:
子標籤 | 作用 |
---|---|
description | 沒啥作用,就是個描述而已 |
constructor-arg | 構造器注入時使用的標籤,標明每個參數需要的值。用得少,因爲可能會導致不能處理的循環依賴 |
property | 爲bean中的屬性注入值,常用 |
meta | bean的元數據信息,業務開發中比較少用,之後跟源碼過程中能看到使用的地方 |
qualifier | 與@Qualifier作用一致,當注入時出現多個匹配的bean時,將會注入qualifier限定的bean |
lookup-method | 可以理解爲覆蓋/重寫方法,把當前bean中的指定方法委託給指定的bean執行(例:把當前beanA.test() 方法委託給 beanB.test(),方法名必須一致),應用場景比較少。 |
replaced-method | 與lookup-method類似,只是該標籤多了子標籤用來準確定位方法,當待委託的方法有多個重名方法(重載)時可以使用。 |
繼續往下看解析過程org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 處理別名
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// 默認id爲beanName
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 如果不配置id,將會取第一個別名當做beanName
beanName = aliases.remove(0);
// ...
}
if (containingBean == null) {
// 校驗beanName、alias是否重複
// 實際上有一個set用來存所以用過的name,避免重複 BeanDefinitionParserDelegate#usedNames
checkNameUniqueness(beanName, aliases, ele);
}
// 要解析元素了,解析xml獲取一個beanDefinition
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// ... 跳過一些邏輯
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 把beanDefinition封裝成Holder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
解析方法parseBeanDefinitionElement
中我們將把xml bean標籤中的信息解析並封裝到 一個BeanDefinition
中,而之後(初始化流程),我們將會根據BeanDefinition
中的屬性來創建bean
實例。現在,先讓我們看一下這個BeanDefinition
的結構:
// 默認使用的是 GenericBeanDefinition
public class GenericBeanDefinition extends AbstractBeanDefinition {
// 這個子類中只有一個parentName屬性,明顯對應bean標籤的parent屬性
@Nullable
private String parentName;
}
// 找父類,基本可以看到屬性和bean標籤的內容是一一對應的,不過meta子標籤的信息還在父類裏面
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
@Nullable
private volatile Object beanClass;
@Nullable
private String scope = SCOPE_DEFAULT;
private boolean abstractFlag = false;
@Nullable
private Boolean lazyInit;
private int autowireMode = AUTOWIRE_NO;
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
@Nullable
private String[] dependsOn;
private boolean autowireCandidate = true;
private boolean primary = false;
// qualifier子標籤信息
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
@Nullable
private Supplier<?> instanceSupplier;
private boolean nonPublicAccessAllowed = true;
private boolean lenientConstructorResolution = true;
@Nullable
private String factoryBeanName;
@Nullable
private String factoryMethodName;
// constructor-arg 子標籤的信息
@Nullable
private ConstructorArgumentValues constructorArgumentValues;
// property子標籤的信息
@Nullable
private MutablePropertyValues propertyValues;
// lookup-method、replaced-method子標籤的信息
private MethodOverrides methodOverrides = new MethodOverrides();
@Nullable
private String initMethodName;
@Nullable
private String destroyMethodName;
@Nullable
private String description;
// 加載這個beanDefinition的資源 -> 哪個xml
@Nullable
private Resource resource;
}
// BeanMetadataAttributeAccessor extends AttributeAccessorSupport
public abstract class AttributeAccessorSupport implements AttributeAccessor, Serializable {
// 保存beanDefinition的元數據信息
/** Map with String keys and Object values. */
private final Map<String, Object> attributes = new LinkedHashMap<>();
}
好,我們繼續往下解析bean
標籤
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
// ...
// 獲取class屬性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 獲取parent屬性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 創建了一個BeanDefinition,感興趣的同學可以跟一下,實際上就是創建了一個
// GenericBeanDefinition 並且把 parent set 進去了
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析bean標籤上的屬性 scope, autowrite等
// 感興趣的同學可以跟一下,就是把值從xml中解析出來塞到beanDefinition而已
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// description屬性
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析meta子標籤
parseMetaElements(ele, bd);
// 解析lockup-method子標籤
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replaced-method子標籤
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析constructor-arg子標籤
parseConstructorArgElements(ele, bd);
// 解析property子標籤
parsePropertyElements(ele, bd);
// 解析qualifier子標籤
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
// ...
return null;
}
// 因爲解析的流程其實都差不多,這邊簡單挑幾個有代表性的看一下
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 循環所有bean標籤的子標籤,找到mate標籤
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
// 封裝成BeanMetadataAttribute
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
// 記住我們的beanDefinition是繼承BeanMetadataAttributeAccessor的
// 所以這裏其實也是放到beanDefinition中了
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 循環所有bean標籤的子標籤,找到lockup-method標籤
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
String beanRef = ele.getAttribute(BEAN_ELEMENT);
// 封裝成LookupOverride
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
// 我們這裏看一下MethodOverrides的結構
// 首先MethodOverrides是在beanDefinition創建的時候就初始化的
private MethodOverrides methodOverrides = new MethodOverrides();
public class MethodOverrides {
// 其實就是一個MethodOverride列表
private final Set<MethodOverride> overrides = new CopyOnWriteArraySet<>();
// ...
}
// 繼續看看MethodOverride
public abstract class MethodOverride implements BeanMetadataElement {
// 被代理/重寫的方法名
private final String methodName;
// 是否是重載的方法 - 重載的方法處理起來要複雜點
private boolean overloaded = true;
// ...
}
// MethodOverride只有兩個子類,LookupOverride 和 ReplaceOverride,看名字大家都知道對應哪個標籤了
public class LookupOverride extends MethodOverride {
// 提供重寫邏輯的bean的名稱
private final String beanName;
// 可以看到,這個屬性我們在解析xml的時候沒有用到。
// 這個應該是用來支持註解@Lockup的,因爲這個功能用的很少,我也沒去深究,不過字段的含義還是很好理解的
private Method method;
}
public class ReplaceOverride extends MethodOverride {
// 提供重寫邏輯的bean的名稱
private final String methodReplacerBeanName;
// 之前有說過replaced-method是用於目標方法有重載的情況,這個參數類型列表就是用來區分重載的方法的
// 也是replaced-method標籤的子標籤arg-type中定義的
private List<String> typeIdentifiers = new LinkedList<>();
}
那麼致此,我們的一個默認的bean
標籤就解析完畢了,並且把所有的信息封裝到了一個BeanDefinition
實例中,然後這個beanDefinition
將會註冊到我們的IOC
容器中去,爲下一步生成實例做準備。org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 具體的解析過程,將會把bean標籤解析並封裝到BeanDefinition中
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 對bean標籤解析出來的BeanDefinition進行裝飾,用的很少,但此處的是個spi很重要
// 這裏下一期再講
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 註冊BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
...
}
}
我們主要看一下注冊beanDefinition
的過程
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
// 註冊bean
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// 註冊別名
registry.registerAlias(beanName, alias);
}
}
}
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// ... 這裏去除掉了大部分的分支判斷和異常處理邏輯,有興趣的同學可以自行看一下
// registerBeanDefinition的主要邏輯其實是以下兩段
// 將當前beanDefinition放入兩個容器
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
// 這裏是把當前bean從 manualSingletonNames 中刪除,簡單看了下邏輯
// 對於通過 DefaultListableBeanFactory#registerSingleton(String beanName, Object singletonObject)
// 直接註冊到IOC容器中的單例bean,因爲沒有對應的beanDefinition,name相應的beanName會被記錄到這個set
// 而如果我們解析xml中獲取到了相應的beanDefinition,就會將其從set中移除
// 這個邏輯可以不看,不是主流程
removeManualSingletonName(beanName);
}
四、總結
spring xml bean
標籤的解析就完整了,其實簡單來講,就是通過一系列手段,拿到xml bean標籤中配置的各種屬性,封裝成一個BeanDefinition
對象,然後把這個對象存到我們IOC
容器的 beanDefinitionMap
、beanDefinitionNames
中。這兩個容器在之後的bean實例創建的過程中將會用到。
第一次寫這種源碼類的博客(其實算是第一次寫博客? 發現自己幾年前居然寫過,emmm…),感覺寫的很乾,希望自己能堅持下來並且有進步吧。
下一篇將會講spring 自定義標籤的解析以及應用~