在上文中我们已经知道了Spring如何从我们给定的位置加载到配置文件,并将文件包装成一个Resource对象。这篇文章我们将要探讨的就是,如何从这个Resouce对象中加载到我们的容器?加载到容器后又是什么样子呢?

大家可以跟着我一步步来,一定要把Spring啃完,加油~

前期准备:

上篇文章中,我们已经跟踪到了org.springframework.beans.factory.support.AbstractBeanDefinitionReaderloadBeanDefinitions方法,继续追踪代码,进入org.springframework.beans.factory.xml.XmlBeanDefinitionReader这个类中,追踪到如下代码:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}

我们可以看到,到这里将Resource对象又进行了一次封装,封装成一个EncodedResource对象,那么EncodedResource又是做什么的?通过名字,我们大致可以推测这个类主要是用于对资源文件的编码进行处理的。我们可以发现它有如下方法:

public Reader getReader() throws IOException {
if (this.charset != null) {
return new InputStreamReader(this.resource.getInputStream(), this.charset);
} else {
// 当我们设置了对应的编码属性时,Spring会使用相应的编码作为输入流的编码
return this.encoding != null ? new
InputStreamReader(this.resource.getInputStream(), this.encoding) : new InputStreamReader(this.resource.getInputStream());
}
}

OK,回到我们的主线,最终进入XmlBeanDefinitionReaderdoLoadBeanDefinitions方法

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException { try {
// 最终在这一步通过解析XML文件获取到了一个dom对象
// 具体的解析逻辑就不多说了,解析XML都大同小异,有兴趣的读者可以自行百度
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
// 省略部分代码
.......
	// 注册bean定义
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 获取一个BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 获取注册前BeanDefinition的数量
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}

继续跟踪代码进入:org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReadedoRegisterBeanDefinitions方法中

protected void doRegisterBeanDefinitions(Element root) {
// BeanDefinitionParserDelegate专门负责对标签的解析
// 在遇到嵌套标签时,该方法要被递归调用,为了delegate不被污染,先将其用parent保存
// 在方法执行完成后再this.delegate = parent,进行引用赋值
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) {
// 获取标签中的profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 判断标签中定义的profile属性的值是否在环境变量中定义过
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 用于子类复写,本身是空实现
preProcessXml(root);
// 解析标签
parseBeanDefinitions(root, this.delegate);
// 用于子类复写,本身是空实现
postProcessXml(root); this.delegate = parent;
}

继续跟踪代码,进入org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReaderparseBeanDefinitions方法

	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 解析默认的标签
parseDefaultElement(ele, delegate);
}
else {
// 解析自定义的标签
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

上面提到了两个概念,一个是默认标签,一个是自定义标签,那么什么是默认标签,什么是自定义标签呢?

// 默认标签
<bean id="bean"></bean>
// 自定义标签
<tx:annotation-driven/>

我们先来看默认标签的解析:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 对import标签进行处理
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 对alias标签进行处理
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 对bean标签进行处理
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 对beans标签进行处理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 递归
doRegisterBeanDefinitions(ele);
}
}

我们从最关键的processBeanDefinition(ele, delegate)方法开始分析:

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. 提取元素中的id和name属性
  2. 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中
  3. 如果监测到bean没有指定beanName,那么使用默认规则为此bean生成beanName
  4. 将获取到的信息封装到BeanDefinitionHolder中

为了更好的理解上面的过程,我们需要弄明白上面涉及到的几个类都是做什么的

  1. BeanDefinitionParserDelegate

这个类,我在上面已经说过了,主要负责解析我们的XML文件,方法大纲如下:

  1. BeanDefinitionHolder
public class BeanDefinitionHolder implements BeanMetadataElement {

	private final BeanDefinition beanDefinition;

	private final String beanName;

	@Nullable
private final String[] aliases;

因为这个类的方法还是非常简单的,相信大家自己能看明白,它的几个成员变量如上所示,这个类的主要作用是作为一个包含了别名及bean名称的beanDefinition的句柄

  1. BeanDefinition

BeanDefinition是一个接口,在Spring中常见的实现有三种:

  • org.springframework.beans.factory.support.RootBeanDefinition
  • org.springframework.beans.factory.support.ChildBeanDefinition
  • org.springframework.beans.factory.support.GenericBeanDefinition

整理其类图如下:

BeanDefinition是配置文件<bean>元素标签在容器的内部表示形式。<bean>元素标签拥有class,scope,lazy-init等配置属性,BeanDefinition则体统了相应的beanClass,scope,lazyInit属性,BeanDefinition<bean>中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的<bean>元素标签,在2.5以后,Spring中新增了GenericBeanDefinition,这个类的优势在于,能够动态的指定父依赖,而不是将这个类硬编码成为一个RootBeanDefinition

​ 在配置文件中可以定义父<bean>和子<bean>,父<bean>RootBeanDefinition表示,而子<bean>ChildBeanDefinition表示,而没有父<bean><bean>就使用RootBeanDefinition表示。AbstractBeanDefinition对两者共同的类信息进行抽象

​ Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistryBeanDefinitionRegistry中读取配置信息。

public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
//parserState是一个栈,里面只能存放ParseState.Entry类型的元素,所以,要想将String类型的 //beanName存入栈中,必须将beanName封装成Entry元素,这个类主要用于打印错误信息
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 {
// 前面已经说了BeanDefinition是配置文件在容器中的体现,所以在解析配置文件时必定要创建一个
// BeanDefinition来承载对应的数据,就是在这个地方创建的
// 用给定的className跟parent创建一个GenericBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 对"<id="" name="" scope="" ..>"配置形式进行解析,解析出该Bean的一些生命周期、
// 是否延迟加载、自动装配方式等属性值,并且赋值给上面生成的BeanDefinition实例
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//解析Bean的"<description ../>"属性
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析元数据
parseMetaElements(ele, bd);
// 解析lookup-method属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replace-method属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析构造函数参数
parseConstructorArgElements(ele, bd);
// 解析property子元素
parsePropertyElements(ele, bd);
// 解析qualifier子元素
parseQualifierElements(ele, bd);
// ....省略部分代码.....
}
开始解析:

接下来我们逐步分析每一个方法,并了解一下标签中每个属性的作用:

	public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
// singleton这个属性一个被scope替代
if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
}
// 指定了这个bean是单例还是多例的,单例singleton/多例prototype
else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
}
else if (containingBean != null) {
// 在嵌套bean标签的情况下,如果没指定scope的属性则使用父标签的scope属性值
bd.setScope(containingBean.getScope());
}
// abstract属性为true时,默认容器不创建这个bean,一般与parent属性配合使用
// 以提供通用模板
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)); // 解析autowire属性
String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
bd.setAutowireMode(getAutowireMode(autowire)); // 解析depends-on属性
if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
} // 解析autowire-candidate属性
String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
if (isDefaultValue(autowireCandidate)) {
String candidatePattern = this.defaults.getAutowireCandidates();
if (candidatePattern != null) {
String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
}
}
else {
bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
} // 解析primary属性
if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
} // 解析init-method属性
if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
bd.setInitMethodName(initMethodName);
}
else if (this.defaults.getInitMethod() != null) {
bd.setInitMethodName(this.defaults.getInitMethod());
bd.setEnforceInitMethod(false);
} // 解析destory-method属性
if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
bd.setDestroyMethodName(destroyMethodName);
}
else if (this.defaults.getDestroyMethod() != null) {
bd.setDestroyMethodName(this.defaults.getDestroyMethod());
bd.setEnforceDestroyMethod(false);
} // 解析factory-method属性
if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
} // 解析factory-bean属性
if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
} return bd;
}

上面还有几个标签的属性没有介绍,下面我们一起过一遍

  • autowire属性
模式 说明
no (默认)不采用autowire机制.。这种情况,当我们需要使用依赖注入,只能用标签。
byName 通过属性的名称自动装配(注入)。Spring会在容器中查找名称与bean属性名称一致的bean,并自动注入到bean属性中。当然bean的属性需要有setter方法。例如:bean A有个属性master,master的setter方法就是setMaster,A设置了autowire=“byName”,那么Spring就会在容器中查找名为master的bean通过setMaster方法注入到A中。
byType 通过类型自动装配(注入)。Spring会在容器中查找类(Class)与bean属性类一致的bean,并自动注入到bean属性中,如果容器中包含多个这个类型的bean,Spring将抛出异常。如果没有找到这个类型的bean,那么注入动作将不会执行。
constructor 类似于byType,但是是通过构造函数的参数类型来匹配。假设bean A有构造函数A(B b, C c),那么Spring会在容器中查找类型为B和C的bean通过构造函数A(B b, C c)注入到A中。与byType一样,如果存在多个bean类型为B或者C,则会抛出异常。但时与byType不同的是,如果在容器中找不到匹配的类的bean,将抛出异常,因为Spring无法调用构造函数实例化这个bean。
default 采用父级标签(即beans的default-autowire属性)的配置。
  • autowire-candidate

前面我们说到配置有autowire属性的bean,Spring在实例化这个bean的时候会在容器中查找匹配的bean对autowire bean进行属性注入,这些被查找的bean我们称为候选bean。作为候选bean,我凭什么就要被你用,老子不给你用。所以候选bean给自己增加了autowire-candidate="false"属性(默认是true),那么容器就不会把这个bean当做候选bean了,即这个bean不会被当做自动装配对象。同样,标签可以定义default-autowire-candidate="false"属性让它包含的所有bean都不做为候选bean。我的地盘我做主。

  • primary

primary这个翻译过来是 首要的,首选的意思。

primary的值有true和false两个可以选择。默认为false。

当一个bean的primary设置为true,然后容器中有多个与该bean相同类型的其他bean,

此时,当使用@Autowired想要注入一个这个类型的bean时,就不会因为容器中存在多个该类型的bean而出现异常。而是优先使用primary为true的bean。

不过,如果容器中不仅有多个该类型的bean,而且这些bean中有多个的primary的值设置为true,那么使用byType注入还是会出错。

  • init-method

用于指定bean初始化时指定执行的方法

  • destory-method

用于指定bean销毁时指定执行的方法

  • factory-method

指定从工厂中获取bean的方法

  • factory-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"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 默认使用无参数的构造放创建对象 -->
<bean id="bean1" class="com.igeek.Bean1"/>
<!-- 通过类中的静态方法获取类的对象 -->
<!-- factory-method:返回对象的静态方法的名称,相当于Bean2.getInstance()-->
<bean id="bean2" factory-method="getInstance" class="com.igeek.Bean2"/>
WW
<!-- 通过实例工厂创建Bean对象 -->
<!-- 配置实例工厂对象 -->
<bean id="bean3Factory" class="com.igeek.Bean3Factory"/>
<!--
factory-bean:创建Bean的实例工厂对象
factory-method:工厂对象中创建实例的方法
-->
<bean id="bean3" factory-bean="bean3Factory" factory-method="getBean3"/>

接下来我们看看元数据的解析

	// 入参是当前标签对应的Element及对应的Beandefinition
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
NodeList nl = ele.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
Element metaElement = (Element) node;
String key = metaElement.getAttribute(KEY_ATTRIBUTE);
String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
attribute.setSource(extractSource(metaElement));
// 将解析的结果放入Beandefinition,底层通过一个LinkedHashMap存储
attributeAccessor.addMetadataAttribute(attribute);
}
}
}

首先,元数据是什么呢?我们看如下配置:

<bean id="bean" class="com.study.test.configuration.Bean">
<meta key="test" value="my"/>
</bean>

这段代码不会体现在Bean的属性当中,而是一个额外的申明,当需要使用里面的信息时候可以通过Beandefinition的getAttribute(key)方法获取

关于look-up属性的解析

首先我们要知道look-up属性是用来做什么的,看如下代码:

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:tx="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<bean id="cat" class="com.study.test.configuration.spring.entity.Cat"/>
<bean id="dog" class="com.study.test.configuration.spring.entity.Dog"/>
<bean id="test" class="com.study.test.configuration.spring.entity.Test">
<lookup-method name="getBean" bean="cat"/>
</bean>
</beans>

实体类:

public interface Animal {
void eat();
} public class Cat implements Animal {
@Override
public void eat(){
System.out.println("猫吃鱼");
}
} public class Dog implements Animal {
@Override
public void eat(){
System.out.println("狗吃肉");
}
} public abstract class Test { public void eat() {
getBean().eat();
}
abstract Animal getBean();
} public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Test bean = context.getBean(Test.class);
bean.eat();
}

运行结果:

猫吃鱼

如果我们将<lookup-method name="getBean" bean="cat"/>改为<lookup-method name="getBean" bean="dog"/>

运行结果:

狗吃肉

通过以上示例,我们可以总结look-up属性的作用了:

我们可以将一个方法申明为返回某种类型的bean,但实际要返回的bean是我们进行配置的

就像上面的实例中,我们将getBean()申明为返回了一个annimal对象,但是实际返回的对象是我们在xml中配置的

现在我们再来看看解析的代码:

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);
}
}
}

可以看到将解析的定义封装成了一个LookupOverride,并添加进了BeanDefinition的MethodOverrides属性中

关于replaced-method的解析:

首先,我们还是要现在知道这个属性是干什么的,先看如下示例:

修改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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--<bean id="cat" class="com.study.test.configuration.spring.entity.Cat"/>-->
<!--<bean id="dog" class="com.study.test.configuration.spring.entity.Dog"/>-->
<bean id="test" class="com.study.test.configuration.spring.entity.Cat">
<!--<lookup-method name="getBean" bean="cat"/>-->
<replaced-method name="eat" replacer="myReplacer"/>
</bean>
<bean id="myReplacer" class="com.study.test.configuration.spring.entity.MyReplacer"/>
</beans>

新增类:

public class MyReplacer implements MethodReplacer {
@Override
public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
System.out.println("替代方法执行了");
return null;
}
}
public class Spring {

    public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml"); // ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
Cat bean = context.getBean(Cat.class);
bean.eat();
}
}

执行结果如下:

替代方法执行了

可以看到,使用这个标签结合自定义org.springframework.beans.factory.support.MethodReplacer的实现,我们可以动态的替换运行的方法

接下来看看解析的过程:

	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 replaceOverride = new ReplaceOverride(name, callback);
// 获取子标签arg-type对应的Element元素
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
for (Element argTypeEle : argTypeEles) {
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
// 如果子标签中的match属性没有值,就取标签中的文本
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
if (StringUtils.hasText(match)) {
// 记录参数类型
replaceOverride.addTypeIdentifier(match);
}
}
replaceOverride.setSource(extractSource(replacedMethodEle));
overrides.addOverride(replaceOverride);
}
}
}

构造函数的解析:

核心方法为org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#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();
}
}
}

我们结合标签来解释上面这段代码:

<bean id="myReplacer" class="com.study.test.configuration.spring.entity.MyReplacer">
<constructor-arg value="zhangSan" name="name" type="java.lang.String"/>
</bean>

上面代码主要根据constructor-arg标签中是否有index属性而分成了两个分支:

  1. 如果有index属性,那么将解析的index,type,name封装称为一个ConstructorArgumentValues.ValueHolder,然后添加进indexedArgumentValues,这实际上是一个Map集合,key是index,value是封装好的对象
  2. 如果没有index数据,同样也封装成一个ConstructorArgumentValues.ValueHolder,但是这种情况下,会将其添加到genericArgumentValues,这是一个ArrayList

上面这种设计也充分利用了Map跟ArrayList的特性

到这里位置,标签的分析我们就完成了最核心的部分了,现在回过头,我们再去看代码:

进入org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReaderparseBeanDefinitions方法:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {			// 第一句我们已经分析完了
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册bean
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));
}
}

现在我们的任务就落在了bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder)这句代码上,从名字上来说,我们可以知道,它大概的目的就是在需要的情况下装饰BeanDefinition,那么什么情况会需要呢?我们看下其实现:

	public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) { BeanDefinitionHolder finalDefinition = definitionHolder; // 需要的话,先装饰自定义的属性
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;
}

当Spring中的bean使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时,这个时候就需要装饰了,举例如下:

<bean id="myReplacer" class="com.study.test.configuration.spring.entity.MyReplacer">
<mybean:user username="aa"/>
</bean>

具体逻辑不再赘述。

总结:

经过上篇文章Spring源码阅读 之 配置的加载跟这篇文章,我们已经学习完了配置的加载,读取,解析。这篇文章中,并没有将所有标签的解析都说完,还有import,alias自定义标签等的解析没有进行说明,留给读者自行学习,实际上都大同小异

有正在学习Spring源码的朋友的话,可以留言一起交流哈~

下篇文章我们将着重分析,bean的注册,敬请期待哦~

喜欢的话,点个赞,加个关注吧,万分感谢!

Spring源码阅读 之 配置的读取,解析的更多相关文章

  1. Spring源码阅读 之 配置的加载(希望有喜欢源码的朋友一起交流)

    想写Spring的源码方面的东西想了好久了,之前花了一段时间学习了SpringCloud,现在总算对SpringCloud有了一个大概的了解,从今天开始好好读一篇Spring的源码,结合书本跟网上的一 ...

  2. spring源码阅读(1)bean解析

    public class Test { public static void main(String[] args) throws Exception { BeanFactory beanFactor ...

  3. Spring源码阅读-ApplicationContext体系结构分析

    目录 继承层次图概览 ConfigurableApplicationContext分析 AbstractApplicationContext GenericApplicationContext Gen ...

  4. 初始化IoC容器(Spring源码阅读)

    初始化IoC容器(Spring源码阅读) 我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ...

  5. Sping学习笔记(一)----Spring源码阅读环境的搭建

    idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...

  6. 搭建 Spring 源码阅读环境

    前言 有一个Spring源码阅读环境是学习Spring的基础.笔者借鉴了网上很多搭建环境的方法,也尝试了很多,接下来总结两种个人认为比较简便实用的方法.读者可根据自己的需要自行选择. 方法一:搭建基础 ...

  7. Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)

    我们到底能走多远系列(33) 扯淡: 各位:    命运就算颠沛流离   命运就算曲折离奇   命运就算恐吓着你做人没趣味   别流泪 心酸 更不应舍弃   ... 主题: Spring源码阅读还在继 ...

  8. Spring源码阅读笔记02:IOC基本概念

    上篇文章中我们介绍了准备Spring源码阅读环境的两种姿势,接下来,我们就要开始探寻这个著名框架背后的原理.Spring提供的最基本最底层的功能是bean容器,这其实是对IoC思想的应用,在学习Spr ...

  9. Spring源码情操陶冶-ComponentScanBeanDefinitionParser文件扫描解析器

    承接前文Spring源码情操陶冶-自定义节点的解析,本文讲述spring通过context:component-scan节点干了什么事 ComponentScanBeanDefinitionParse ...

随机推荐

  1. IO流学习总结

    IO: 概述: IO流用来处理设备之间的数据传输,如上传文件和下载文件 Java对数据的操作是通过流的方式 Java用于操作流的对象都在IO包中按照数据流向: 输入流 读入数据 从操作系统上读入文件到 ...

  2. Android调用系统设置

    最近,弄了一下,调用系统设置的方法,Android4.0的系统,下面的所有设置项,都亲测可以调用.首先调用的方式如下: Intent mintent_setting_time = new Intent ...

  3. Qt发送一次信号触发两次槽函数的原因

    在手动为控件编写槽函数的时候,如果将槽函数名字按如下格式编辑,则不需要再次进行手动关联 void on_pushButton_1_clicked(); void on_radioButton_clic ...

  4. python操作数据库-SQLSERVER-pyodbc

    刚开始学python时,大家都习惯用pymssql去读写SQLSERVER.但是实际使用过程中,pymssql的读写性能以及可靠性的确不如pyodbc来的好. 正如微软官方推荐使用pyodbc库,作为 ...

  5. mybatis源码配置文件解析之三:解析typeAliases标签

    在前边的博客在分析了mybatis解析settings标签,<mybatis源码配置文件解析之二:解析settings标签>.下面来看解析typeAliases标签的过程. 一.概述 在m ...

  6. 通过 Swoole\Table 实现 Swoole 多进程数据共享

    第三方存储媒介 前面我们介绍了基于 Swoole 的 Process 及 Process\Pool 模块在 PHP 中实现多进程管理,但是多进程模式下进程间是相互隔离的,无法共享数据和变量,即便是通过 ...

  7. MySql --FIND_IN_SET() 函数 (转)

    例子:https://www.jianshu.com/p/b2c1ba0ba34f 举个例子来说:有个文章表里面有个type字段,他存储的是文章类型,有 1头条,2推荐,3热点,4图文 .....11 ...

  8. thinkphp5 input坑

    取值方式改了而已?a1=1&a2=2这种可以用input(get.) a1/1/a2/2 用input('a1')和input('a2') post方法当然是input('post.') 我觉 ...

  9. css背景渐变色

    张鑫旭关于渐变色博客 菜鸟教程关于渐变色 .img-box{ background: #ec9259; /* 一些不支持背景渐变的浏览器 */ background: -webkit-linear-g ...

  10. QString 转换成 wchar 的一个小陷阱

    QString::toWCharArray(wchar_t * array) 其中 wchar_t * array 除了要分配内存之外,必须用 wmemset 初始化. 环境是 Visual Stud ...