前言
在了解了Spring容器加载资源的一个过程后,其对资源加载的最后一步则是生成对应的BeanDefinition ,在本篇文章,我们着重讲Spring是如何对配置文件的标签进行解析的流程,parseDefaultElement 和parseCustomElement 方法。
先从parseDefaultElement 开始:
一. 默认标签的解析
默认标签的解析,会分别对4种不同的标签做处理:
import :importBeanDefinitionResource() alias :processAliasRegistration() bean :processBeanDefinition() beans :doRegisterBeanDefinitions()
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
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)) {
doRegisterBeanDefinitions(ele);
}
}
}
还记得我们上篇文章中讲的小案例吗?里面用的就是bean 标签,这也是最基本最常用的一个标签,因此我们着重来讲它。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
.....省略">
<bean name="user" class="org.springframework.beans.User">
<property name="id" value="1" />
<property name="name" value="你好" />
</bean>
</beans>
1.1 bean标签的解析和注册
让我们来深入processBeanDefinition 方法:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
}
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
1.1.1 解析BeanDefinition
这一小节,我们主要关注这行代码BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
public class BeanDefinitionParserDelegate {
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
}
简而言之就是:
- 解析
id 和name 属性,并对name 属性进行分割。 - 解析其他属性。若
beanName 没有,则自动生成个。 - 将上述获取到结果的再进行整合,得到最终的
BeanDefinitionHolder 实例对象。
那我们再来细看下第二步的 “解析其他属性” 的过程: AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
形式上就像根据不同的Key ,来获取不同的Value 而已value = getAttribute(key)
@Nullable
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 bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch {
}
finally {
this.parseState.pop();
}
return null;
}
这里的解析其他属性,也就是解析class 、description 、property 等属性。我们先来看下,用于承载这些属性的GenericBeanDefinition 对象:
(1) GenericBeanDefinition对象
BeanDefinition 是一个接口,他是配置文件<bean> 元素标签在容器内部的一个表现形式。 例如:<bean> 标签拥有class 、scope 、lazy-init 等配置属性。而BeanDefinition 则提供了对应的beanClass 、scope 、lazyInit 操作方法。其为一一对应的一个关系。
Spring中,BeanDefinition 的实现有这3个常见的,它们都继承了AbstractBeanDefinition
RootBeanDefinition :最常用的实现类,对应一般性的<bean> 元素标签。ChildBeanDefinition :用于标识子<bean> 标签。GenericBeanDefinition :一种一站式服务类
Spring容器一般在读取加载完配置资源文件后,将其转为BeanDefinition 类型实例的内部表示,而这些实例一般会注册到BeanDefinitionRegistry 中。其主要以map 形式来保存,后续操作都是从BeanDefinitionRegistry 中读取配置信息的。
(2) 解析各种属性
我们来看下parseBeanDefinitionAttributes() 这个方法:
建议:如果想知道里面解析了哪些具体的属性,读者可以展开代码瞅瞅,否则,这段代码其实没什么好看的。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
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) {
bd.setScope(containingBean.getScope());
}
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
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));
}
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
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);
}
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);
}
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;
}
(3) lookup-method标签的作用
案例如下:
项目结构 User 类:
public class User {
void getName(){
System.out.println("I am User");
}
}
Student 类:
public class Student extends User{
void getName(){
System.out.println("I am Student");
}
}
GetNameTest 类:
public abstract class GetNameTest {
public abstract User getBean();
public void getName() {
this.getBean().getName();
}
}
test.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="getNameTest" class="org.springframework.beans.GetNameTest">
<lookup-method name="getBean" bean="student"/>
</bean>
<bean id="student" class="org.springframework.beans.Student"/>
</beans>
Test 类:
public class Test {
public static void main(String[] args) {
ClassPathResource resource = new ClassPathResource("test.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
GetNameTest getNameTest = (GetNameTest) factory.getBean("getNameTest");
getNameTest.getName();
}
}
结果如下: 原理分析:
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
代码展开如下:
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
String beanRef = ele.getAttribute(BEAN_ELEMENT);
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
也就是Spring会读取特定的<lookup-method> 标签,并根据配置的bean 的具体的引用来执行对应的方法。代码中,getBean() 并没有具体的实现,但是我们通过XML 配置,将该抽象方法声明为返回了某种类型的bean ,在本案例中就是返回了Student 对象实例。
再把两者代码结合在一起来看看: 所以,当我们在业务中,不再需要Student 类的相关逻辑了,需要Teacher 类的getName() 方法,我们只需要换一个引用即可,该标签常用于插拔功能。
(4) replaced-method标签的作用
案例如下: GetNameTest 类:
public class GetNameTest {
public void getName() {
System.out.println("Hello");
}
}
Change 类:需要实现MethodReplacer 接口
import org.springframework.beans.factory.support.MethodReplacer;
import java.lang.reflect.Method;
public class Change implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("Method Change!");
return null;
}
}
test.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="getNameTest" class="org.springframework.beans.GetNameTest">
<replaced-method name="getName" replacer="change"/>
</bean>
<bean id="change" class="org.springframework.beans.Change"/>
</beans>
Test 类:
public class Test {
public static void main(String[] args) {
ClassPathResource resource = new ClassPathResource("test.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
GetNameTest getNameTest = (GetNameTest) factory.getBean("getNameTest");
getNameTest.getName();
}
}
结果如下: 可以看出来,<replaced-method> 标签允许我们在运行时用新方法代替现有的方法。
那么上述两种标签有什么区别呢?
<replaced-method> :一般用于替代现有逻辑。<lookup-method> :一般用于执行其他方案,需要创建其他Bean 。
源码分析:(建议结合XML 配置来看)
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
Element replacedMethodEle = (Element) node;
String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
overrides.addOverride(replaceOverride);
}
}
}
注意:
- 两种标签,最后都是创建了
MethodOverride 实例:LookupOverride 与ReplaceOverride 。用于记录相关参数。而其最终都是记录在AbstractBeanDefinition 类中的methodOverrides 属性中。
(5) constructor-arg标签的作用
首先,我们先来看下这个标签有什么作用。案例如下: User 类:
public class User {
private int id;
private String name;
private Map<String, String> map;
public User(int id, String name, Map<String, String> map) {
this.id = id;
this.name = name;
this.map = map;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Test 方法:
@org.junit.jupiter.api.Test
public void test() {
BeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);
ClassPathResource resource = new ClassPathResource("user.xml");
reader.loadBeanDefinitions(resource);
User user = (User) factory.getBean("user");
System.out.println(user.getId());
System.out.println(user.getName());
System.out.println(user.getMap().get("key"));
}
user.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean name="user" class="org.springframework.beans.User">
<constructor-arg index="0">
<value>1</value>
</constructor-arg>
<constructor-arg index="1">
<value>名字</value>
</constructor-arg>
<constructor-arg index="2">
<map>
<entry key="key" value="value"/>
</map>
</constructor-arg>
</bean>
</beans>
结果如下: 可以看到,该标签在功能上,实现了初始化Bean 的时候,将设置的参数传了进去。那么我们再来看下Spring默认解析标签时对该标签解析的调用起点位置:
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
↓↓↓↓↓↓
public void parseConstructorArgElements(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, CONSTRUCTOR_ARG_ELEMENT)) {
parseConstructorArgElement((Element) node, bd);
}
}
}
↓↓↓↓↓↓parseConstructorArgElement↓↓↓↓↓↓
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
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 valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
error("Ambiguous constructor-arg entries for index " + index, ele);
}
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 {
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));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
上述流程当中,分为两种情况:
- 情况1:配置中指定了
index 属性: - 情况2:配置中没有指定
index 属性。
两种情况的前几步骤是一样的:
- 解析
Constructor-arg 的子元素。 - 使用
valueHolder 实例去封装解析出来的元素。
唯一不同的就是:
- 情况1:将
valueHolder 实例封装于BeanDefinition 下的indexedArgumentValues 属性中。 - 情况2:将
valueHolder 实例封装于BeanDefinition 下的genericArgumentValues 属性中。
在此之前,先来讲一下解析构造子元素的方法原理parsePropertyValue :
Object value = parsePropertyValue(ele, bd, null);
↓↓↓↓↓↓
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
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;
}
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 {
error(elementName + " must specify a ref or value", ele);
return null;
}
}
再来看下Spring对Constructor-arg 的子元素是如何解析的:
parsePropertySubElement(subElement, bd);
↓↓↓↓↓↓
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
↓↓↓↓↓↓
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
}
else if (nodeNameEquals(ele, REF_ELEMENT)) {
}
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
到这里,我们可以了解Spring会对可支持的子类进行分类处理,而具体的处理就不再一一展开细说了。
(6) 解析property子元素
老样子,先来看下它有什么作用:
@org.junit.jupiter.api.Test
public void test() {
BeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);
ClassPathResource resource = new ClassPathResource("user.xml");
reader.loadBeanDefinitions(resource);
User user = (User) factory.getBean("user");
System.out.println(user.getId());
}
user.xml :
<bean name="user" class="org.springframework.beans.User">
<property name="id" value="1"/>
</bean>
结果如下: 看来,其也就是用于给类的字段赋值的,我们来看下其源码。
parsePropertyElements(ele, bd);
↓↓↓↓↓↓
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)) {
parsePropertyElement((Element) node, bd);
}
}
}
↓↓↓↓↓↓
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
Object val = parsePropertyValue(ele, bd, propertyName); 这行代码,我们在第5小节,解析constructor-arg 标签的时候就已经讲过了,无非就是解析ref 、value 以及子元素(set 、map 等标签)的过程,这里也就不做展开。
最终解析好的属性则会记录在BeanDefinition 中的propertyValues 属性中。
(7) qualifier子元素
Spring框架中进行自动装配时,Spring容器中匹配的候选Bean 的数量有且只有一个。 Spring允许我们通过Qualifier 指定注入Bean 的名称,这样可以用于消除歧义。 若找不到一个匹配的bean 时,Spring容器将抛出BeanCreationnException 异常。
贴出注解形式的伪代码:
public interface User {
void log();
}
@Service("teacher")
public class Teacher implements User {
@Override
public void log() {
System.out.println("i am teacher");
}
}
@Service("student")
public class Student implements User {
@Override
public void log() {
System.out.println("i am student");
}
}
public class Test {
@Autowired
@Qualifier("student")
private User user;
@RequestMapping("/test")
public void test() {
user.log();
}
}
1.1.2 AbstractBeanDefinition的属性
1.1.1节中,对标签的解析和属性的承载,也就是完成了XML 文档到GenericBeanDefinition 的转换。其中XML 的所有配置都可以在GenericBeanDefinition 的实例类中找到对应的位置。大部分的通用属性则保存在AbstractBeanDefinition 中。
我们来看下这个类中的属性:
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
implements BeanDefinition, Cloneable {
private volatile Object beanClass;
private String scope = SCOPE_DEFAULT;
private boolean abstractFlag = false;
private Boolean lazyInit;
private int autowireMode = AUTOWIRE_NO;
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
private String[] dependsOn;
private boolean autowireCandidate = true;
private boolean primary = false;
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
private Supplier<?> instanceSupplier;
private boolean nonPublicAccessAllowed = true;
private boolean lenientConstructorResolution = true;
private String factoryBeanName;
private String factoryMethodName;
private ConstructorArgumentValues constructorArgumentValues;
private MutablePropertyValues propertyValues;
private MethodOverrides methodOverrides = new MethodOverrides();
private String initMethodName;
private String destroyMethodName;
private boolean enforceInitMethod = true;
private boolean enforceDestroyMethod = true;
private boolean synthetic = false;
private int role = BeanDefinition.ROLE_APPLICATION;
private String description;
private Resource resource;
}
1.1.3 解析默认标签中的自定义标签
再从头看起:默认标签的解析processBeanDefinition 方法:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
}
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
上文已经把第一步中,对配置文件的一个基础解析和提取已经讲完了。至此我们继续第二步,解析默认标签下的自定义属性bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 什么叫默认标签下的自定义标签呢?上代码:
<bean id="teacher" class="org.springframework.beans.Teacher">
<mybean:user username="aaa"/>
</bean>
注意,这里的自定义标签对应的是bean 的一个属性。
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
↓↓↓↓↓↓
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
↓↓↓↓↓↓
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
BeanDefinitionHolder finalDefinition = originalDef;
NamedNodeMap attributes = ele.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node node = attributes.item(i);
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
NodeList children = ele.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node node = children.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
}
}
return finalDefinition
}
从代码中我们可以看出来,无论是哪一个代码块,最终都执行了decorateIfRequired 方法:
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(node);
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
}
return originalDef;
}
- 获取属性或元素的命名空间。
- 判断该元素是否适用于自定义标签解析的条件。
- 找到自定义类型对应的
handler 进行进一步解析(第二章节具体展开)。
1.1.3 注册解析的BeanDefinition
对于配置文件,Spring容器负责加载解析,将其转化为Document 实例。而上述流程则是将配置解析完了。相关的装饰功能也做好了,那么此时BeanDefinition 已经满足使用的要求了,剩下的工作即是注册了。
代码入口如下:
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
我们来深入了解:
public abstract class BeanDefinitionReaderUtils {
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
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 的注册分成了两个部分:
- 通过beanName的注册。
- 通过别名的注册。
(1) 通过beanName注册
整体而言,我们都将直到Spring容器中存储BeanDefinition 的方式就是放在map 中,beanName 则作为map 的key 。具体的我们来看下:
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
↓↓↓↓↓↓↓
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
@Override
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 BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
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;
removeManualSingletonName(beanName);
}
}
else {
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
}
注册前的最后一次校验:((AbstractBeanDefinition) beanDefinition).validate();
public void validate() throws BeanDefinitionValidationException {
if (hasMethodOverrides() && getFactoryMethodName() != null) {
throw new BeanDefinitionValidationException(
"Cannot combine factory method with container-generated method overrides: " +
"the factory method must create the concrete bean instance.");
}
if (hasBeanClass()) {
prepareMethodOverrides();
}
}
总结下就是:
- 校验
methodOverrides 属性。 - 处理
beanName 被注册但是不允许被覆盖的情况。 - 加入
map 进行注册。 - 清除缓存。
(2) 通过别名注册
registry.registerAlias(beanName, alias);
↓↓↓↓↓↓↓
public class SimpleAliasRegistry implements AliasRegistry {
@Override
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)) {
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 + "'");
}
}
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
}
到这里,bean 标签的解析和注册已经结束,但这个仅仅是针对默认的标签解析,而其他的allias 、import 、beans 标签,本文不再展开描述。
二. 自定义标签的解析
讲完parseDefaultElement 方法后,也就是默认标签的解析流程。我们接下来去了解下自定义标签的使用和解析原理parseCustomElement 方法。
2.1 自定义标签的运用
扩展一个自定义标签的配置大概需要这么几个步骤:
- 创建一个需要扩展的组件。
- 定义一个
XSD 文件描述组件的内容。 - 创建一个文件,实现
BeanDefinitionParser 接口,用来解析XSD 文件中的定义和组件定义。 - 创建一个
Handler 文件,扩展自NamespaceHandlerSupport ,用于将组件注册到Spring容器中。 - 编写
Spring.handlers 和Spring.schemas 文件。
案例,项目结构如下: 1.创建一个需要扩展的组件User 。
package com.mytest;
public class User {
private String email;
private String name;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.定义一个XSD 文件描述组件的内容,user.xsd ,这里定义了3种标签属性,id、name、email
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.ljjTest.com/schema/user"
xmlns:tns="http://www.ljjTest.com/schema/user"
elementFormDefault="qualified">
<element name="user">
<complexType>
<attribute name="id" type="string"/>
<attribute name="name" type="string"/>
<attribute name="email" type="string"/>
</complexType>
</element>
</schema>
3.创建一个文件,实现BeanDefinitionParser 接口,用来解析XSD 文件中的定义和组件定义,UserBeanDefinitionParser :
package com.mytest;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
public class UserBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
protected Class getBeanClass(Element element) {
return User.class;
}
protected void doParse(Element element, BeanDefinitionBuilder bean) {
String name = element.getAttribute("name");
String email = element.getAttribute("email");
if (StringUtils.hasText(name)) {
bean.addPropertyValue("name", name);
}
if (StringUtils.hasText(email)) {
bean.addPropertyValue("email", email);
}
}
}
4.创建一个Handler 文件,扩展自NamespaceHandlerSupport ,用于将组件注册到Spring容器中,UserNamespaceHandler :
package com.mytest;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
}
5.编写Spring.handlers 和Spring.schemas 文件。 Spring.handlers :
http\://www.ljjTest.com/schema/user=com.mytest.UserNamespaceHandler
Spring.schemas :
http\://www.ljjTest.com/schema/user.xsd=META-INF/user.xsd
6.编写XML 配置Bean 。user.xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:myname="http://www.ljjTest.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.ljjTest.com/schema/user http://www.ljjTest.com/schema/user.xsd">
<myname:user id = "user" name="粽" email = "胖粽"/>
</beans>
7.测试类Test :
package com.mytest;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;
public class Test {
@org.junit.jupiter.api.Test
public void test() {
BeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);
ClassPathResource resource = new ClassPathResource("user.xml");
reader.loadBeanDefinitions(resource);
User user = (User) factory.getBean("user");
System.out.println(user.getName());
System.out.println(user.getEmail());
}
}
结果如下: Spring加载自定义标签的流程大概是这样:
- 遇到自定义标签后,就去
Spring.handlers 和Spring.schemas 文件中去找到对应的解析handler 和XSD 。默认位置是/META-INF/ 下。 - 找对于的
handler 后,自然而然能够找到解析元素的Parser 。进而完成整个自定义元素的解析。
2.2 自定义标签解析原理
在了解自定义标签的使用和大致流程后,接下来让我们来看下Spring是如何对自定义标签进行解析的。让我们继续从parseCustomElement 这个方法作为入口来解析:
public class BeanDefinitionParserDelegate {
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
}
↓↓↓↓↓↓
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
我们来看下第二步,提取自定义标签处理器。
2.2.1 提取自定义标签处理器
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
public NamespaceHandler resolve(String namespaceUri) {
Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
}
}
}
上述代码很好理解,无非就是根据我们Spring.handlers 配置文件中的映射地址,去找到对应的Handler ,通过反射去调用其初始化方法,来提取自定义标签处理器。 那么Spring是如何读取配置文件的呢?我们来看下第一步代码
Map<String, Object> handlerMappings = getHandlerMappings();
↓↓↓↓↓↓
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
if (logger.isTraceEnabled()) {
logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
}
try {
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isTraceEnabled()) {
logger.trace("Loaded NamespaceHandler mappings: " + mappings);
}
handlerMappings = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}
我们来看下其构造:
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
↓↓↓↓↓↓
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
}
↓↓↓↓↓↓
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader, String handlerMappingsLocation) {
Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
this.handlerMappingsLocation = handlerMappingsLocation;
}
可见我们在固定的文件目录下创建固定名称的文件是有意义的(META-INF/spring.handlers )
2.2.2 标签解析
上述代码中,我们只讲完了Handler的初始化工作,还没有讲其解析的工作,我们来看下第三步:
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
代码定位到NamespaceHandlerSupport 类中:
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
}
解析的第一步肯定是找到对应的解析器,而我们Handler 在初始化的时候,注册了自己的解析器UserBeanDefinitionParser :
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
我们来看下findParserForElement 方法:
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
最后对于解析的处理,定位到AbstractBeanDefinitionParser 类:
public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {
public final BeanDefinition parse(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error(
"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
+ "' when used as a top-level tag", element);
}
String[] aliases = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
registerBeanDefinition(holder, parserContext.getRegistry());
if (shouldFireEvents()) {
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
String msg = ex.getMessage();
parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
return null;
}
}
return definition;
}
}
这里就和上文的默认标签解析的流程大致相同了,解析Bean 属性、用holder 存放数据、再注册等等。文章到这里也就讲完了。讲到这里,Spring对标签(默认、自定义)的解析、处理都已经结束。而Spring中的全部解析工作也已经完成,即:bean 从配置文件到加载到内存的全部过程。
三. 大总结☆
Spring对标签的解析分为两种:
- 默认标签解析(Spring原生自带的)
parseDefaultElement 方法。 - 自定义标签解析
parseCustomElement 方法。
本文对标签的解析是在Spring读取解析好XML 配置并将其转化为Document 对象之后。通过解析Document 对象来解析标签获得可用的BeanDefinition 。
parseDefaultElement 流程(一共4种:<import> 、<alias> 、<bean> 、<beans> ),以最常见的<bean> 标签的解析为例,
下一篇文章准备学习下bean 的加载。
|