spring加载配置
源码分析基于spring 4.3.x
本文通过阅读源码,分析spring加载配置文件的过程
小栗子
bean类
public class Blog {
private String title;
... getter and settter
}
spring.xml配置
<bean id="blog" class="spring.bean.Blog">
<property name="title" value="hello spring"></property>
</bean>
测试方法
@Test
public void test() {
BeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("spring.xml"));
Blog bean = (Blog)xmlBeanFactory.getBean("blog");
Assert.assertEquals(bean.getTitle(), "hello spring");
}
解析
XmlBeanFactory
继承于DefaultListableBeanFactory
,DefaultListableBeanFactory
是整个bean加载的核心部分,是Spring注册及加载的bean的默认实现。 XmlBeanFactory
只是使用了自定义的XML读取器XmlBeanDefinitionReader
但本篇文章就关注XmlBeanDefinitionReader
跟踪XmlBeanFactory
的构造方法,
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public /home/binecy/work/bin-learning(Resource resource, BeanFactory parentBeanFactory) ... {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource); // 注:this.reader为XmlBeanDefinitionReader
}
接口结构
reader.pngXmlBeanFactory实现了BeanDefinitionRegistry, 可以将beanDefinition注册到spring环境
XmlBeanDefinitionReader实现了BeanDefinitionReader, 实现核心方法loadBeanDefinitions, 该方法复制读取xml文件,并加载到spring中, 是spring加载配置的核心。
BeanDefinitionReader中有一个关键属性BeanDefinitionRegistry, 这里就是XmlBeanFactory(XmlBeanDefinitionReader构造参数的this就是XmlBeanFactory)
这里会调用到XmlBeanDefinitionReader.loadBeanDefinitions方法:
public int loadBeanDefinitions(EncodedResource encodedResource) {
//获取配置文件的InputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 构造InputSource
InputSource inputSource = new InputSource(inputStream);
// 解析InputSource
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
// 查看 解析过程
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) {
// 获取Document对象
Document doc = doLoadDocument(inputSource, resource);
// 解析Document并注册到spring
return registerBeanDefinitions(doc, resource);
}
// 查看Document解析过程
public int registerBeanDefinitions(Document doc, Resource resource) {
// 创建DefaultBeanDefinitionDocumentReader对象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 解析documentReader
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
}
先看一下createReaderContext
public XmlReaderContext createReaderContext(Resource resource) {
return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
this.sourceExtractor, this, getNamespaceHandlerResolver());
}
XmlReaderContext就是一个上下文, 这里把this(就是XmlBeanDefinitionReader), resource等信息放到了这个上下文中。
再看看BeanDefinitionDocumentReader, 他是一个简单的接口, 只有一个方法registerBeanDefinitions和一个实现类DefaultBeanDefinitionDocumentReader, 但对xml的解析基本都在这个类了:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
// 获取root元素
Element root = doc.getDocumentElement();
// 解析root元素
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
// 创建BeanDefinitionParserDelegate对象
this.delegate = createDelegate(getReaderContext(), root, parent);
preProcessXml(root); // 模板方法,提供给子类扩展
parseBeanDefinitions(root, this.delegate); // 解析root元素
postProcessXml(root); // 模板方法,提供给子类扩展
}
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); // 解析node结点
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
上述方法根据Namespace Uri判断node是否为Spring定义的元素,如果是,则调用parseDefaultElement方法解析元素。否则调用自定义的解析方法。
用户可以自定义标签及标签解析器, 文章最后会提到。
下面看看默认的解析方法:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // 解析import标签
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // 解析alias标签
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // 解析bean标签
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // 解析beans标签
doRegisterBeanDefinitions(ele);
}
}
parseDefaultElement
方法对import,alias,bean,beans标签进行了解析,这里主要看bean的解析过程:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析元素,解析结果为BeanDefinitionHolder对象
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); // 装饰模式
// 注册BeanDefinition,即将解析结果存储在registry对象中.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());
// 发送事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
这里出现了一个很重要的角色BeanDefinition。
BeanDefinition是spring对bean的抽象, 是配置文件中<bean>
标签在spring容器中的内部表示类, 存储了bealn的相关信息, 如属性PropertyValues, BeanClassName,Scope等
BeanDefinition有两个实现类ChildBeanDefinition和RootBeanDefinition。
BeanDefinition存在子类RootBeanDefinition和ChildBeanDefinition。
ChildBeanDefinition表示child <bean>
, child <bean>
从parent
中继承父类的构造函数参数值,属性值和方法覆盖,并且可以添加新的值。
RootBeanDefinition表示没有parent
的bean
spring会将配置文件中<bean>配置信息转化为beanDefinition对象,并注册到容器中。
BeanDefinitionHolder中持有BeanDefinition实例, 同时附带了beanName和aliases属性。
回来看看delegate.parseBeanDefinitionElement
,这里调用BeanDefinitionParserDelegate.parseBeanDefinitionElement方法:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 解析bean元素
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
String beanClassName = beanDefinition.getBeanClassName();
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
// 查看bean元素的解析过程
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); // 解析className
parent = ele.getAttribute(PARENT_ATTRIBUTE);
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); // 解析"scope","scope"等属性
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd); // 解析meta属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); // 处理lookup-method元素
parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // 处理replaced-method元素
parseConstructorArgElements(ele, bd); // 解析constructor-arg构造方法参数
parsePropertyElements(ele, bd); // 解析property元素
parseQualifierElements(ele, bd); //解析qualifier元素
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
看一下属性的解析过程parsePropertyElements
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 如果是property节点就解析
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
// 解析property节点
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
// 解析property节点Value
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
// 解析结果添加到BeanDefinition
bd.getPropertyValues().addPropertyValue(pv);
}
// 解析property节点的Value
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if (hasRefAttribute) { // 如果有ref属性
String refName = ele.getAttribute(REF_ATTRIBUTE);
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) { // 如果有value属性
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
} else if (subElement != null) {
// 如果有子标签,如list,set
return parsePropertySubElement(subElement, bd);
}
...
}
可以看到,解析property节点主要解析它ref属性(结果为RuntimeBeanReference)或value属性(结果为TypedStringValue)或子标签,并将结果添加到BeanDefinition 中,供spring生成bean时使用。
可以看看子标签的解析
public Object parsePropertySubElement(Element ele, BeanDefinition bd, 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);
}
...
}
可以看到,这里对各种类型的子标签(bean,ref,array,list等)进行了解析。这里不再对每一类型的子标签展开了。
最后看一下注册过程BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) ...{
// 为bean注册beanName
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 为bean注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
getReaderContext().getRegistry()获取到XmlBeanFactory, registry.registerBeanDefinition
最终将调用到DefaultListableBeanFactory.registerBeanDefinition方法:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) ... {
this.beanDefinitionMap.put(beanName, beanDefinition);
}
最后,bean解析结果beanDefinition存储在DefaultListableBeanFactory的beanDefinitionMap
属性中(实际上就是map对象)。
最后, 再看一下自定义标签的处理, 将调用BeanDefinitionParserDelegate.parseCustomElement
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
// 获取对应的NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
// 调用NamespaceHandler处理
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
这里可以看到, 自定义标签主要使用自定义的NamespaceHandler进行处理。