上一篇文章我們講到了
BeanFactory
的創建和BeanDefinition
的創建。本文接着上一篇文章,來看看一個<bean>
標籤是如何被解析成BeanDefinition
對象並註冊到BeanFactory
裏面
爲了方便講解,在開始
<bean>
標籤解析前我們稍微對測試代碼做一點小調整,在配置文件中新增兩個<property>
屬性,對應的userService
相應的增加兩個對應的屬性字段。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
>
<bean id="userService" class="UserServiceImpl">
// userService增加兩個屬性
<property name="name" value="z小趙"/>
<property name="age" value="27"/>
</bean>
</beans>
// UserServiceImpl增加兩個字段,並聲明瞭對應的set方法
public class UserServiceImpl implements UserService {
private String name;
private int age;
@Override
public void save(String name, int age) {
System.out.println("name:" + name + ",age:" + age);
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
遍歷 Document 的所有子節點,針對不同的標籤執行不同的解析邏輯,我們側重來看下平時最常用的
<bean>
標籤是怎麼被解析的。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// Step1:如果 “import” 標籤
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// Step2:如果是 “alias” 標籤
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// Step3:如果是 “bean” 標籤
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// Step4:如果是 “beans” 標籤
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
看看
processBeanDefinition
是怎麼把<bean>
標籤解析成 BeanDefinition 並註冊到 BeanFactory 裏,重點在第一步解析<bean>
標籤和第三步註冊BeanDefinition
。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// Step1:創建一個BeanDefinitionHolder,從名字看就是創建了一個BeanDefinition對象持有者
// 它包含了創建出來的BeanDefinition定義,還有beanName等等屬性
// 最後會從這個對象裏面獲取beanName和BeanDefinition定義,並將其註冊到BeanFactory裏面
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// Step2:對創建出來的BeanDefinitionHolder做一些裝飾操作,如果有必要的話.
// 比如 “autowire" "id" "lazy-init"等等node做一些額外的裝飾操作
// 還有,如果當前element還有子節點並且子節點也是element,則對子節點進行裝飾操作
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// Step3:註冊BeanDefinition到BeanFactory裏面
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// Step4:發送一個註冊完成事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
接着上面先來看第一步
<bean>
標籤是怎麼被解析成BeanDefinition
對象的。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// Step1:獲取當前bean標籤的id和name(這個name其實就是我們在xml文件裏面指定的別名)屬性
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// Step2:解析別名,我們沒有配置,所以不會走這裏
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// Step3:默認使用id作爲beanName,beanName就是我們在代碼裏面指定要獲取那個,Bean實例的名字
String beanName = id;
// 這裏是如果沒有指定id && 指定了name屬性,則使用別名作爲beanName
// 所以從這裏也可以看出,如果同時指定id和name,beanName還是會優先用id指定的名字
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// Step4:檢查beanName和alias是否是唯一的。
// 如果發現在之前已經初始化了一個相同名字的bean定義,則拋出異常
// 反之,將當前的beanName添加到BeanDefinitionParseDelegate裏面
checkNameUniqueness(beanName, aliases, ele);
}
// Step5:根據element構建出BeanDefinition對象,前面一直說的BeanDefinition對象就是在這個方法裏面創建出來的
// 後面會對這個方法展開具體講解
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// Step6:如果創建BeanDefinition對象的時候報錯有可能會返回null
if (beanDefinition != null) {
// Step6.1:如果當前beanName爲空,
if (!StringUtils.hasText(beanName)) {
try {
// Step6.1.1:containingBean不爲null,則從containingBean定義生成一個beanName, 一般這裏走不到,因爲我們在xml裏面都會指定id或者name屬性
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
// Step6.1.2:否則利用BeanDefinition定義生成一個beanName
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.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
// Step6.2:將創建出來的BeanDefinition定義、beanName、alias等封裝成一個BeanDefinitionHolder返回
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
在進一步深入看看根據 element 是如何創建出
BeanDefinition
對象的,即parseBeanDefinitionElement
方法的具體實現邏輯是怎樣的。
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// Step1:首先獲取class屬性值,如果存在的話
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// Step2:獲取parent屬性,如果指定了的話,我們在xml裏面定義的時候可以指定parent屬性。感興趣的可以試試
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// Step3:根據當前element對應的className和其對應的父name,創建一個GenericBeanDefintion對象,
// 因爲GenricBeanDefintion繼承了AbstractBeanDefinition,
// 所以返回了AbstractBeanDefinition對象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// Step4:有了BeanDefinition定義後,開始解析一些其他屬性
// 比如是否指定了 init-method方法、destory-method方法、depends-on等等屬性
// 如果指定了,則把指定的相關信息設置到BeanDefinition定義上
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// Step5:設置描述
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// Step6:解析元數據
parseMetaElements(ele, bd);
// Step7:解析 override 的方法
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// Step8:解析 replaced-method 的方法
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// Step9:解析構造器的參數,及 constructor-arg標籤
parseConstructorArgElements(ele, bd);
// Step10:解析property屬性,其實就是解析我們的測試例子指定了下面的這個屬性的,其他的解析類似。感興趣的可以自己研究
// <property name="name" value="z小趙">
// <property name="age" value="27">
// 將解析後的結果構建成一個個PropertyValue對象,
// 然後將該對象設置到BeanDefinition的property字段上
parsePropertyElements(ele, bd);
// Step11:解析qualifier屬性
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
// Step12:經過上面一系列的填充之後獲得BeanDefinition對象,然後將其返回
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;
}
到這裏我們創建出了
BeanDefinition
對象,然後接着第 3 步,對創建出來的BeanDefinition
對象在做一些裝飾操作,如果有必要的話。具體在這裏就不深究了,感興趣的同學自行研究一下。我們重點來看下對經過一系列操作後得到最終的BeanDefinition
定義是怎麼被註冊到BeanFactory
裏面的。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
// Step1:獲取當前BeanDefinition對應的beanName,
// 然後使用registry將BeanDefinition註冊到BeanFactory裏面
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
// Step2:如果有別名的,同時也將別名註冊到BeanFactory裏面
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
那我們重點看下如何通過
registry
將BeanDefinition
定義註冊到BeanFactory
裏面,因爲我們最開始的時候創建了BeanFactory
是DefaultListableBeanFactory
,所以這裏調用了DefaultListableBeanFactory
的registryBeanDefinition
方法進行註冊的。
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");
// Step1:當前BeanDefinition是否實現或繼承了AbstractBeanDefinition對象
// 從我們上面分析代碼可以看出是繼承了AbstractBeanDefinition類的
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// Step1.1:對將要註冊的BeanDefinition定義進行校驗操作。
// 其實主要校驗的就是提前指定了classLoader的那種bean
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
// Step2:根據beanName獲取舊的BeanDefinition,如果已經存在了並且不允許被覆蓋的話,就拋出異常。正常情況下這個oldBeanDefinition是爲null的
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
// Step3:檢查一下bean的創建工作是否已經開始,這裏大白話解釋一下
// 從else的註釋可以看出,這裏其實想表達的意思應該是BeanFactory創建BeanDefinition定義的階段已經完成了
// 但是此時還想再註冊BeanDefinition定義,則走if條件。如有理解不對請指正
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Step4:正常註冊流程走的else,因爲當前階段正是在啓動BeanFactory和註冊BeanDefinition的時候
// 從代碼可以看出註冊其實就是將創建出來的BeanDefinition放到BeanFactory的map裏面,就這麼簡單而已
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
總結
到此,我們關於BeanFactory
創建,BeanDefinition
定義創建及註冊工作就全部做完了,關於 <import>
、<benas>
標籤的解析處理工作,大致流程都是差不多的,就不在一一展開了,有興趣的同學可以自行閱讀以下,只要搞懂了<bean>
的構建流程,相信關於其他標籤的構建也就比較容易了。
接下來兩篇文章我們接着講和我們常見的<bean>
標籤解析不同的另外一種自定義標籤,比如<cotext:component-scan>
標籤的實現原理;講完非默認標籤解析後,講講如果想實現一個自定義標籤,那該怎麼做呢?相信大家見過各種各樣的自定義標籤名,比如如果你用過motan
的話,會遇到過類似如下的這種標籤。如果想知道這種自定義標籤是怎麼被解析的,那就跟着我繼續深入學習吧。
<motan:referer id="userService" interface="com.xxx.UserService">
歡迎關注我,一起學習