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 }
先看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)); } }
(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 }
(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 }
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 }
具体看解析子元素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 }
1 <bean id="tiger" class="com.vi.springbean.Tiger"> 2 <meta key="tigerKey" value="hello"/> 3 </bean>
具体看解析子元素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 }
其实和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>
运行结果:
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>
运行结果:
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 }
看一下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>
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 }
运行结果:
每个人都将成为王者
解析子元素constructor-arg
TODO
解析子元素property
TODO
注:
GenericBeanDefinition,ChildBeanDefinition,RootBeanDefinition 都继承AbstractBeanDefinition
AbstractBeanDefinition 继承 BeanMetadataAttributeAccessor 实现 BeanDefinition
------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------
解析默认标签中的自定义标签元素
TODO
了解alias标签的解析
TODO
了解import标签的解析
TODO
了解beans标签的解析
递归调用beans标签的解析过程
已经是2020年的第二天了 时间过的很快 或许只有我一人觉得19年自己碌碌无为 20年加油吧
以上是关于Spring源码学习默认标签的解析的主要内容,如果未能解决你的问题,请参考以下文章
5Spring 源码学习 ~ 默认标签 alias 标签的解析与注册
6Spring 源码学习 ~ 默认标签的解析之 import 标签的解析