Spring XML Bean 定義的載入和註冊
- 2020 年 4 月 14 日
- 筆記
- Spring IoC
前言
本篇文章主要介紹 Spring IoC 容器怎麼載入 bean
的定義元資訊。
下圖是一個大致的流程圖:
第一次畫圖,畫的有點爛。😂
正文
首先定義一個簡單的 POJO,如下:
public class User {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
再編寫一個 XML 文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user" class="com.leisurexi.ioc.domain.User">
<property name="id" value="1"/>
<property name="name" value="leisurexi"/>
</bean>
</beans>
最後再來一個測試類。
@Test
public void test(){
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("META-INF/spring-bean.xml");
User user = beanFactory.getBean("user", User.class);
System.out.println(user);
}
上面這段程式碼比較簡單,無非就是聲明 bean
工廠,然後通過指定的 XML 文件載入 bean
的定義元資訊,最後通過 bean
工廠獲取 bean
。
DefaultListableBeanFactory
首先我們來了解一下 DefaultListableBeanFactory
,下面是該類的類圖及層次結構。
- AliasRegistry:定義對
alias
的簡單增刪改等操作。 - SimpleAliasRegistry:主要使用
map
作為alias
的快取,並對介面AliasRegistry
進行實現。 - SingletonBeanRegistry:定義了對單例 bean 的註冊及獲取。
- BeanFactory:定義獲取單個
bean
及bean
的各種屬性。 - DefaultSingletonBeanRegistry:對介面
SingletonBeanRegistry
各函數的實現。 - HierarchicalBeanFactory:繼承
BeanFactory
,也就是在BeanFactory
定義的功能的基礎上增加了對parentBeanFactory
的支援。 - BeanDefinitionRegistry:定義了對
BeanDefinition
的各種增刪改操作。 - FactoryBeanRegistrySupport:在
DefaultSingletonBeanRegistry
基礎上增加了對FactoryBean
的特殊處理功能。 - ConfigurableBeanFactory:提供配置
BeanFactory
的各種方法。 - ListableBeanFactory:繼承
BeanFactory
提供了獲取多個bean
的各種方法。 - AbstractBeanFactory:綜合
FactoryBeanRegistrySupport
和ConfigurableBeanFactory
的功能。 - AutowireCapableBeanFactory:提供創建
bean
、自動注入、初始化以及應用bean
的後處理器。 - AbstractAutowireCapableBeanFactory:綜合
AbstractBeanFactory
並對介面AutowireCapableBeanFactory
進行實現。 - ConfigurableListableBeanFactory:
BeanFactory
配置清單,指定忽略類型及介面等。 - DefaultListableBeanFactory:綜合上面所有功能,主要是對
bean
註冊後的處理。
可以看到上面的介面大多數是定義了一些功能或在父介面上擴展了一些功能,DefaultListableBeanFactory
實現了所有介面,大多數默認情況下我們所使用的 beanFactory
就是 DefaultListableBeanFactory
。
1.AbstractBeanDefinitionReader#loadBeanDefinitions 方法
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
// 獲取 resourceLoader,這邊是 PathMatchingResourcePatternResolver
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}
// 判斷 resourceLoader 是否是 ResourcePatternResolver,我們這邊是符合的
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
// 根據路徑獲取所欲符合的配置文件並封裝成 Resource 對象
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 根據 Resource 載入 bean definition,並返回數量,見下面詳解
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
// 只能通過絕對路徑載入單個資源
Resource resource = resourceLoader.getResource(location);
// 根據 Resource 載入 bean definition,並返回數量,見下面詳解
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}
上面方法主要是將資源文件轉換為 Resource
對象,然後調用 loadBeanDefinitions(Resource...)
載入 BeanDefinition
。
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int count = 0;
for (Resource resource : resources) {
// 載入 BeanDefinition,見下文詳解
count += loadBeanDefinitions(resource);
}
return count;
}
該方法主要就是遍歷 resources
然後調用 XmlBeanDefinitionReader#loadBeanDefinitions(Resource)
。
2.XmlBeanDefinitionReader#loadBeanDefinitions
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
// 將 Resource 封裝成 EncodedResource,也就是對資源指定編碼和字符集
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
// 當前正在載入的 EncodedResource
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 如果當前 EncodedResource 以及存在,代表出現了循環載入,拋出異常
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 獲取 Resource 的輸入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 將 inputStream 封裝成 org.xml.sax.InputSource
InputSource inputSource = new InputSource(inputStream);
// 如果 encodedResource 的編碼不為空,設置 inputSource 的編碼
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 載入 BeanDefinition (方法以 do 開頭,真正處理的方法)
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
// 關閉流
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
// 當前資源以及載入完畢,從 currentResources 中移除
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
上面主要將 Resource
封裝成 EncodedResource
,也就是制定資源的編碼和字符集。然後獲取 Resource
的輸入流 InputStream
,並封裝成 InputSource
設置其編碼,最終調用 doLoadBeanDefinitions
開始真正的載入流程。
3.XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 根據 inputSource 和 resource 載入 XML 文件,並封裝成 Document,見下文詳解
Document doc = doLoadDocument(inputSource, resource);
// 用 doc 去解析和註冊 bean definition,見下文詳解
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
上面程式碼拋開異常處理,邏輯非常簡單,就是用 inputSource
和 resource
載入 XML 文件,並封裝成 Document
對象,然後去註冊 BeanDefinition
。
4.XmlBeanDefinitionReader#doLoadDocument
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
// 獲取 XML 文件的驗證模式
protected int getValidationModeForResource(Resource resource) {
// 如果手動指定了驗證模式則使用指定的驗證模式
int validationModeToUse = getValidationMode();
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
// 如果未指定則使用自動檢測,其實就是判斷文件是否包含 DOCTYPE,如果
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
// Hmm, we didn't get a clear indication... Let's assume XSD,
// since apparently no DTD declaration has been found up until
// detection stopped (before finding the document's root tag).
// 如果沒有找到驗證,默認使用 XSD 模式,因為 DTD 已經不維護了
return VALIDATION_XSD;
}
// DefaultDocumentLoader.java
// 這裡就是使用 DocumentLoader 去載入 XML 文件。首先創建 DocumentBuilderFactory,再通過 DocumentBuilderFactory 創建 DocumentBuilder,進而解析 inputSource 來返回 Document 對象
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
detectValidationMode()
方法其實就是讀取文件內容,判斷是否包含 DOCTYPE
,如果包含就是 DTD 否則就是 XSD。
獲取 XML 配置文件的驗證模式。XML 文件的驗證模式是用來保證 XML 文件的正確性,常見的驗證模式有 DTD 和 XSD。
DTD XML 格式示例:
STD XML 格式示例:
5.XmlBeanDefinitionReader#registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 獲取 DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 獲取註冊中心,再靠註冊中心獲取註冊之前以及註冊過的 BeanDefinition 數量
int countBefore = getRegistry().getBeanDefinitionCount();
// 解析並註冊 BeanDefinition,見下文詳情
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 獲取註冊過後 BeanDefinition 數量減去註冊之前的數量,得到的就是本次註冊的數量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
這裡的 getRegistry()
方法返回的就是 DefaultListableBeanFactory
,因為就只有它實現了 BeanDefinitionRegistry
介面。
DefaultListableBeanFactory
中定義了存放 BeanDefinition
的快取,所以 getBeanDefinitionCount()
方法返回的就是 beanDefinitionMap
的數量。
// 存放 BeanDefinition 的快取,key 為 bean 的名稱,value 就是其 BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
6.DefaultBeanDefinitionDoucumentReader#registerBeanDefinitions
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 提取 root,註冊 BeanDefinition (理論上 Spring 的配置文件,root 都應該是 beans 標籤)
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
// 專門處理解析
this.delegate = createDelegate(getReaderContext(), root, parent);
// 校驗 root 節點的命名空間是否為默認的命名空間 (默認命名空間//www.springframework.org/schema/beans)
if (this.delegate.isDefaultNamespace(root)) {
// 處理 profile 屬性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
// 校驗當前節點的 profile 是否符合當前環境定義的,如果不是則直接跳過,不解析該節點下的內容
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 解析前處理,留給子類實現
preProcessXml(root);
// 解析註冊 BeanDefinition,見下文詳解
parseBeanDefinitions(root, this.delegate);
// 解析後處理,留給子類實現
postProcessXml(root);
this.delegate = parent;
}
profile
主要是用於多環境開發,例如:
集成到 Web 環境時,在 web.xml 中加入以下程式碼:
<coontext-param>
<param-name>Spring.profiles.active</param-name>
<param-value>dev</param-value>
</coontext-param>
preProcessXml()
和 postProcessXml()
採用的 模板方法模式,子類可以繼承 DefaultBeanDefinitionDoucumentReader
來重寫這兩個方法,這也是解析前後的擴展點。
7.DefaultBeanDefinitionDoucumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 校驗 root 節點的命名空間是否為默認的命名空間,這裡為什麼再次效驗,因為調用解析前調用了preProcessXml() 方法,可能會對節點做修改
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)) {
// 默認命名空間節點的處理,例如 <bean id="..." class="..."/>
parseDefaultElement(ele, delegate);
}
else {
// 自定義命名空間節點的處理,例如 <context:compoent-scan/>、<aop:aspectj-autoproxy>
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 自定義命名空間節點的處理
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 對 import 標籤的處理
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);
}
}
上面 parseDefaultElement
方法中對 bean 標籤的處理方法 processBeanDefinition
最為重要,下面來著重分析一下。
7-1.DefaultBeanDefinitionDocumentReader#processBeanDefinition
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 將 ele 解析成 BeanDefinitionHolder,見下面詳解
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 若存在默認標籤下的子節點下不再有自定義屬性,需要再次對自定義標籤再進行解析(基本不用,不做深入分析)
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 註冊最終的 BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 發出響應事件,通知相關監聽器,這個 bean 已經註冊完
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
上面程式碼主要步驟如下:
- 將
Element
解析成BeanDefinitionHolder
。 - 若存在默認標籤下的子節點下有自定義屬性,需要再次對自定義標籤再進行解析。
- 註冊
BeanDefinition
。 - 發出響應事件,通知相關監聽器,這個 bean 已經註冊完,具體詳情可以查看
ReaderEventListener#componentRegistered()
方法。可以通過以下方式去註冊這個監聽器:
7-1-1.BeanDefinitionParseDelegate#parseBeanDefinitionElement
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
// 解析元素,封裝成 BeanDefinitionHolder
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 獲取 id 屬性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 獲取 name 屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
// 將 name 屬性所有的名稱按照逗號或者分號(,;)分割成數組放入別名集合 aliases
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// beanName 默認使用 id
String beanName = id;
// 沒有指定 id 屬性 && 指定了 name 屬性
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// 如果沒有指定id,beanName 等於第一個別名,剩下的依然作為別名使用
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// 驗證 beanName 和 aliases 是否在同一個 <beans> 下已經存在
checkNameUniqueness(beanName, aliases, ele);
}
// 將元素解析成 GenericBeanDefinition
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
// 如果不存在 beanName 會根據 Spring 的命名規則生成一個
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 用 beanDefinition 和 beanName 以及 aliasesArray 構建 BeanDefinitionHolder
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// 獲取 class 屬性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
// 獲取 parent 屬性
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 創建用於承載屬性的 AbstractBeanDefinition 類型的 GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析默認 bean 的各種屬性,見下方 parseBeanDefinitionAttributes 方法詳解
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取 description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析元數據,見下方 parseMetaElements 方法詳解
parseMetaElements(ele, bd);
// 解析 lookup-method 屬性,很少使用,不具體介紹
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析 replaced-method 屬性,很少使用,不具體介紹
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析 constructot-arg 屬性,見下方 parseConstructorArgElements 方法詳解
parseConstructorArgElements(ele, bd);
// 解析 property 屬性,見下方 parsePropertyElements 方法詳解
parsePropertyElements(ele, bd);
// 解析 qualifier 屬性,見下方 parseQualifierElements 方法詳解
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
上面程式碼主要將 bean
標籤,解析為 BeanDefinitionHolder
返回,主要步驟如下:
- 解析
id
、name
屬性,將name
按照,
或者;
分割作為別名 (alias
)。 - 解析剩下的屬性,並封裝成
GenericBeanDefinition
。- 調用
parseBeanDefinitionAttributes
方法解析bean
標籤的所有屬性。 - 調用
parseMetaElements
方法解析元數據資訊。 - 調用
parseLookupOverrideSubElements
方法解析lookup-method
子標籤。 - 調用
parseReplacedMethodSubElements
方法解析replaced-method
子標籤。 - 調用
parseConstructorArgElements
方法解析constructor-arg
子標籤。 - 調用
parsePropertyElements
方法解析property
子標籤。 - 調用
parseQualifierElements
方法解析qualifier
子標籤。
- 調用
- 判斷
beanName
是否存在,不存在會根據 Spring 的命名規則生成一個。 - 使用
beanDefinition
、beanName
、aliasesArray
構建BeanDefinitionHolder
返回。
我們這邊可以簡單看一下 BeanDefinitionHolder
的屬性,如下:
public class BeanDefinitionHolder implements BeanMetadataElement {
// bean 的定義元資訊
private final BeanDefinition beanDefinition;
// bean 的名稱
private final String beanName;
// bean 的別名數組
@Nullable
private final String[] aliases;
...省略其它程式碼
}
BeanDefinitionHolder
其實就是對 BeanDefinition
的包裝。
parseBeanDefinitionAttributes
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// 解析 singleton 屬性
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
// singleton 屬性已經不支援了,使用了會直接拋出異常,請使用 scope 屬性替代
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
// 解析 scope 屬性
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// Take default from containing bean in case of an inner bean definition.
bd.setScope(containingBean.getScope());
}
// 解析 abstract 屬性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
// 解析 lazy 屬性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
// 若沒有設置或者設置成其他字元都會被設置為默認值 false
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// 解析 autowire 屬性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 解析 depends-on 屬性
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// 解析 autowire-candidate 屬性
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
}
// 解析 primary 屬性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
// 解析 init-mehtod 屬性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
// 如果 beans 標籤指定了 default-init-method 屬性,則會給所有此標籤下的 bean 都指定該 init-method
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
// 解析 destory-method 屬性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
// 如果 beans 標籤指定了 default-destory-method 屬性,則會給所有此標籤下的 bean 都指定該 destory-method
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
}
// 解析 factory-method 屬性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 解析 factory-bean 屬性
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
上面方法完成了對所有 bean
標籤屬性的解析。值得注意的地方是如果同時指定了 bean
標籤的 init-method
和 beans
標籤的 default-init-method
屬性,那麼優先使用前者,destory-mehtod
標籤也是一樣。
大家可以去看一下
AbstractBeanDefinition
中定義的屬性就一目了然了,這裡限於篇幅原因就不展示了。
parseMetaElements
這裡先回顧一下元數據 meta
屬性的使用。
這個屬性並不會體現在 user
的屬性當中,而是一個額外的聲明,當需要使用裡面的資訊時可以通過 BeanDefinition#getAttribute(key)
來獲取。
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
// 獲取所有子節點
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 提取 meta
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
// 使用 key、value 構造 BeanMetadataAttribute
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
// 記錄資訊
attributeAccessor.addMetadataAttribute(attribute);
}
}
}
parseConstructorArgElements
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
// 獲取所有子節點
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 提取 constructor-arg
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
// 解析 constructor-arg
parseConstructorArgElement((Element) node, bd);
}
}
}
// <constructor-arg index="0" type="" value=""/>
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 提取 index 屬性
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 提取 type 屬性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 提取 name 屬性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// index 不為空
if (StringUtils.hasLength(indexAttr)) {
try {
// 轉換為 int 類型
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
// 解析屬性值,見下面詳解
Object value = parsePropertyValue(ele, bd, null);
// 使用 ConstructorArgumentValues.ValueHolder 類型來封裝解析出來的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 如果 type 不為空,設置 ValueHolder 的 type
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
// 如果 name 不為空,設置 ValueHolder 的 name
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
// 判斷 index 是否重複,重複則拋出異常
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { error("Ambiguous constructor-arg entries for index " + index, ele);
}
// 將 index 和 valueHolder 以 key-value的形式 添加到 BeanDefinition 的 ConstructorArgumentValues 當中
else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else {
try {
// 這裡就是 constructor-arg 標籤中沒有指定 index 屬性
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
// 將 valueHolder 添加 BeanDefinition 的 GenericArgumentValue 中
// 這裡和上面的 IndexedArgumentValue 類似,只不過上面是 Map,這裡是 List
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
上面程式碼首先提取 constructor-arg
標籤中必要的屬性 (index
、type
、name
)。
- 如果指定了
index
屬性:- 解析
constructor-arg
的子元素。 - 使用
ConstructorArgumentsValues.ValueHolder
類型來封裝解析出來的元素。 - 將
type
、name
和index
屬性一併封裝在ConstructorArgumentsValues.ValueHolder
類型中,並添加到當前BeanDefinition
的ConstructorArgumentValues
中的LinkedHashMap
類型的屬性indexedArgumentValues
中。
- 解析
- 如果有指定
index
屬性:- 解析
constructor-arg
的子元素。 - 使用
ConstructorArgumentsValues.ValueHolder
類型來封裝解析出來的元素。 - 將
type
、name
和index
屬性一併封裝在ConstructorArgumentsValues.ValueHolder
類型中,並添加到當前BeanDefinition
的ConstructorArgumentValues
中的ArrayList
類型的屬性genericArgumentValues
中。
- 解析
parsePropertyValue
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// Should only have one child element: ref, value, list, etc.
// 獲取所有子節點,例如 list、map等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 跳過 description 或者 meta 不處理
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
// 提取 ref 屬性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 提取 value 屬性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 如果同時有 ref 和 value 屬性 || 有 ref 或 value 屬性同時又有子元素,拋出異常
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
// 只有 ref 屬性,使用 RuntimeBeanReference 封裝對應的 ref 名稱 (該 ref 值指向另一個 bean 的 beanName)
// RuntimeBeanReference 起到佔位符的作用, ref 指向的 beanName 將在運行時被解析成真正的 bean 實例引用
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
// 只有 value 屬性,使用 TypedStringValue 封裝
else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
// 解析子元素
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
// 沒有子元素,也沒有 ref 和 value,直接拋出異常
error(elementName + " must specify a ref or value", ele);
return null;
}
}
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
// 校驗是否為默認的命名空間,如果不是則走解析自定義節點程式碼
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
// 解析 bean 節點
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
// 解析 ref 節點
else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean' or 'parent' is required for <ref> element", ele);
return null;
}
}
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
ref.setSource(extractSource(ele));
return ref;
}
// 解析 idref 節點
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
// 解析 value 節點
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
// 解析 null 節點
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
// It's a distinguished null value. Let's wrap it in a TypedStringValue
// object in order to preserve the source location.
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
// 解析 array 節點
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
// 解析 list 節點
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
// 解析 set 節點
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
// 解析 map 節點
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
// 解析 props 節點
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
// 未知屬性,拋出異常
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
從上面的程式碼來看,對構造函數中屬性元素的解析,步驟如下:
- 略過
description
或者meta
。 - 提取
constructor-arg
上的ref
和value
屬性,以便於根據規則驗證正確性。其規則為在constructor-arg
上不存在一下情況:- 同時存在
ref
和value
屬性。 - 存在
ref
或者value
屬性,並且又有子元素。
- 同時存在
parsePropertyElements
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
// 獲取所有子節點
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
// 解析 property 節點
parsePropertyElement((Element) node, bd);
}
}
}
// 這裡是解析 property 標籤,<property name="..." value="..."/>
public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 獲取 name 屬性
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
// name 為空,拋出異常
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
// 出現兩個 name 相同的拋出異常
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
// 解析屬性值,跟構造器解析一樣,查看上方程式碼
Object val = parsePropertyValue(ele, bd, propertyName);
// 用 name 和 val 封裝成 PropertyValue
PropertyValue pv = new PropertyValue(propertyName, val);
// 解析元數據,跟 beans 標籤內的 meta 一樣
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
// 添加到 BeanDefiniton 的 PropertyValues 屬性中
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
上面方法主要是遍歷 property
節點,然後解析屬性值封裝成 PropertyValue
添加到 BeanDefinition
的 PropertyValues
中。
注意:
property
節點類似於 POJO 中的set
方法,bean
中的屬性必需有set
方法否則會拋出異常。
parseQualifierElements
public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
// 獲取子節點
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
// 解析 qualifier 節點
parseQualifierElement((Element) node, bd);
}
}
}
public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
// 提取 type
String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
// type 為空拋出異常
if (!StringUtils.hasLength(typeName)) {
error("Tag 'qualifier' must have a 'type' attribute", ele);
return;
}
this.parseState.push(new QualifierEntry(typeName));
try {
AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
qualifier.setSource(extractSource(ele));
// 提取 value
String value = ele.getAttribute(VALUE_ATTRIBUTE);
// value 不為空,設置到 AutowireCandidateQualifier 的 attribute 中
if (StringUtils.hasLength(value)) {
qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
}
// 獲取子節點
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 如果是有 attribute 節點,進行解析,提取值放入到 AutowireCandidateQualifier 的MetadataAttribute 中
if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
Element attributeEle = (Element) node;
String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
attribute.setSource(extractSource(attributeEle));
qualifier.addMetadataAttribute(attribute);
}
else {
error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
return;
}
}
}
// 設置 BeanDefinition 的 qualifier
bd.addQualifier(qualifier);
}
finally {
this.parseState.pop();
}
}
對於 qualifier
元素的獲取,我們大多數接觸的更多是註解的形式,在使用 Spring 框架中進行自動注入時,Spring 容器中匹配的候選 Bean
必需有且只有一個。如果存在多個類型相同的 Bean
,且按照類型注入時,Spirng 允許通過 Qualifier
指定注入 Bean
的名稱,這樣歧義就消除了。
7-1-2.BeanDefinitionReaderUtils#registerBeanDefinition
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// 獲取 beanName
String beanName = definitionHolder.getBeanName();
// 以 key-value 的形式註冊,key 為 beanName,value 為 BeanDefinition。見下文詳解
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
// 註冊 bean 的別名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// 以 key-value 的形式註冊 bean 的別名,key 為別名,value 為 beanName。見下文詳解
registry.registerAlias(beanName, alias);
}
}
}
// DefaultListableBeanFactory.java
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 驗證 Bean 的格式是否正確
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 這裡判斷 BeanDefinition 是否存在
if (existingDefinition != null) {
// 這裡就是如果 Bean 定義以及存在,判斷是否可以覆蓋,默認是可以的
// Spring Boot 2.1開始這裡會手動設置 allowBeanDefinitionOverriding 的值為 false
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 將 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 快取中
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// bean 是否已經開始創建
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
// 將 beanName 和 BeanDefinition 以 key-value 形式放入beanDefinitionMap 快取中
this.beanDefinitionMap.put(beanName, beanDefinition);
// 這裡將 beanDefinitionNames 寫時複製一份,類似於 CopyOnWriteArrayList
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
// 從單例 Bean 註冊名稱列表中刪除當前 beanName
removeManualSingletonName(beanName);
}
}
// bean 不在創建狀態中
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
// 因為 ConcurrentHashMap 是無序的,這裡將 beanName 放入 ArrayList,記錄註冊順序
this.beanDefinitionNames.add(beanName);
// 從單例 Bean 註冊名稱列表中刪除當前 beanName
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 如果存在相同的 beanName 的 BeanDefinition,或者 beanName 已經存在單例對象,則將該 beanName 對應的快取資訊、單例對象清除,因為這些對象都是由老的 BeanDefinition 創建的,需要被覆蓋掉。再用新的 BeanDefinition 來創建這些快取和單例對象
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
// SimpleAliasRegistry.java
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
// 如果別名和 beanName 相同,從快取中移除
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
// 如果別名以及註冊過,直接返回
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
// 如果不允許覆蓋,拋出異常
if (!allowAliasOverriding()) {
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
// 檢查 name 和 alias 是否存在循環引用。例如 A 的別名為 B,B的別名為A
checkForAliasCircle(name, alias);
// 將 alias 和 name 以 key-value 對放入到 aliasMap 中,進行快取
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
上面程式碼有兩個變數比較重要 beanDefinitionMap
和 beanDefinitionNames
,下面程式碼是這兩個屬性在 DefaultListableBeanFactory
中的定義:
/** Map of bean definition objects, keyed by bean name. */
// 快取 BeanDefinition 的 Map,key 為 beanName,value 為 BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
// 保存 BeanDefinition 的註冊順序,保存的是 beanName
/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
-
如果
BeanDefinition
是AbstractBeanDefinition
類型,驗證Bean
的格式是否正確。這次效驗主要是對於
AbstractBeanDefinition
屬性中的methodOverrides
的校驗,校驗methodOverrides
是否與 工廠方法 並存或者methodOverrides
中的方法根本不存在。 -
判斷該
beanName
的BeanDefinition
是否已經註冊過;如果存在判斷是否允許覆蓋,允許的話直接替換,不允許直接拋出異常。默認的情況下是允許的,但是在 Spring Boot 2.1 開始這裡會手動的設置為不允許。
-
beanName
對應的BeanDefinition
以前沒有註冊過,判斷bean
是否已經開始創建;如果在創建中對beanDefinitionMap
進行加鎖 (這裡雖然beanDefinitionMap
是執行緒安全的ConcurrentHashMap
,單個操作是執行緒安全的但多個操作不是,所以這裡手動加鎖),然後將beanName
和BeanDefinition
以key-value
形式放入beanDefinitionMap
快取中,然後寫時複製一份beanDefinitionNames
,將beaName
快取進去,記錄bean
的註冊順序;如果不在創建直接將BeanDefinition
和beanName
分別放入beanDefinitionMap
和beanDefinitionNames
中。 -
最後判斷如果
BeanDefinition
已經註冊過,或者beanName
已經存在單例對象,則將該beanName
對應的快取資訊、單例對象清除,因為這些對象都是由老的BeanDefinition
創建的,需要被覆蓋掉。再用新的BeanDefinition
來創建這些快取和單例對象。
總結
本文主要介紹了通過 XML 文件的方式註冊 Bean
,我們可以重新梳理一下思路:
-
解析 XML 文件,構建成
AbstractBeanDefinition (GenericBeanDefinition)
對象來存放所有解析出來的屬性。 -
將
AbstractBeanDefinition
、beanName
、aliasesArray
構建成BeanDefinitionHolder
對象。 -
最後通過
BeanDefinitionHolder
將beanName
和BeanDefinition
註冊到DefaultListableBeanFactory
中,也就是保存起來。上文提到的兩個比較重要的屬性
beanDefinitionNames
和beanDefinitionMap
,在後面都會多次用到,可以重點關注一下。
最後,我模仿 Spring 寫了一個精簡版,程式碼會持續更新,現在是 0.0.1
版本。地址://github.com/leisurexi/tiny-spring。訪問新部落格地址,觀看效果更佳 //leisurexi.github.io/
參考
-
《Spring 源碼深度解析》—— 郝佳