文章目錄
書接上文挖的坑,我們已經知道Spring讀取配置文件後,會將我們的配置文件裏的每一個標籤轉換成對應的Element。Spring將配置文件中的標籤分爲默認標籤和自定義標籤,默認標籤的解析是在parseDefaultElement方法中進行的,這個方法的功能邏輯一目瞭然,分別對4 種不同標籤(import、alias、bean、 beans) 做了不同的處理。
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
使用工具類的nodeNameEquals方法,來判斷ele是屬於什麼標籤。
1. bean標籤的解析及註冊
bean標籤的解析最爲重要,所以咱們先分析bean標籤的解析。看一下方法processBeanDefinition(ele, delegate)的源碼。
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//1.將ele轉換成BeanDefinitionHolder bdHolder
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//2.對子節點的自定義屬性進行解析
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//3.根據BeanDefinitionHolder進行註冊bean
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 4、將bean註冊完成的消息通知監聽器
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
這個方法名起的挺有意思,字面意思,定義bean的過程。Spring一般用process開頭的方法,都是一大塊邏輯的入口方法,這種命名規則可以借鑑一下。即使再多的邏輯,Spring的作者也會分步驟,逐步進行。
- 使用工具類BeanDefinitionParserDelegate的parseBeanDefinitionElement的方法進行元素解析,返回BeanDefinitionHolder類型的實例bdHolder。bdHolder就是一個包含beanDefinition、beanName的持有者,它有beanName、beanDefinition、aliases屬性。這個方法就是真正的解析Element,此時bdHolder的beanDefinition就會包含我們配置文件中配置的各種屬性了,例如class、name、id、alias之類的屬性。
- 當返回的bdHolder 不爲空的情況下,若存在默認標籤的子節點下再有自定義屬性, 還需要再次對自定義標籤進行解析。
- 根據BeanDefinitionHolder進行註冊bean,註冊的功能由BeanDefinitionReaderUtils的
registerBeanDefinition方法完成。 - 最後發出響應事件,通知相關的監昕器,這個bean 已經加載完成了。
1.1 解析Element獲取BeanDefinition
進入BeanDefinitionDelegate類的parseBeanDefinitionElement 方法,Spring通過這個方法將Element裏面的內容解析至BeanDefinitionHolder裏面。
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element)
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
// 直接調用下面的方法
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);
//分割name屬性
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// beanName的默認值是id
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
// beanName爲一串空格,則beanName爲別名的第一個
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) {
checkNameUniqueness(beanName, aliases, ele);
}
// 解析bean元素,併產生一個beanDefinition!!!
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// 如果不存在beanName那麼根據spring中提供的命名規則爲當前bean生成對應的beanName
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.isDebugEnabled()) {
logger.debug("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);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
這個方法主要完成的方法如下:
- 提取元素中的id及name屬性。
- 使用重載的parseBeanDefinitionElement方法,進一步解析其他所有屬性並統一封裝至GenericBeanDefinition 類型的實例中。
- 如果檢測到bean沒有指定beanName ,那麼使用默認規則爲此bean 生成beanName。
- 將上面獲得的beanDefinition、beanName、別名數組,封裝至BeanDefinitionHolder。
本方法最重要的目的有兩個,第一,確定全局唯一的beanName。beanName的默認值是id屬性的值;如果id爲null,則默認是name屬性的第一個名字,name屬性可以用,;分隔符進行配置多個;最後,如果沒有指定beanName ,那麼Spring將會按照自己的規則生成beanName。第二,調用重載方法,得到GenericBeanDefinition。下面我們看看重載的parseBeanDefinitionElement。
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//創建用於承載屬性的AbstractBeanDefinition類型的GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//硬編碼解析默認bean的各種屬性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//提取description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析子元素meta
parseMetaElements(ele, bd);
//解析lookup-method屬性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析replaced-method屬性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析構造函數參數
parseConstructorArgElements(ele, bd);
//解析property子元素
parsePropertyElements(ele, bd);
//解析qualifier子元素
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;
}
- 創建用於承載屬性的AbstractBeanDefinition類型的GenericBeanDefinition。
BeanDefinition是一個接口,是配置文件<bean>元素標籤在容器中的內部表示形式。在Spring 中存在三種常見的實現: RootBeanDefinition、ChildBeanDefinition
以及GenericBeanDefinition三種實現均繼承了AbstractBeanDefiniton。<bean>元素標籤擁有class 、scope 、lazy-init 等配置屬性, BeanDefinition 則提供了相應的beanClass、scope、lazyInit屬性,BeanDefinition 和<bean>中的屬性是一一對應的。其中
RootBeanDefinition 是最常用的實現類,它對應一般性的<bean>元素標籤。GenericBeanDefinition 是自2.5 版本以後新加入的bean文件配置屬性定義類,是一站式服務類。
在配置文件中可以定義父<bean >和子<bean>,父<bean>用RootBeanDefinition 表示,而子<bean>用ChildBeanDefiniton 表示,而沒有父<bean>的<bean>就使用RootBeanDefinition 表示。AbstractBeanDefinition 對兩者共同的類信息進行抽象。
Spring 通過BeanDefinition 將配置文件中的<bean>配置信息轉換爲容器的內部表示,並將這些BeanDefiniton 註冊到BeanDefinitonRegistry 中。BeanDefinitionRegistrγ 就像是Spring 配置信息的內存數據庫,主要是以map 的形式保存,後續操作直接從BeanDefinitionRegistry中讀取配置信息。
由此可知,要解析屬性首先要創建用於承載屬性的實例,也就是創建GenericBeanDefinition類型的實例。而代碼createBeanDefinition(className, parent)的作用就是實現此功能。
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#createBeanDefinition
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
throws ClassNotFoundException {
return BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
}
org.springframework.beans.factory.support.BeanDefinitionReaderUtils#createBeanDefinition
public static AbstractBeanDefinition createBeanDefinition(
@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
當我們創建了用於承載bean標籤各種屬性的對象後,便可以進行bean標籤的各種屬性解析了。
2. parseBeanDefinitionAttributes 方法是對element 所有元素屬性進行解析,源碼如下。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
//解析scope屬性
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
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.
//在嵌入beanDefinition情況下且沒有單獨指定scope屬性則使用父類默認的屬性
bd.setScope(containingBean.getScope());
}
//解析abstract屬性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
//解析lazy-init屬性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
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-method"屬性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
}
//解析destroy-method屬性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
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));
}
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
}
return bd;
}
上面就是Spring把bean標籤的屬性值,set到BeanDefinition中。這些屬性有我們常用的scope、lazy-init、init-method等等。
Spring在上面設置完bean標籤的屬性後,Spring還有子元素meta、lookup-method、replaced-method、constructor-arg、property、qualifier需要解析,這裏就不在分析了,我們只需要知道,這些子標籤定義的功能是需要Spring這些底層的代碼支撐的。至此我們便完成了對XML文檔到GenericBeanDefinition的轉換,也就是說,XML中所有的配置都可以在GenericBeanDefinition的實例類中找到對應的配置。
1.2 解析bean標籤下的自定義標籤
我們回到源碼剛開始的地方
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition方法,1.1節一直在分析
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);這一行代碼,現在我們開始第二行:bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder)。
這一行的作用是解析bean標籤下面聲明自定義標籤,將其封裝到BeanDefinition中。這種用法我們在項目中一般不會這樣使用,目前我是沒有遇到過,咱們就忽略吧,感興趣的同學可以去分析一下。
1.3 註冊BeanDefiniton
現在終於得到了我們最終版的BeanDefinition,剩下就是註冊了。註冊的代碼在咱們的
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());完成。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 使用beanName做唯一標識進行註冊
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 遍歷別名進行註冊
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
解析的beanDefinition都會被註冊到BeanDefinitionRegistry 類型的實例registry中,而對於beanDefinition 的註冊分成了兩部分:通過beanName的註冊以及通過別名的註冊。
1.3.1 通過beanName註冊BeanDefinition
Spring通過beanName註冊BeanDefinition,就是以beanName爲key,beanDefinition對象爲value,這個鍵值對存入到map裏面,另外Spring還做了一些其他的事情。
org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition
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 {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + existingDefinition + "] bound.");
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isWarnEnabled()) {
logger.warn("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.isInfoEnabled()) {
logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// 因爲beanDefinitionMap是全局變量,這裏定會存在併發訪問的情況
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 {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
- 對AbstractBeanDefinition的校驗,此時的校驗時是對於AbstractBeanDefinition的methodOverrides 屬性的。
- 對beanName已經註冊的情況的處理。如果設置了不允許bean的覆蓋,則需要拋出異常,否則直接覆蓋。
- 加入map 緩存。
- 清除解析之前留下的對應beanName的緩存。
1.3.2 通過別名註冊BeanDefinition
org.springframework.core.SimpleAliasRegistry#registerAlias
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) {
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.isInfoEnabled()) {
logger.info("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
// 校驗會不出產生:別名A1--->A,A--->A1情況,如過出現此情況則,拋出異常,不允許保存別名
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
- alias與beanName相同情況處理。若alias與beanName 名稱相同則不需要處理並刪除掉原有alias。
- alias覆蓋處理。若aliasName已經使用並已經指向了另一beanName 則需要用戶的設置
進行處理。 - alias 循環檢查。// 校驗會不出產生:別名A1—>A,A—>A1情況,如過出現此情況則,拋出異常,不允許保存別名
- 註冊alias 。
1.4 通知監聽器解析註冊完成的事件
最後一行代碼:
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
Spring運用設計模式中的觀察者模式,將我們重要的事件:bean標籤解析完成生成BeanDefinition,且註冊BeanDefinition完成事件通知給各個觀察者。目前在Spring 中並沒有對此事件做任何邏輯處理。
現在我們的Bean Definition已經被註冊到Map類型的beanDefinitionMap中,剩下的操作,就是根據這map中的BeanDefinition進行創建bean。這個我們以後再開一坑。