Spring源码阅读之-bean的解析与注册
2017-07-15 14:12
686 查看
Spring源码阅读之-bean的解析与注册
接上,Spring初始化,refresh ApplicationContext时,通过refreshBeanFactory将原有的beanFactory注销,并重新读取Spring配置,并在配置中读取bean的定义。refreshBeanFactory()如下
/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. */ @Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans();//销毁Beans closeBeanFactory();//关闭BeanFactory,实际这一步是将BeanFactory类型的实例变量指向null } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory);//一些自定义的设置 loadBeanDefinitions(beanFactory);//加载bean的定义 synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
其
loadBeanDefinitions(beanFactory)顾名思义就是加载Bean定义的主要实现,其方法签名为
org.springframework.context.support.AbstractRefreshableApplicationContext protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException
他是一个抽象方法由具体的子类来实现,看看这个这个方法的子类实现都有那几个。
因为我是由
org.springframework.context.support.ClassPathXmlApplicationContext这个类一路跟过来的,其实现类为AbstractXmlApplicationContext,方法实现如下:
/** * Loads the bean definitions via an XmlBeanDefinitionReader. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see #initBeanDefinitionReader * @see #loadBeanDefinitions */ @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
从代码上看,其初始化了一个
org.springframework.beans.factory.xml.XmlBeanDefinitionReader对象,该类实现自
org.springframework.beans.factory.support.BeanDefinitionReader,BeanDefinitionReader依名称进行ChineseEnglish翻译,其作用为Bean定义读取器。该接口约定了几个行为:
主要看loadBeanDefinitions,几个方法,从参数列表中可得知,Bean定义的来源可以是一个Resource对象,也可以是一个String类型的文件地址,该方法返回int类型,返回值为找到的Bean的个数。方法签名如下(取loadBeanDefinitions(Resource resource)说明):
/** * Load bean definitions from the specified resource. * @param resource the resource descriptor * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of loading or parsing errors */ int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
org.springframework.beans.factory.xml.XmlBeanDefinitionReader继承自
org.springframework.beans.factory.support.AbstractBeanDefinitionReader该抽象类中定义了如下几个实例变量
private final BeanDefinitionRegistry registry;//bean定义的注册器 private ResourceLoader resourceLoader; //资源加载器 private ClassLoader beanClassLoader; //类加载器 private Environment environment;//Spring环境 private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator(); //bean的名称生成器
其主要是registry属性,该域声明为final,子类不可继承,即子类只能定义加载Bean的方式方法,同样获取注册器和BeanFactory的方法也都声明为final,定义如下
public final BeanDefinitionRegistry getBeanFactory() { return this.registry; } @Override public final BeanDefinitionRegistry getRegistry() { return this.registry; }
回到初始化XmlBeanDefinitionReader的操作
//初始化时传入注册器即当前的BeanFactory,也就是说将Bean都注册到当前的bean工厂 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); //加载Bean的定义 loadBeanDefinitions(beanDefinitionReader);
加载Bean的定义主要是在
loadBeanDefinitions(beanDefinitionReader);方法中完成的,其内部获得初始化ApplicationContext时的配置文件路径封装Resource对象,并调用beanDefinitionReader的loadBeanDefinitions(Resource… resources)方法,其内部循环调用loadBeanDefinitions(Resource resource)方法来分别解析具体的配置文件,该方法内部调用
doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法将Resource解析为Xml DOC并调用
registerBeanDefinitions(Document doc, Resource resource)方法,进行进一步的操作。
/** * Register the bean definitions contained in the given DOM document. * Called by {@code loadBeanDefinitions}. * <p>Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. * @param doc the DOM document * @param resource the resource descriptor (for context information) * @return the number of bean definitions found * @throws BeanDefinitionStoreException in case of parsing errors * @see #loadBeanDefinitions * @see #setDocumentReaderClass * @see BeanDefinitionDocumentReader#registerBeanDefinitions */ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
在该方法内部中,发现Spring又定义了一个十分可爱的Reader,其作为与BeanDefinitionReader又不相同,该BeanDefinitionDocumentReader 为bean定义文档读取器,该BeanDefinitionDocumentReader是一个接口,他定义的行为如下:
** * SPI for parsing an XML document that contains Spring bean definitions. * Used by {@link XmlBeanDefinitionReader} for actually parsing a DOM document. * * <p>Instantiated per document to parse: implementations can hold * state in instance variables during the execution of the * {@code registerBeanDefinitions} method — for example, global * settings that are defined for all bean definitions in the document. * * @author Juergen Hoeller * @author Rob Harrop * @since 18.12.2003 * @see XmlBeanDefinitionReader#setDocumentReaderClass */ public interface BeanDefinitionDocumentReader { /** * Read bean definitions from the given DOM document and * register them with the registry in the given reader context. * @param doc the DOM document 代指 Spring 的配置文件信息,通过 刚刚的BeanDefinitionReader解析Resrouce实例的过程得到。 * @param readerContext the current context of the reader 主要包含了Resource和BeanDefinitionReader,和一些事件处理 * (includes the target registry and the resource being parsed) * @throws BeanDefinitionStoreException in case of parsing errors */ void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) throws BeanDefinitionStoreException; }
他只有一个实现类
org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader,所以在此实现类中观察代码
/** * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). * <p>Opens a DOM Document; then initializes the default settings * specified at the {@code <beans/>} level; then parses the contained bean definitions. */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); Element root = doc.getDocumentElement(); //获得文档的根节点,即<beans>标签 doRegisterBeanDefinitions(root); //* 继续Doc的解析并注册bean的定义 }
关于 doRegisterBeanDefinitions,先看其代码:
/** * Register each bean definition within the given root {@code <beans/>} element. * 注册在给定的root节点中的每一个BeanDefinition */ protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. //类名最后一个单词(委托)即BeanDefinitionDocumentReader本身并不具备解析XML节点的能力,而是将该操作交给BeanDefinitionParserDelegate来代为完成 BeanDefinitionParserDelegate parent = this.delegate; //创建委托对象 this.delegate = createDelegate(getReaderContext(), root, parent); //判断是否是默认命名空间的节点(因为是解析bean的功能,其默认的命名空间URI为‘http://www.springframework.org/schema/beans’,即只处理beans-3.0.xsd中定义的节点) 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.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //空方法 preProcessXml(root); //解析Bean的定义 parseBeanDefinitions(root, this.delegate); //空方法 postProcessXml(root); this.delegate = parent; }
关于 doRegisterBeanDefinitions,该方法的主要作用有:创建 BeanDefinitionParserDelegate 对象,用于将 Document 的内容转成 BeanDefinition 实例,也就是上面提到的解析过程
下面开始通过遍历取得 beans 元素的所有子节点,其逻辑在
parseBeanDefinitions(root, this.delegate);中。
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 验证Doc文件的命名空间 if (delegate.isDefaultNamespace(root)) { //又来判断 //获得根元素(<beans>)的所有子元素 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)) { //解析<beans>子元素的内容 parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); //否则解析自定义节点 } } } } else { //按自定义节点解析 delegate.parseCustomElement(root); } }
通过以上代码发现,parseBeanDefinitions在拿到了标签的子元素后,开始解析子元素标签,常见的标签有 import,alias,bean,beans 等(详见spring-beans.xsd或在线doc,或delegate类的常量)。
这里delegate.parseCustomElement(ele); 是另一个处理分支,例如aop和web开发配置文件中常见的annotion这类标签即是通过该方法解析的,暂不探究,另起炉灶说明
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>标签 // recurse doRegisterBeanDefinitions(ele); } }
parseDefaultElement方法中判断各标签,并根据标签不同调用不同的方法解析标签(其中import即是获取import文件路径,将上边叙述的过程又走了一遍,不再表述),我们目前只关注beans->bean的解析过程
上面提到 bean 标签的具体解析工作是委托给 BeanDefinitionParserDelegate 类来完成的,其解析的代码如下
/** * Process the given bean element, parsing the bean definition * and registering it with the registry. */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //将解析结果添加BeanDefinitionHolder对象,该对象存储BeanDefinition基本信息(内部持有一private final BeanDefinition beanDefinition;),具体解析过程就是一个解析element的过程,将解析结果封装到一个BeanDefinition中,并交给Holder持有 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)); } }
可以发现
processBeanDefinition在完成解析取得 BeanDefinition(被添加进了 BeanDefinitionHolder ) 对象之后利用 BeanDefinitionRegistry 完成注册过程(BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());)。
BeanDefinitionHolder bdHolder 是如何返回的?其解析过程如下
/** * Parses the supplied {@code <bean>} element. May return {@code null} * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */ public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { //获得<bean>节点的Id属性 String id = ele.getAttribute(ID_ATTRIBUTE); //获得<bean>节点的name属性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<String>(); //判断name属性是否为NULL if (StringUtils.hasLength(nameAttr)) { 这个函数用于将给定的字符串按照给定的分隔符分隔成字符串数组,这里就是 把nameAttr按照“,;”分隔开 //这个函数用于将给定的字符串按照给定的分隔符分隔成字符串数组,这里就是 把nameAttr按照“,;”分隔开 String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); // 把结果添加进别名集合 aliases.addAll(Arrays.asList(nameArr)); } //beanName默认为id属性值 String beanName = id; //如果id属性为空,且别名集合中不为空 if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { //取别名集合的第一个元素当作 id ,并将其从别名集合当中移除 beanName = aliases.remove(0); if (logger.isDebugEnabled()) { //日志 logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { //刚刚流程过来,此处确为null // 检查beanName和alias是否唯一 checkNameUniqueness(beanName, aliases, ele); } // 解析<bean>标签的相关属性,并将其添加进beanDefinition返回(主要处理属性如class,parent等相关属性,详细可阅读源码) AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { //自动生成BeanName,BeanDefinitionReader beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. //取得bean的完整类名 String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { //如果类名不为null,并且自动生成的BeanName长度比类名长,并且类名在工厂中还没有作为BeanName使用,那么别名集合中添加bean的类名 aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } //将别名集合转为数组 String[] aliasesArray = StringUtils.toStringArray(aliases); //返回封装的holder return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
至此一个Holder返回完成,其内部持有BeanDefinition对象,beanName以及BeanName映射的别名数组。我们拿到Bean的定义之后,要干大事,即将bean注册到Bean工厂中,这一部是如何做到的呢?
Bean的注册
回到org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader在我们利用委托完成节点解析并返回Holder后,调用
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());方法完成bean的注册,其代码如下,因为之前已经分析过BeanDefinitionHolder的初始化过程,此处还算较好理解
/** * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with * @throws BeanDefinitionStoreException if registration failed */ public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. // 在holder中获取Bean的名称 String beanName = definitionHolder.getBeanName(); // 注册BeanDefinition到bean工厂(这里看到是一个BeanDefinitionRegistry对象,其实最终是一个DefaultListableBeanFactory对象) registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 注册Bean的别名 // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
BeanDefinitionRegistry和DefaultListableBeanFactory关系如下,其实最终还是注册到BeanFactory中了:
总结
总结来说,ApplicationContext将解析配置文件的工作交给BeanDefinitionReader,然后BeanDefinitionReader将配置文件读取为xml的Document文档之后,又委托给BeanDefinitionDocumentReader ,BeanDefinitionDocumentReader将具体节点的解析工作交给BeanDefinitionParserDelegate 类来完成,并将Bean的element定义转成BeanDefinition对象,然后交给BeanDefinitionHolder持有总体流程:
1.创建BeanDefinitionReader并传入beanFactory作为注册器,读取配置文件,将配置文件转Document,并将自己放入ReaderContext中作为BeanDefinitionDocumentReader 的上下文
2.使用BeanDefinitionDocumentReader 读取Document全文,根据标签名处理不同的标签,并将bean标签交由BeanDefinitionParserDelegate 处理
3.BeanDefinitionParserDelegate 处理bean标签,读取各属性并处理别名等情况,将其封装为BeanDefinition对象,将处理结果交给BeanDefinitionHolder持有
4.在Holder对象中取得beanName和alias[],还有BeanDefinition对象,从BeanDefinitionDocumentReader 对象的上下文中取出BeanFactory对象,并将对象注册给beanfactory中
以上调用链,只处理spring-beans3.0.xsd的标签,其他自定义标签spring提供了其他方式处理
*工厂对bean的维护,另开疆拓土,此篇完事,全篇流水账。只做学习笔记之用。
最后贴个别人的图
相关文章推荐
- Spring源码-IOC容器(二)-Bean的定位解析注册
- Spring源码学习IOC(4):IoC容器解析Bean定义资源并注册解析后的Bean
- 【Spring源码从入门到精通】(八)解析及注册BeanDefinitions
- spring源码分析(2)——Bean 定义的解析与Bean的注册
- 【spring源码分析】--Bean的解析与注册
- spring源码阅读(1)bean解析
- Spring源码阅读--BeanDefinition 在 IOC 容器中的注册
- Spring Bean加载源码解析
- Spring源码阅读——简单模拟Spring的控制反转IOC和依赖注入(Bean的加载和获取)
- Spring bean定义解析源码分析
- Spring源码阅读之DefaultListableBeanFactory系列-DefaultSingletonBeanRegistry
- spring bean 标签的解析和注册
- Spring源码之旅(3)_BeanDefinition的解析与注册
- Spring 源码深入解析(1)之bean容器的基本实现(二)
- Spring源码解析——如何阅读源码
- spring源码解析之bean的子元素
- Spring源码解析——如何阅读源码
- 【spring源码学习】spring的IOC容器之自定义xml配置标签扩展namspaceHandler向IOC容器中注册bean
- Spring源码阅读——Bean的加载和获取过程
- Spring源码解析之Bean的加载