spring 源码学习笔记(一)—— spring ioc 之加载XML转换为BeanDefinition

欢迎访问我的个人博客休息的风

spring ioc容器的核心类是AbstractApplicationContext,入口方法是refresh。这个方法是个模板方法,定义了加载到容器的全部过程。本篇博客将分析,spring将xml配置文件加载到内存的一个过程。(著名的dubbo分布式框架也利用了spring加载xml的机制,定制自己的xml解析器将对象接入到ioc容器中。)大致过程为:创建beanFactory用于存放转换后的信息->读取文件到输入流中->读取输入流里的数据,用NamespaceHandler里注册的解析器处理返回BeanDefinition->将BeanDefinition保存到DefaultListableBeanFactory的beanDefinitionMap中。

以下是整个调用过程的时序图,最终xml在spring中会放到一个以名称为key,beanDefinition为value的ConcurrentHashMap中。将根据这个时序图,逐步分析对应的源码,希望能把spring加载xml这一过程解释清楚(图象看不清可右击在新页签中查看)

spring 源码学习笔记(一)—— spring ioc 之加载XML转换为BeanDefinition


首先,在AbstractApplicationContext.refresh方法中,作为spring初始化的全部过程定义。obtainFreshBeanFactory这个方法是开始解析xml文件的入口

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      //省略很多代码。。。。。


在进入到obtainFreshBeanFactory方法里,可以看到,这是创建DefaultListableBeanFactory的入口,这个DefaultListableBeanFactory可以当作是存放ioc容器的地方。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   //在AbstractRefreshableApplicationContext中去创建DefaultListableBeanFactory类
   //这个DefaultListableBeanFactory可以当作是存放ioc容器的地方
   refreshBeanFactory();
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
   }
   return beanFactory;
}

具体如何创建DefaultListableBeanFactory的,在子类AbstractRefreshableApplicationContext中实现

protected final void refreshBeanFactory() throws BeansException {
   //是否存在bean工厂类,这个工厂类指的是DefaultListableBeanFactory
   if (hasBeanFactory()) {
      //销毁工厂类里面的bean
      destroyBeans();
      //工厂类设置为null
      closeBeanFactory();
   }
   try {
      //创建DefaultListableBeanFactory对象
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      //设置工厂类一些参数值
      customizeBeanFactory(beanFactory);
      //真正开始加载xml,转换为beandefinition的入口
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }

在AbstractXmlApplicationContext会去调用loadBeanDefinitions,去读取还是交由XmlBeanDefinitionReader类处理

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   //以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);
   //交由beanDefinitionReader去读取文件内容
   loadBeanDefinitions(beanDefinitionReader);
}

XmlBeanDefinitionReader类里,会去把文件转换为文件输入流。

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   //省略一些代码
   try {
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         InputSource inputSource = new InputSource(inputStream);
         if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally {
         inputStream.close();
      }
   }

接下来读取输入流里面的信息,用BeanDefinitionDocumentReaderDefaultBeanDefinitionDocumentReader的registerBeanDefinitions方去去读取。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   //创建文件Document读取器
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   //这里的getRegistry()其实也是获取DefaultListableBeanFactory
   int countBefore = getRegistry().getBeanDefinitionCount();
   //用文件Document读取器读取,并把解析的BeanDefinition放到DefaultListableBeanFactory里
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

在DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法里,会去创建BeanDefinitionParserDelegate对象。需要特别关注这个类,因为这个类会去调用对应的NamespaceHandler里面注册的解析器去解析。(dubbo里面DubboNamespaceHandler就是在这里与spring结合在一起的)

protected void doRegisterBeanDefinitions(Element root) {
   
   BeanDefinitionParserDelegate parent = this.delegate;
   this.delegate = createDelegate(getReaderContext(), root, parent);

   //省略一些代码。。

   preProcessXml(root);
   //解析xml配置文件里的配置,转换为beanDefinition,
   // 并用BeanDefinitionReaderUtils注册到DefaultListableBeanFactory里
   parseBeanDefinitions(root, this.delegate);
   postProcessXml(root);

   this.delegate = parent;
}

在解析xml的时候,会委托BeanDefinitionParserDelegate去做,这里真正处理xml里面的配置具体内容的,是注册在NamespaceHandler里面的Parser解析器。

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
   String namespaceUri = getNamespaceURI(ele);
   if (namespaceUri == null) {
      return null;
   }
   //这里根据namespaceUri去获取对应的NamespaceHandler去做处理
   NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
   if (handler == null) {
      error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
      return null;
   }
   return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}


NamespaceHandlerSupport类有对NamespaceHandler抽象的统一处理。通过NamespaceHandlerSupport.findParserForElement方法找到之前注册到NamespaceHandler的BeanDefinitionParser

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

   @Override
   public void init() {
      registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
      registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
      registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
      registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
      registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
      registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
      registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
      registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
   }

}

解析之后,将xml里面的数据处理成beanDefinition后,统一通过BeanDefinitionReaderUtils.registerBeanDefinition注册到DefaultListableBeanFactory的beanDefinitionMap中。

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // Register bean definition under primary name.
   String beanName = definitionHolder.getBeanName();
   //这个registry就是DefaultListableBeanFactory
   //放入到beanDefinitionMap中
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // Register aliases for bean name, if any.
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

至此,就将xml配置文件里的配置信息转换为DefaultListableBeanFactory的beanDefinitionMap里面的信息。

开篇讲到的整个过程为:1、创建beanFactory用于存放转换后的信息->2、读取文件到输入流中->3、读取输入流里的数据,用NamespaceHandler里注册的解析器处理返回BeanDefinition->4、将BeanDefinition保存到DefaultListableBeanFactory的beanDefinitionMap中。再回顾下整个代码过程,与开篇的对应如下:

1、在AbstractApplicationContext.refresh这个方法里,作为创建beanFactory的入口。

2、XmlBeanDefinitionReader.loadBeanDefinitions,会去把文件转换为文件输入流。

3、BeanDefinitionParserDelegate.parseCustomElement这个类的方法会去调用对应的NamespaceHandler里面注册的解析器去解析

4、BeanDefinitionReaderUtils.registerBeanDefinition这个方法里,会把BeanDefinition注册到BeanFactory中