这里主要是使用org.springframework:spring-beans:5.2.0.RELEASE进行分析
BeanDefinitions
一. XmlBeanDefinitionReader
- 在上一章Document文件配置读取后,需要注册Bean,当拥有XML文档文件的Document实例对象时,就会被引入XmlBeanDefinitionReader的这个方法。
1. XmlBeanDefinitionReader里面的 registerBeanDefinitions() 源码
//其中的参数doc是loadDocument加载转换出来的
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 实例化BeanDefinitionReader时候会将BeanDefinitionRegistry传入,默认使用继承自DefaultListableBeanFactory的子类
int countBefore = getRegistry().getBeanDefinitionCount();
// 加载及注册bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 记录本次加载的BeanDefinition个数
return getRegistry().getBeanDefinitionCount() - countBefore;
}
- 使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader
- 实例化BeanDefinitionReader时候会将BeanDefinitionRegistry传入,默认使用继承自DefaultListableBeanFactory的子类
- 加载及注册bean
- 记录本次加载的BeanDefinition个数
- BeanDefinitionDocumentReader是一个接口,而实例化的工作是在createBeanDefinitionDocumentReader()中完成的
- 而通过此方法, BeanDefinitionDocumentReader真正的类型其实已经是DefaultBeanDefinitionDocumentReader了
二.DefaultBeanDefinitionDocumentReader
2. DefaultBeanDefinitionDocumentReader里面的 registerBeanDefinitions() 源码
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
1. doRegisterBeanDefinitions();
- doRegisterBeanDefinitions才是真正地开始进行解析
2. DefaultBeanDefinitionDocumentReader里面的 doRegisterBeanDefinitions 源码
protected void doRegisterBeanDefinitions(Element root) {
// 专门处理解析
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);
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;
}
- 处理流程解析
-
profile的处理
-
preProcessXml(root)
-
postProcessXml(root)
如果继承自DefaultBeanDefinitionDocumentReader的子类需要在Bean解析前后做一些处理的话,那么只需要重写这两个方法就可以了。
三 . profile属性的使用
- 在注册bean的最开始是对PROFILE_ATTRIBUTE属性的解析
<?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">
//开发环境
<beans profile="dev">
......
</beans>
//生产环境
<beans profile="production">
......
</beans>
</beans>
- Web环境中时,在web.xml中配置:
<context-param>
<param-name>Spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
3.这个特性可以在配置文件中部署两套配置来适用于生产环境和开发环境,这样可以方便的进行切换开发、部署环境,最常用的就是更换不同的数据库。
四. 解析并注册BeanDefinition
- 处理了profile后就可以进行XML的读取
- parseBeanDefinitions(root, this.delegate)源码
2. DefaultBeanDefinitionDocumentReader里面的 parseBeanDefinitions 源码
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 对beans的处理
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)) {
// 对bean的处理
parseDefaultElement(ele, delegate);
}
else {
// 对bean的处理
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
- Spring的XML配置里面有两大类Bean声明
对于根节点或者子节点如果是默认命名空间的话则采用parseDefaultElement方法进行解析,否则使用delegate.parseCustomElement(ele)方法对自定义命名空间进行解析,判断是否默认命名空间还是自定义命名空间的办法是使用node.getNamespaceURI()获取命名空间,并与Spring中固定的命名空间http://www.springframework.org/schema/beans进行比对,如果一致则认为是默认,否则就认为是自定义
//这个是默认的
<bean id="hello" class="com.xizi.pojo.Hello">
</bean>
//这个是自定义的
<tx:annotation-driven/>