Spring源码学习默认标签的解析

Posted VVII

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring源码学习默认标签的解析相关的知识,希望对你有一定的参考价值。

默认标签的解析
分为四种:import,alias,bean,beans,在下面函数中进行

技术图片
 1     private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
 2         if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
 3             importBeanDefinitionResource(ele);
 4         }
 5         else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
 6             processAliasRegistration(ele);
 7         }
 8         else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
 9             processBeanDefinition(ele, delegate);
10         }
11         else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
12             // recurse
13             doRegisterBeanDefinitions(ele);
14         }
15     }
parseDefaultElement

 

先看bean标签的解析与注册的大致过程

技术图片
//BeanDefinitionParserDelegate:定义解析Element的各种方法
    //BeanDefinitionHolder:Holder for a BeanDefinition with name and aliases.Can be registered as a placeholder for an inner bean.
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name ‘" +
                        bdHolder.getBeanName() + "‘", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
processBeanDefinition

(1)先调用BeanDefinitionParserDelegate.parseBeanDefinitionElement(),对xml中的各种属性进行解析,方法返回BeanDefinitionHolder类型的实例bdHolder

(2)bdHolder下有自定义标签则对自定义标签进行解析

(3)对bdHolder进行注册

 

元素解析大致过程

技术图片
 1 @Nullable
 2     public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
 3         //获取id
 4         String id = ele.getAttribute(ID_ATTRIBUTE);
 5         //获取name
 6         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
 7 
 8         List<String> aliases = new ArrayList<>();
 9         if (StringUtils.hasLength(nameAttr)) {
10             String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
11             aliases.addAll(Arrays.asList(nameArr));
12         }
13 
14         String beanName = id;
15         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
16             beanName = aliases.remove(0);
17             if (logger.isTraceEnabled()) {
18                 logger.trace("No XML ‘id‘ specified - using ‘" + beanName +
19                         "‘ as bean name and " + aliases + " as aliases");
20             }
21         }
22 
23         //
24         if (containingBean == null) {
25             checkNameUniqueness(beanName, aliases, ele);
26         }
27 
28         //解析beanDefinition标签
29         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
30         if (beanDefinition != null) {
31             if (!StringUtils.hasText(beanName)) {
32                 try {
33                     if (containingBean != null) {
34                         beanName = BeanDefinitionReaderUtils.generateBeanName(
35                                 beanDefinition, this.readerContext.getRegistry(), true);
36                     }
37                     else {
38                         beanName = this.readerContext.generateBeanName(beanDefinition);
39                         // Register an alias for the plain bean class name, if still possible,
40                         // if the generator returned the class name plus a suffix.
41                         // This is expected for Spring 1.2/2.0 backwards compatibility.
42                         String beanClassName = beanDefinition.getBeanClassName();
43                         if (beanClassName != null &&
44                                 beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
45                                 !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
46                             aliases.add(beanClassName);
47                         }
48                     }
49                     if (logger.isTraceEnabled()) {
50                         logger.trace("Neither XML ‘id‘ nor ‘name‘ specified - " +
51                                 "using generated bean name [" + beanName + "]");
52                     }
53                 }
54                 catch (Exception ex) {
55                     error(ex.getMessage(), ele);
56                     return null;
57                 }
58             }
59             String[] aliasesArray = StringUtils.toStringArray(aliases);
60             return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
61         }
62 
63         return null;
64     }
parseBeanDefinitionElement

(1)获取标签中的id,name属性,当name不为空情况下获取其aliases

(2)解析其他的属性封装在AbstractBeanDefinition中并返回实例beanDefinition

(3)当bean无id属性则按照默认规则为其生成,并校验是否添加至aliases

(4)将beanDefinition,beanName,获取的aliases的aliasesArray封装至BeanDefinitionHolder中

 

具体解析了哪些其他标签

技术图片
 1 @Nullable
 2     public AbstractBeanDefinition parseBeanDefinitionElement(
 3             Element ele, String beanName, @Nullable BeanDefinition containingBean) {
 4 
 5         this.parseState.push(new BeanEntry(beanName));
 6 
 7         String className = null;
 8         if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
 9             className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
10         }
11         String parent = null;
12         if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
13             parent = ele.getAttribute(PARENT_ATTRIBUTE);
14         }
15 
16         try {
17             //创建GenericBeanDefinition类型的实例,用于承载属性的实例
18             AbstractBeanDefinition bd = createBeanDefinition(className, parent);
19 
20             //解析bean默认的各种属性
21             parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
22             bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
23 
24             //解析元数据
25             parseMetaElements(ele, bd);
26             //解析lookup-method方法
27             parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
28             //解析replaced-method方法
29             parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
30 
31             //解析构造函数
32             parseConstructorArgElements(ele, bd);
33             //解析Property
34             parsePropertyElements(ele, bd);
35             //解析Qualifier
36             parseQualifierElements(ele, bd);
37 
38             bd.setResource(this.readerContext.getResource());
39             bd.setSource(extractSource(ele));
40 
41             return bd;
42         }
43         catch (ClassNotFoundException ex) {
44             error("Bean class [" + className + "] not found", ele, ex);
45         }
46         catch (NoClassDefFoundError err) {
47             error("Class that bean class [" + className + "] depends on not found", ele, err);
48         }
49         catch (Throwable ex) {
50             error("Unexpected failure during bean definition parsing", ele, ex);
51         }
52         finally {
53             this.parseState.pop();
54         }
55 
56         return null;
57     }
parseBeanDefinitionElement

 

bean的各种属性

技术图片
 1 /**
 2      * Apply the attributes of the given bean element to the given bean * definition.
 3      * @param ele bean declaration element
 4      * @param beanName bean name
 5      * @param containingBean containing bean definition
 6      * @return a bean definition initialized according to the bean element attributes
 7      */
 8     public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
 9             @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
10 
11         if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
12             error("Old 1.x ‘singleton‘ attribute in use - upgrade to ‘scope‘ declaration", ele);
13         }
14         else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
15             bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
16         }
17         else if (containingBean != null) {
18             // Take default from containing bean in case of an inner bean definition.
19             // 如果是内部bean定义,则从包含bean中获取默认值。
20             bd.setScope(containingBean.getScope());
21         }
22 
23         if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
24             bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
25         }
26 
27         String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
28         if (isDefaultValue(lazyInit)) {
29             lazyInit = this.defaults.getLazyInit();
30         }
31         bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
32 
33         String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
34         bd.setAutowireMode(getAutowireMode(autowire));
35 
36         if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
37             String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
38             bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
39         }
40 
41         String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
42         if (isDefaultValue(autowireCandidate)) {
43             String candidatePattern = this.defaults.getAutowireCandidates();
44             if (candidatePattern != null) {
45                 String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
46                 bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
47             }
48         }
49         else {
50             bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
51         }
52 
53         if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
54             bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
55         }
56 
57         if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
58             String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
59             bd.setInitMethodName(initMethodName);
60         }
61         else if (this.defaults.getInitMethod() != null) {
62             bd.setInitMethodName(this.defaults.getInitMethod());
63             bd.setEnforceInitMethod(false);
64         }
65 
66         if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
67             String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
68             bd.setDestroyMethodName(destroyMethodName);
69         }
70         else if (this.defaults.getDestroyMethod() != null) {
71             bd.setDestroyMethodName(this.defaults.getDestroyMethod());
72             bd.setEnforceDestroyMethod(false);
73         }
74 
75         if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
76             bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
77         }
78         if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
79             bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
80         }
81 
82         return bd;
83     }
parseBeanDefinitionAttributes

 

 具体看解析子元素meta标签

技术图片
 1 public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
 2         NodeList nl = ele.getChildNodes();
 3         for (int i = 0; i < nl.getLength(); i++) {
 4             Node node = nl.item(i);
 5             if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
 6                 Element metaElement = (Element) node;
 7                 String key = metaElement.getAttribute(KEY_ATTRIBUTE);
 8                 String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
 9                 BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
10                 attribute.setSource(extractSource(metaElement));
11                 attributeAccessor.addMetadataAttribute(attribute);
12             }
13         }
14     }
parseMetaElements
技术图片
1     <bean id="tiger" class="com.vi.springbean.Tiger">
2         <meta key="tigerKey" value="hello"/>
3     </bean>
eg:xml

 

具体看解析子元素lookup-method标签

技术图片
 1     public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
 2         NodeList nl = beanEle.getChildNodes();
 3         for (int i = 0; i < nl.getLength(); i++) {
 4             Node node = nl.item(i);
 5             if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
 6                 Element ele = (Element) node;
 7                 // <lookup-method name="" bean=""/>
 8                 String methodName = ele.getAttribute(NAME_ATTRIBUTE);
 9                 String beanRef = ele.getAttribute(BEAN_ELEMENT);
10                 LookupOverride override = new LookupOverride(methodName, beanRef);
11                 override.setSource(extractSource(ele));
12                 overrides.addOverride(override);
13             }
14         }
15     }
parseLookupOverrideSubElements

其实和meta标签解析大同小异

看一下lookup-method标签具体如何使用

技术图片
1 public class Animal {
2     public void sayHello(){
3         System.out.println("hello ··");
4     }
5 }
父类
技术图片
1 public class Tiger extends Animal {
2 
3     @Override
4     public void sayHello() {
5         System.out.println("hello  我是王者");
6     }
7 }
子类
技术图片
1 public class Mouse extends Animal{
2     @Override
3     public void sayHello() {
4         System.out.println("hello 我是生肖之首");
5     }
6 }
子类
技术图片
1 public abstract class LookUpTest {
2 
3     public abstract Animal getBean();
4 
5     public  void say(){
6         this.getBean().sayHello();
7     }
8 }
调用方法
技术图片
1 public class Test {
2     @org.junit.Test
3     public void test() {
4         String path = "spring-config.xml";
5         ApplicationContext bf = new ClassPathXmlApplicationContext(path);
6         LookUpTest bean = bf.getBean(LookUpTest.class);
7         bean.say();
8     }
9 }
测试方法

当xml配置如下:

技术图片
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5        http://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <bean id="lookUpTest" class="com.vi.springbean.LookUpTest">
 8         <lookup-method name="getBean" bean="tiger"></lookup-method>
 9     </bean>
10 
11     <bean id="mouse" class="com.vi.springbean.Mouse"></bean>
12     <bean id="tiger" class="com.vi.springbean.Tiger"></bean>
13 
14 </beans>
xml配置0

运行结果:

hello 我是动物之王

当xml配置如下

技术图片
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5        http://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <bean id="lookUpTest" class="com.vi.springbean.LookUpTest">
 8         <lookup-method name="getBean" bean="mouse"></lookup-method>
 9     </bean>
10 
11     <bean id="mouse" class="com.vi.springbean.Mouse"></bean>
12     <bean id="tiger" class="com.vi.springbean.Tiger"></bean>
13 
14 </beans>
xml配置1

运行结果:

hello 我是生肖之首

这个配置完成的功能就是通过更改xml中lookup-method的配置,bean对应的对象作为getBean的返回

这是一种特殊的注入方式,被称为获取器注入,他是把一个方法声明为返回某种类型的bean,但实际要返回的bean是在xml文件中配置的。

好处是看解决程序依赖

具体看解析子元素replaced-method标签 

技术图片
 1 /**
 2      * Parse replaced-method sub-elements of the given bean element.
 3      */
 4     public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
 5         NodeList nl = beanEle.getChildNodes();
 6         for (int i = 0; i < nl.getLength(); i++) {
 7             Node node = nl.item(i);
 8             if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
 9                 Element replacedMethodEle = (Element) node;
10                 // <replace-method name="" replace=""/>
11                 String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
12                 String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
13                 ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
14                 // Look for arg-type match elements.
15                 List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
16                 for (Element argTypeEle : argTypeEles) {
17                     String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
18                     match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
19                     if (StringUtils.hasText(match)) {
20                         replaceOverride.addTypeIdentifier(match);
21                     }
22                 }
23                 replaceOverride.setSource(extractSource(replacedMethodEle));
24                 overrides.addOverride(replaceOverride);
25             }
26         }
27     }
parseReplacedMethodSubElements

看一下replaced-method标签具体如何使用

技术图片
1 public class Replacer {
2     public void changeMe(){
3         System.out.println("hello please change me");
4     }
5 }
待替换方法
技术图片
1 public class ReplaceTest implements MethodReplacer {
2     @Override
3     public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
4         System.out.println("每个人都将成为王者");
5         return null;
6     }
7 }
替换的方法实现
技术图片
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans
 5        http://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <bean id="replacer" class="com.vi.springbean.Replacer">
 8         <replaced-method name="changeMe" replacer="replaceTest"></replaced-method>
 9     </bean>
10 
11     <bean id="replaceTest" class="com.vi.springbean.ReplaceTest"></bean>
12 
13 </beans>
xml配置
技术图片
1 public class Test {
2     @org.junit.Test
3     public void test() {
4         String path = "spring-config.xml";
5         ApplicationContext bf = new ClassPathXmlApplicationContext(path);
6         Replacer bean = bf.getBean(Replacer.class);
7         bean.changeMe();
8     }
9 }
Test类

运行结果:

每个人都将成为王者

 

解析子元素constructor-arg

TODO

解析子元素property

TODO

 

注:
GenericBeanDefinition,ChildBeanDefinition,RootBeanDefinition 都继承AbstractBeanDefinition
AbstractBeanDefinition 继承 BeanMetadataAttributeAccessor 实现 BeanDefinition

------------------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------------------------

 解析默认标签中的自定义标签元素

 TODO

 

了解alias标签的解析

TODO

了解import标签的解析

TODO

 

了解beans标签的解析

递归调用beans标签的解析过程

 



已经是2020年的第二天了 时间过的很快 或许只有我一人觉得19年自己碌碌无为 20年加油吧

 

以上是关于Spring源码学习默认标签的解析的主要内容,如果未能解决你的问题,请参考以下文章

Spring源码深度解析学习系列默认标签解析

Spring源码学习默认标签的解析

5Spring 源码学习 ~ 默认标签 alias 标签的解析与注册

6Spring 源码学习 ~ 默认标签的解析之 import 标签的解析

7Spring 源码学习 ~ 默认标签的解析之嵌入式 beans 标签的解析

7Spring 源码学习 ~ 默认标签的解析之嵌入式 beans 标签的解析