spring源码之XmlBeanDefinitionReader与bean的注册
2013-09-24 15:57
429 查看
终于开始spring的源码了,记得距离自己本科阶段ssh的经历,应该已经过去了几年了了。。。
好了闲话不多说,先来看个简单的spring的例子,直接用的是spring的XmlBeanFactory,来看代码:
这个是主代码,用于从spring的ioc池子中获取bean,那么来看看xml文件的定义:
上面还有一部分aop的配置,这里就忽略掉。。。
来看看bean是怎么从xml文件中获取配置信息的吧:
其实在这里就可以看到,基本上都是reader在做事情,这也是这篇文章要做的主要的事情,来分析一些这个XmlBeanDfinitionReader到底是怎么读取数据的,还是先来看看它的继承体系吧:
![](https://oscdn.geek-share.com/Uploads/Images/Content/201309/7222ee059238a9dd9008341b5f0b5b0c)
这里XmlBeanDefinitionReader的继承体系还是很简单的,相对于spring其他类型的继承来说,那么我们先来看看最顶层的接口都定义了一些什么方法吧:
这里最重要的方法就是loadDefinitions,用于从resource中读取bean的定义,
好了,那么看完了接口的定义,接下来来看看这个loadBeanDefinitions是怎么实现的吧:
好了,这部分代码啥意思都没有,接下来继续吧:
这部分代码其实也没啥太大的意思,无非就是将resource保存起来,然后获取inputstream,然后在调用别的方法继续处理,不过这里有个自己比较郁闷的地方,为什么要用线程变量来保存处理的resource,难道不同的线程要保存不同的resource了。。?搞不懂。。。以后再说吧,接下来继续看代码:
从这部分代码开始就涉及到xml文件的内部了,说明准备开始解析xml文件了。。。不过内容并不多,可能最主要的就是要处理一些xml解析时候的异常吧,继续看代码:
这里开始准备读取xml文件,新建了一个document的reader,这个reader的类型其实是DefaultBeanDefinitionDocumentReader,然后真正的事情是这个reader来做的。。。那么接下来来看看这个reader做了啥吧:
这代码没啥意思,我勒个去,接下来继续看吧:
这段代码之后就要开始进行真正的xml文件的解析,并要注册bean的信息了,
这部分代码主要在做的事情就是遍历当前xml文件下的node,然后在解析这些node的数据,将其转化为bean的信息保存起来并注册信息:
这里我们就只看看如何处理bean的定义吧,其他的就不看了:
那么到这里为止,这个bean的信息的读取就已经差不多了,将bean的数据转化为定义的类型保存起来,具体是什么样子的就不管他了,无非就是一些id,property什么的。。。
那么接下来来看看这个注册的过程是怎么样子的吧:
这部分的代码就是用于将bean的名字(id)与bean的定义信息关联起来,然后保存在beanFactory的一个map里面,具体来看一下:
上述的代码就是整个bean的注册的过程,说白了就是将这些数据保存到当前beanfactory的一个ConcurrentHashMap里面去。。
好了,那么到这里整个bean的定义的解析与注册过程就差不多了,下一篇文章来看看整个生成bean吧。。。
好了闲话不多说,先来看个简单的spring的例子,直接用的是spring的XmlBeanFactory,来看代码:
package fjs; import java.io.File; import java.util.Map; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; public class Say { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void saying() { System.out.println("say is : " + name); } public void before() { System.out.println("before"); } public static void main(String args[]) { Resource res = new ClassPathResource( "beans.xml"); BeanFactory bf = new XmlBeanFactory(res); //ApplicationContext context = new FileSystemXmlApplicationContext("beans.xml"); //Say say1 = (Say)bf.getBean("say1"); //Say say2 = (Say)bf.getBean("say1"); //System.out.println(say1 == say2); //say1.saying(); } }
这个是主代码,用于从spring的ioc池子中获取bean,那么来看看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.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd "> <bean id="say1" class="fjs.Say"> <property name="name" value="fjs" /> </bean> <bean id="fjs" class="fjs.Fjs"> </bean> <aop:config> <aop:aspect ref="fjs"> <aop:pointcut id="haha" expression="execution(* fjs.Say.saying(..))" /> <aop:before pointcut-ref="haha" method="before" /> <aop:after pointcut-ref="haha" method="after" /> </aop:aspect> </aop:config> </beans>
上面还有一部分aop的配置,这里就忽略掉。。。
来看看bean是怎么从xml文件中获取配置信息的吧:
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); }
其实在这里就可以看到,基本上都是reader在做事情,这也是这篇文章要做的主要的事情,来分析一些这个XmlBeanDfinitionReader到底是怎么读取数据的,还是先来看看它的继承体系吧:
这里XmlBeanDefinitionReader的继承体系还是很简单的,相对于spring其他类型的继承来说,那么我们先来看看最顶层的接口都定义了一些什么方法吧:
public interface BeanDefinitionReader { //返回用于注册bean的factory BeanDefinitionRegistry getRegistry(); //返回用于加载resource的loader ResourceLoader getResourceLoader(); //获取class loader ClassLoader getBeanClassLoader(); /** * Return the BeanNameGenerator to use for anonymous beans * (without explicit bean name specified). */ //名字生成器,为那些没有名字的bean生成名字 BeanNameGenerator getBeanNameGenerator(); //从resource中获取bean的定义 int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; //从多个resource中获取bean的定义 int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException; //给定resource的位置,获取definition int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; //从多个地址获取definition int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException; }
这里最重要的方法就是loadDefinitions,用于从resource中读取bean的定义,
好了,那么看完了接口的定义,接下来来看看这个loadBeanDefinitions是怎么实现的吧:
//用于从resource中读取bean的definition public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { //将其组装生encoderesource,这个没啥太大的意思 return loadBeanDefinitions(new EncodedResource(resource)); }
好了,这部分代码啥意思都没有,接下来继续吧:
//加载bean的definition public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } //获取当前的线程变量,它是用于保存处理的resource的 //这里为什么要用线程变量来保存,不同的线程难道还要保存不同的resource了。。? Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); //保存当前的resource } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { //获取resource的inputStream InputStream inputStream = encodedResource.getResource().getInputStream(); try { //xml的input InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //读取definition return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
这部分代码其实也没啥太大的意思,无非就是将resource保存起来,然后获取inputstream,然后在调用别的方法继续处理,不过这里有个自己比较郁闷的地方,为什么要用线程变量来保存处理的resource,难道不同的线程要保存不同的resource了。。?搞不懂。。。以后再说吧,接下来继续看代码:
//读取xml文件中的beandefinition protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); Document doc = this.documentLoader.loadDocument( //获取xml的document inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware()); return registerBeanDefinitions(doc, resource); //读取并注册bean的信息 } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
从这部分代码开始就涉及到xml文件的内部了,说明准备开始解析xml文件了。。。不过内容并不多,可能最主要的就是要处理一些xml解析时候的异常吧,继续看代码:
//读取bean,并且还要注册 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //获取用于读取document的reader 其实是:DefaultBeanDefinitionDocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //将环境穿进去 documentReader.setEnvironment(this.getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount(); //之前已经注册的bean的数目 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //读取definition并注册 return getRegistry().getBeanDefinitionCount() - countBefore; }
这里开始准备读取xml文件,新建了一个document的reader,这个reader的类型其实是DefaultBeanDefinitionDocumentReader,然后真正的事情是这个reader来做的。。。那么接下来来看看这个reader做了啥吧:
//这里要读取并注册bean public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); //获取xml的root doRegisterBeanDefinitions(root); }
这代码没啥意思,我勒个去,接下来继续看吧:
protected void doRegisterBeanDefinitions(Element root) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { Assert.state(this.environment != null, "Environment must be set for evaluating profiles"); String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!this.environment.acceptsProfiles(specifiedProfiles)) { return; } } BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createHelper(this.readerContext, root, parent); preProcessXml(root); //前置处理,不过是空方法 parseBeanDefinitions(root, this.delegate); //解析xml文件,并将其注册到beanfactory postProcessXml(root); //后置处理 this.delegate = parent; }
这段代码之后就要开始进行真正的xml文件的解析,并要注册bean的信息了,
//用于解析当前root下面的node protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); //遍历当前的node 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)) { //解析node的数据,例如bean的id,class路径,property等 parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
这部分代码主要在做的事情就是遍历当前xml文件下的node,然后在解析这些node的数据,将其转化为bean的信息保存起来并注册信息:
//根据不同的node类型,做不同的处理 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)) { //这个是处理bea类型的node,也就是bean的定义 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); } }
这里我们就只看看如何处理bean的定义吧,其他的就不看了:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //用于将xml定义的bean的基本信息转化为spring定义的数据类型保存起来 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. //相当于是注册bean的definition //这里封装了说白了就是bean的id,class路径,scope,property等一些基本的信息, BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. //发送有bean注册的事件 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
那么到这里为止,这个bean的信息的读取就已经差不多了,将bean的数据转化为定义的类型保存起来,具体是什么样子的就不管他了,无非就是一些id,property什么的。。。
那么接下来来看看这个注册的过程是怎么样子的吧:
//在beanfactory上面注册bean public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); //获取bean的名字 //注册bean,name与definition对应起来,其实是将他们保存在了一个并发map里面 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. 注册别名,呵呵 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String aliase : aliases) { registry.registerAlias(beanName, aliase); } } }
这部分的代码就是用于将bean的名字(id)与bean的定义信息关联起来,然后保存在beanFactory的一个map里面,具体来看一下:
//用于注册bean的信息,将他们保存起来,key是id,value是definition 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); } } //加锁 synchronized (this.beanDefinitionMap) { //相当于是判断一下是否有重复的bean Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } } else { this.beanDefinitionNames.add(beanName); //将bean的名字保存起来 this.frozenBeanDefinitionNames = null; } this.beanDefinitionMap.put(beanName, beanDefinition); //将数据保存在map里面,这里用的是ConcurrentHashMap } resetBeanDefinition(beanName); }
上述的代码就是整个bean的注册的过程,说白了就是将这些数据保存到当前beanfactory的一个ConcurrentHashMap里面去。。
好了,那么到这里整个bean的定义的解析与注册过程就差不多了,下一篇文章来看看整个生成bean吧。。。
相关文章推荐
- 【spring】源码解析之 DefaultListableBeanFactory 和 XmlBeanDefinitionReader
- 分析spring源码第二篇:XmlBeanDefinitionReader
- XmlBeanDefinitionReader-----Spring源码解析 配置文件装载与解析
- spring源码分析(1)——AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner的初始化
- 解析Spring源码(4)--XmlBeanDefinitionReader
- spring 源码解读与设计详解:5 XmlBeanDefinitionReader与Resource
- Spring源码入门——XmlBeanDefinitionReader解析
- 解析Spring源码(3)---XmlBeanDefinitionReader(1)
- spring4.0 源码分析 DefaultListableBeanFactory和XmlBeanDefinitionReader(二)
- 解析Spring源码(9)--XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
- Spring源码阅读--BeanDefinition 在 IOC 容器中的注册
- 通过DefaultListableBeanFactory加载.xml配置文件学习Spring-IoC容器注册/加载bean的机制(源码走读)
- Spring初始化 - AbstractBeanDefinitionReader, XmlBeanDefinitionReader
- 理解spring的BeanDefinition和配置文件(原文标题:动态注册bean到spring容器)
- Spring源码学习--Spring配置文件解析BeanDefinitionReader(二)
- 7-spring源码3.2.18解读+spring技术内幕(关于BeanDefinition的注册)
- Spring 3.2 源码解析 -- XML bean 元素到 BeanDefinition 解析过程
- Spring源码分析之配置文件加载并注册Bean
- spring beans源码解读之--BeanFactory的注册
- Spring源码学习IOC(4):IoC容器解析Bean定义资源并注册解析后的Bean