前言一. 默认标签的解析
1.1 bean标签的解析和注册
1.1.1 解析BeanDefinition
(1) GenericBeanDefinition对象(2) 解析各种属性(3) lookup-method标签的作用(4) replaced-method标签的作用(5) constructor-arg标签的作用(6) 解析property子元素(7) qualifier子元素 1.1.2 AbstractBeanDefinition的属性1.1.3 解析默认标签中的自定义标签1.1.3 注册解析的BeanDefinition
(1) 通过beanName注册(2) 通过别名注册 二. 自定义标签的解析
2.1 自定义标签的运用2.2 自定义标签解析原理
2.2.1 提取自定义标签处理器2.2.2 标签解析 三. 大总结☆
前言在了解了Spring容器加载资源的一个过程后,其对资源加载的最后一步则是生成对应的BeanDefinition,在本篇文章,我们着重讲Spring是如何对配置文件的标签进行解析的流程,parseDefaultElement和parseCustomElement方法。
先从parseDefaultElement开始:
一. 默认标签的解析默认标签的解析,会分别对4种不同的标签做处理:
import:importBeanDefinitionResource()alias:processAliasRegistration()bean:processBeanDefinition()beans:doRegisterBeanDefinitions()
public class DefaultBeanDefinitiondocumentReader implements BeanDefinitiondocumentReader {
// 这里的ele对象,是Spring容器已经将配置资源读取、加载完成并转化为document对象了,可以理解为文档对象的节点。
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);
}
}
}
还记得我们上篇文章中讲的小案例吗?里面用的就是bean标签,这也是最基本最常用的一个标签,因此我们着重来讲它。
1.1 bean标签的解析和注册
让我们来深入processBeanDefinition方法:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1.进行Bean元素解析,返回的实例bdHolder中,已经包含配置文件里面的各种属性了,例如:class、name、id、alias等。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 2.若默认标签的子节点下还有自定义的属性,那么还需要再次解析。
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 3.解析完成后,需要对解析后的bdHolder进行注册。
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
// ...
}
// 4.发送响应事件,通知相关监听器,该bean已经加载完成了。
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) {
// 解析id属性和name属性
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 1.分割name属性,根据 ,; 来分割,MULTI_VALUE_ATTRIBUTE_DELIMITERS = ",; ";
List 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);
// log...
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 2.这里会进一步解析其他所有属性,并统一封装成beanDefinition,最后返回的实例对象为GenericBeanDefinition类型,
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// 3.若不存在beanName,则根据Spring的命名规则为当前bean生成对应的beanName
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);
}
}
// log...
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 4.将获取到的信息封装成BeanDefinitionHolder的实例中并返回。
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));
// 解析class属性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 解析parent属性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建一个用于承载属性的GenericBeanDefinition对象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 硬编码解析默认bean的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取description,bean的一个描述信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DEscriptION_ELEMENT));
// 解析元数据
parsemetaElements(ele, bd);
// 解析lookup-method属性:把一个方法声明为返回某种类型的bean的标签。用于设计插拔功能
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 {
// 各种catch...
}
finally {
this.parseState.pop();
}
return null;
}
这里的解析其他属性,也就是解析class、description、property等属性。我们先来看下,用于承载这些属性的GenericBeanDefinition对象:
(1) GenericBeanDefinition对象BeanDefinition是一个接口,他是配置文件
Spring中,BeanDefinition的实现有这3个常见的,它们都继承了AbstractBeanDefinition
RootBeanDefinition:最常用的实现类,对应一般性的 Spring容器一般在读取加载完配置资源文件后,将其转为BeanDefinition类型实例的内部表示,而这些实例一般会注册到BeanDefinitionRegistry中。其主要以map形式来保存,后续操作都是从BeanDefinitionRegistry中读取配置信息的。 我们来看下parseBeanDefinitionAttributes()这个方法: 建议:如果想知道里面解析了哪些具体的属性,读者可以展开代码瞅瞅,否则,这段代码其实没什么好看的。 案例如下: 项目结构 Student类: GetNameTest类: test.xml文件: Test类: 结果如下: 也就是Spring会读取特定的 再把两者代码结合在一起来看看: 案例如下: Change类:需要实现MethodReplacer接口 test.xml: Test类: 结果如下: 那么上述两种标签有什么区别呢? 源码分析:(建议结合XML配置来看) 注意: 两种标签,最后都是创建了MethodOverride实例:LookupOverride与ReplaceOverride。用于记录相关参数。而其最终都是记录在AbstractBeanDefinition类中的methodOverrides属性中。
(5) constructor-arg标签的作用
首先,我们先来看下这个标签有什么作用。案例如下: Test方法: user.xml: 结果如下: 上述流程当中,分为两种情况: 情况1:配置中指定了index属性:情况2:配置中没有指定index属性。
两种情况的前几步骤是一样的: 唯一不同的就是: 情况1:将valueHolder实例封装于BeanDefinition下的indexedArgumentValues属性中。情况2:将valueHolder实例封装于BeanDefinition下的genericArgumentValues属性中。
在此之前,先来讲一下解析构造子元素的方法原理parsePropertyValue: 再来看下Spring对Constructor-arg的子元素是如何解析的: 到这里,我们可以了解Spring会对可支持的子类进行分类处理,而具体的处理就不再一一展开细说了。 老样子,先来看下它有什么作用: user.xml: 结果如下: Object val = parsePropertyValue(ele, bd, propertyName);这行代码,我们在第5小节,解析constructor-arg标签的时候就已经讲过了,无非就是解析ref、value以及子元素(set、map等标签)的过程,这里也就不做展开。 最终解析好的属性则会记录在BeanDefinition中的propertyValues属性中。 Spring框架中进行自动装配时,Spring容器中匹配的候选Bean的数量有且只有一个。 Spring允许我们通过Qualifier指定注入Bean的名称,这样可以用于消除歧义。 若找不到一个匹配的bean时,Spring容器将抛出BeanCreationnException异常。 贴出注解形式的伪代码: 1.1.1节中,对标签的解析和属性的承载,也就是完成了XML文档到GenericBeanDefinition的转换。其中XML的所有配置都可以在GenericBeanDefinition的实例类中找到对应的位置。大部分的通用属性则保存在AbstractBeanDefinition中。 我们来看下这个类中的属性: 再从头看起:默认标签的解析processBeanDefinition方法: 上文已经把第一步中,对配置文件的一个基础解析和提取已经讲完了。至此我们继续第二步,解析默认标签下的自定义属性bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 注意,这里的自定义标签对应的是bean 的一个属性。 从代码中我们可以看出来,无论是哪一个代码块,最终都执行了decorateIfRequired方法: 对于配置文件,Spring容器负责加载解析,将其转化为document实例。而上述流程则是将配置解析完了。相关的装饰功能也做好了,那么此时BeanDefinition已经满足使用的要求了,剩下的工作即是注册了。 代码入口如下: 我们来深入了解: 可见对于BeanDefinition的注册分成了两个部分: 整体而言,我们都将直到Spring容器中存储BeanDefinition的方式就是放在map中,beanName则作为map的key。具体的我们来看下: 注册前的最后一次校验:((AbstractBeanDefinition) beanDefinition).validate(); 总结下就是: 到这里,bean标签的解析和注册已经结束,但这个仅仅是针对默认的标签解析,而其他的allias、import、beans标签,本文不再展开描述。 讲完parseDefaultElement方法后,也就是默认标签的解析流程。我们接下来去了解下自定义标签的使用和解析原理parseCustomElement方法。 扩展一个自定义标签的配置大概需要这么几个步骤: 案例,项目结构如下: 2.定义一个XSD文件描述组件的内容,user.xsd,这里定义了3种标签属性,id、name、email 3.创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义,UserBeanDefinitionParser: 4.创建一个Handler文件,扩展自NamespaceHandlerSupport,用于将组件注册到Spring容器中,UserNamespaceHandler: 5.编写Spring.handlers和Spring.schemas文件。 Spring.schemas: 6.编写XML配置Bean。user.xml: 7.测试类Test: 结果如下: 在了解自定义标签的使用和大致流程后,接下来让我们来看下Spring是如何对自定义标签进行解析的。让我们继续从parseCustomElement这个方法作为入口来解析: 我们来看下第二步,提取自定义标签处理器。 上述代码很好理解,无非就是根据我们Spring.handlers配置文件中的映射地址,去找到对应的Handler,通过反射去调用其初始化方法,来提取自定义标签处理器。 那么Spring是如何读取配置文件的呢?我们来看下第一步代码 我们来看下其构造: 可见我们在固定的文件目录下创建固定名称的文件是有意义的(meta-INF/spring.handlers) 上述代码中,我们只讲完了Handler的初始化工作,还没有讲其解析的工作,我们来看下第三步: 代码定位到NamespaceHandlerSupport类中: 解析的第一步肯定是找到对应的解析器,而我们Handler在初始化的时候,注册了自己的解析器UserBeanDefinitionParser: 我们来看下findParserForElement方法: 最后对于解析的处理,定位到AbstractBeanDefinitionParser类: 这里就和上文的默认标签解析的流程大致相同了,解析Bean属性、用holder存放数据、再注册等等。文章到这里也就讲完了。讲到这里,Spring对标签(默认、自定义)的解析、处理都已经结束。而Spring中的全部解析工作也已经完成,即:bean从配置文件到加载到内存的全部过程。 Spring对标签的解析分为两种: 本文对标签的解析是在Spring读取解析好XML配置并将其转化为document对象之后。通过解析document对象来解析标签获得可用的BeanDefinition。 parseDefaultElement流程(一共4种: 下一篇文章准备学习下bean的加载。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// 1.解析scope属性。singleton这个标签已经过时了,改用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) {
bd.setScope(containingBean.getScope());
}
// 2.解析abstract属性
if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
}
// 3.解析lazy-init属性
String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
if (isDefaultValue(lazyInit)) {
lazyInit = this.defaults.getLazyInit();
}
bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
// 4.解析autowire属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire));
// 5.解析depends-on属性
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
}
// 6.解析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));
}
// 7.解析primary属性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
}
// 8.解析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);
}
// 9.解析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);
}
// 10.解析factory-method属性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
}
// 11.解析factory-bean 属性
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");
}
}
public class Student extends User{
void getName(){
System.out.println("I am Student");
}
}
public abstract class GetNameTest {
public abstract User getBean();
public void getName() {
// 调用了抽象方法getBean(),返回User实例
this.getBean().getName();
}
}
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);
// 仅仅在 Spring默认bean的子元素为lookup-method 时有效
if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
Element ele = (Element) node;
// 获取要修饰的方法
String methodName = ele.getAttribute(NAME_ATTRIBUTE);
// 获取配置返回的bean引用
String beanRef = ele.getAttribute(BEAN_ELEMENT);
// LookupOverride实体对象用于记录属性 ,最终记录在AbstractBeanDefinition类中的methodOverrides中
LookupOverride override = new LookupOverride(methodName, beanRef);
override.setSource(extractSource(ele));
overrides.addOverride(override);
}
}
}
所以,当我们在业务中,不再需要Student类的相关逻辑了,需要Teacher类的getName()方法,我们只需要换一个引用即可,该标签常用于插拔功能。
GetNameTest类:public class GetNameTest {
public void getName() {
System.out.println("Hello");
}
}
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;
}
}
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();
}
}
可以看出来,public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 仅当在默认bean的子标签为replace-method情况下有效
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);
// Look for arg-type match elements.
List
User类:public class User {
private int id;
private String name;
private Map
@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"));
}
可以看到,该标签在功能上,实现了初始化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) {
// 提取index属性,对应构造函数的第几个参数
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 提取type属性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 提取name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
// 构造函数的参数下标,不可能比0小
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
// 解析ele对应的属性元素
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 {
// 将type name index属性都封装进去,并将最终的封装实例塞到BeanDefinition的ConstructorArgumentValues下的indexedArgumentValues属性中
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 {
// 若没有指定index属性,则自动寻找
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
// valueHolder用于封装解析出来的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
// 将type name index属性都封装进去,并将最终的封装实例,塞到BeanDefinition的ConstructorArgumentValues下的genericArgumentValues属性中
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();
}
}
}
解析Constructor-arg的子元素。使用valueHolder实例去封装解析出来的元素。
Object value = parsePropertyValue(ele, bd, null);
↓↓↓↓↓↓
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"
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);
}
// 解析 value 标签
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parsevalueElement(ele, defaultValueType);
}
// 解析 null 标签
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
// 解析 array 标签
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
// 解析 list 标签
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
// 解析 set 标签
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
// 解析 map 标签
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
// 解析 props 标签
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}
@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());
}
看来,其也就是用于给类的字段赋值的,我们来看下其源码。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();
}
}
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() {
// 输出:i am student
user.log();
}
}
1.1.2 AbstractBeanDefinition的属性
public abstract class AbstractBeanDefinition extends BeanmetadataAttributeAccessor
implements BeanDefinition, Cloneable {
private volatile Object beanClass;
// bean 的作用范围,对应bean的scope属性
private String scope = SCOPE_DEFAULT;
// 是否抽象,对应bean的abstract
private boolean abstractFlag = false;
// 是否延迟加载。对应bean的lazy-init
private Boolean lazyInit;
// 自动注入模式,对应bean的autowire
private int autowireMode = AUTOWIRE_NO;
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
// 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean标签下的depend-on
private String[] dependsOn;
// 设置为false的话,表示容器在查找自动装配的对象的时候,将不考虑该bean,对应autowire-candidate
private boolean autowireCandidate = true;
// 当自动装配出现多个bean的候选者的时候,作为首选者
private boolean primary = false;
// 用于记录Qualifier,对影子元素qualifier
private final Map
1.1.3 解析默认标签中的自定义标签
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 1.进行Bean元素解析,返回的实例bdHolder中,已经包含配置文件里面的各种属性了,例如:class、name、id、alias等。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 2.若默认标签的子节点下还有自定义的属性,那么还需要再次解析。
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 3.解析完成后,需要对解析后的bdHolder进行注册。
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
// ...
}
// 4.发送响应事件,通知相关监听器,该bean已经加载完成了。
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
什么叫默认标签下的自定义标签呢?上代码:bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
↓↓↓↓↓↓
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
// 都三个参数的作用:父类bean,当对某个嵌套配置进行分析时,需要用到父类的scope属性
// 而当前分析默认标签的自定义标签这个场景下,分析的是顶层配置,因此传递null
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
}
public BeanDefinitionHolder decorateIfRequired(
Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
// 1.获取自定义标签的命名空间
String namespaceUri = getNamespaceURI(node);
if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
// 2.根据命名空间找到对应的处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler != null) {
// 3.进行修饰
BeanDefinitionHolder decorated =
handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
if (decorated != null) {
return decorated;
}
}
// ...
}
return originalDef;
}
获取属性或元素的命名空间。判断该元素是否适用于自定义标签解析的条件。找到自定义类型对应的handler进行进一步解析(第二章节具体展开)。
1.1.3 注册解析的BeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
public abstract class BeanDefinitionReaderUtils {
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 1.使用beanName作为唯一的标识key注册
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 2.注册所有的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
}
通过beanName的注册。通过别名的注册。
(1) 通过beanName注册
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
↓↓↓↓↓↓↓
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 1.beanName和对应的BeanDefinition肯定不能为空或者null,否则我还注册啥呢?注册也就是将其变成key-value而已
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
// 2.进行注册前的最后一次校验
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 若该beanName已经被注册
if (existingDefinition != null) {
// 3.若对于的beanName已经注册并且在配置中配置了bean不可以被覆盖,那么此时抛出异常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
// ...各种log记录
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// beanDefinitionMap作为全局变量,可能存在并发访问的情况
synchronized (this.beanDefinitionMap) {
// 4.加入map缓存,该BeanDefinition通过注册
this.beanDefinitionMap.put(beanName, beanDefinition);
// 5.更新beanName列表
List
public void validate() throws BeanDefinitionValidationException {
// 主要是对AbstractBeanDefinition中的methodOverrides进行校验
// 校验methodOverrides是否和工厂方法并存,或者methodOverrides对应的方法不存在。
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) {
// 若beanName和别名alias相同,则不记录alias,并删除它,
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;
}
// 同样考虑到alias是否允许被覆盖
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 + "'");
}
}
// 检查是否存在循环引用,例如”A->B存在时,若出现A->C->B则抛出异常
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
}
创建一个需要扩展的组件。定义一个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;
}
}
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);
}
}
}
package com.mytest;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
}
Spring.handlers:http://www.ljjTest.com/schema/user=com.mytest.UserNamespaceHandler
http://www.ljjTest.com/schema/user.xsd=meta-INF/user.xsd
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 自定义标签解析原理
public class BeanDefinitionParserDelegate {
public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
}
↓↓↓↓↓↓
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 1.获取对应的命名空间(用的是第三方org.w3c.dom.Node的方法,不做深入了解)
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 2.根据命名空间找到对应的Handler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 3.调用自定义的Handler进行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
public NamespaceHandler resolve(String namespaceUri) {
// 1.获取所有已经配置的handler映射
Map
Map
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;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
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);
}
}
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 1.获取元素名称,也就是public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {
public final BeanDefinition parse(Element element, ParserContext parserContext) {
// 1.做一系列的数据准备,对beanClass、scope、lazyInit等属性的准备
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
// 解析id
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));
}
}
// 将解析出来的结果存于holder实例,并注册BeanDefinition
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;
}
}
默认标签解析(Spring原生自带的)parseDefaultElement方法。自定义标签解析parseCustomElement方法。



