Spring IOC源码分析-obtainFreshBeanFactory(二)

524 阅读5分钟

这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战

前言

Tell the subclass to refresh the internal bean factory.

告诉子类启动refreshBeanFactory()方法,Bean定义资源文件的载入从子类的refreshBeanFactory()方法启动

Spring 源码分析系列

源码分析obtainFreshBeanFactory

前面分析了

  1. 入口ClassPathXmlApplicationContext 构造函数
  2. 获取资源加载器AbstractApplicationContext
  3. 解析配置路径 setConfigLocations()
  4. 容器启动流程 refresh()

第一步prepareRefresh 比较简单这里不再赘述, obtainFreshBeanFactory 是一个比较核心的方法。

该方法的主要作用是将bean定义beandefinition加载到BeanFactory中。

  1. 该方法会解析所有 Spring 配置文件(application-**.xml),将所有 Spring 配置文件中的 bean 定义封装成 BeanDefinition,加载到 BeanFactory 中。

  2. 常见的,如果解析到<context:component-scan base-package="" /> 注解时,会扫描 base-package 指定的目录,将该目录下使用指定注解(@Controller、@Service、@Component、@Repository)的配置也同样封装成 BeanDefinition,加载到 BeanFactory 中。

三个重要的缓存:

  • beanDefinitionNames缓存:所有被加载到 BeanFactory 中的bean的beanName 集合。
  • beanDefinitionMap缓存:所有被加载到 BeanFactory 中的bean的beanName和 BeanDefinition 映射。
  • aliasMap缓存:所有被加载到 BeanFactory 中的bean的beanName和别名映射。

AbstractApplicationContext#obtainFreshBeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   // 1. 刷新BeanFactory,有子类 AbstractRefreshableApplicationContext 实现
   refreshBeanFactory();
   // 2. 拿到刷新后的BeanFactory
   return getBeanFactory();
}

1.1 AbstractRefreshableApplicationContext#refreshBeanFactory()

刷新BeanFactory,有子类 AbstractRefreshableApplicationContext 实现

@Override
protected final void refreshBeanFactory() throws BeansException {
   // 1. 判断是否存在BeanFactory, 如果存在先销毁,关闭该BeanFactory
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      // 2. 创建新的BeanFactory
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      // 3. 加载bean定义, 由XmlWebApplicationConetxt实现
      loadBeanDefinitions(beanFactory);
      this.beanFactory = beanFactory;
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

1.2 XmlWebApplicationContext#loadBeanDefinitions

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   // 1. 为BeanFactory 创建 XmlBeanDefinitionReader
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   // 2. 使用此上下文资源加载环境配置
   beanDefinitionReader.setEnvironment(getEnvironment());
   // setResourceLoader 为 XmlBeanDefinitionReader
   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);
   // 3. 加载bean定义
   loadBeanDefinitions(beanDefinitionReader);
}

1.3 XmlWebApplicationContext#loadBeanDefinitions

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
   // 1. 获取配置文件路径
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      for (String configLocation : configLocations) {
         // 2. 根据配置文件路径加载Bean定义
         reader.loadBeanDefinitions(configLocation);
      }
   }
}

1.4 AbstractBeanDefinitionReader#loadBeanDefinitions

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
   // 1. 获取 resourceLoader, 这边为 XmlWebApplicationContext
   ResourceLoader resourceLoader = getResourceLoader();
   if (resourceLoader == null) {
      throw new BeanDefinitionStoreException(
            "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
   }

   // 2. 判断 resourceLoader 是否为 ResourcePatternResolver 的实例
   if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      try {
         // 2.1 根据路径查找到该路径下面所有符合的配置文件,并封装成Resource
         Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
         // 2.2 根据Resource定义,加载Bean定义
         int count = loadBeanDefinitions(resources);
         if (actualResources != null) {
            Collections.addAll(actualResources, resources);
         }
         if (logger.isTraceEnabled()) {
            logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
         }
         return count;
      }
      catch (IOException ex) {
         throw new BeanDefinitionStoreException(
               "Could not resolve bean definition resource pattern [" + location + "]", ex);
      }
   }
   else {
      // Can only load single resources by absolute URL.
      // 3. 只能通过绝对URL路径加载单个资源
      Resource resource = resourceLoader.getResource(location);
      // 3.1 根据 resource 加载bean定义
      int count = loadBeanDefinitions(resource);
      if (actualResources != null) {
         actualResources.add(resource);
      }
      if (logger.isTraceEnabled()) {
         logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
      }
      return count;
   }
}

基本流程

  1. 获取 resourceLoader
  2. 判断 resourceLoader 是否为 ResourcePatternResolver 的实例
    2.1 根据路径拿到该路径下所有符合的配置文件,并封装成 Resource
    2.2 根据 Resource,加载 bean 定义

1.5 AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)

@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   Assert.notNull(resources, "Resource array must not be null");
   int count = 0;
   // 1. 遍历所有的Resource
   for (Resource resource : resources) {
      // 2. 根据 Resource 加载Bean定义,XmlBeanDefinitionReader实现
      count += loadBeanDefinitions(resource);
   }
   return count;
}

根据 Resource 加载 bean 定义,由 XmlBeanDefinitionReader 实现

1.6 XmlBeanDefinitionReader#loadBeanDefinitions

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isTraceEnabled()) {
      logger.trace("Loading XML bean definitions from " + encodedResource);
   }

   // 1. 当前正在加载的EncodedResource
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

   // 2. 将当前的 encodedResource 添加到 currentResources
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
   // 3. 拿到 encodedResource 的 inputStream
   try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
      // 4. 将 inputStream 封装成 org.xml.sax.InputSource
      InputSource inputSource = new InputSource(inputStream);
      if (encodedResource.getEncoding() != null) {
         inputSource.setEncoding(encodedResource.getEncoding());
      }
      // 5. 加载bean定义, 真正加载bean定义的do方法
      return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   }
   finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove();
      }
   }
}

1.6 AbstractBeanDefinitionReader#doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {

   try {
      // 1. 根据imputResource和resource 加载XML文件并封装Document
      Document doc = doLoadDocument(inputSource, resource);
      // 2. 根据返回的Document 注册Bean信息
      int count = registerBeanDefinitions(doc, resource);
      if (logger.isDebugEnabled()) {
         logger.debug("Loaded " + count + " bean definitions from " + resource);
      }
      return count;
   }
   catch (BeanDefinitionStoreException ex) {
      // ....
   }
}

1.7 AbstractBeanDefinitionReader#doLoadDocument

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
// 1. 获取XML文件的验证方式
// 2. 加载XML文件并得到 Document
   return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
         getValidationModeForResource(resource), isNamespaceAware());
}

1.7.1 getValidationModeForResource

protected int getValidationModeForResource(Resource resource) {
   int validationModeToUse = getValidationMode();
   if (validationModeToUse != VALIDATION_AUTO) {
      return validationModeToUse;
   }
   int detectedMode = detectValidationMode(resource);
   if (detectedMode != VALIDATION_AUTO) {
      return detectedMode;
   }
   // Hmm, we didn't get a clear indication... Let's assume XSD,
   // since apparently no DTD declaration has been found up until
   // detection stopped (before finding the document's root tag).
   return VALIDATION_XSD;
}

1.7.2 loadDocument

@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
      ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
   // 1. 创建DocumentBuilderFactory
   DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
   if (logger.isTraceEnabled()) {
      logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
   }
   // 2. 通过 DocumentBuilderFactory 创建 DocumentBuilder
   DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
   // 3. 使用DocumentBuilder解析并返回Document对象
   return builder.parse(inputSource);
}

1.8 registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   // 1. 使用DefaultBeanDefinitionDocumentReader 实例化 BeanDefinitionDocumentReader
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   // 2. 记录统计BeanDifinition加载的个数
   int countBefore = getRegistry().getBeanDefinitionCount();
   // 3. createReaderContext, 根据resource创建XmlReaderContext
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

1.9 createReaderContext

  1. 根据 resource 构建一个 XmlReaderContext,用于存放解析时会用到的一些上下文信
  2. 其中 namespaceHandlerResolver 会创建默认的 DefaultNamespaceHandlerResolver,DefaultNamespaceHandlerResolver的handlerMappingsLocation 属性会使用默认的值 “META-INF/spring.handlers”,并且这边有个重要的属性 handlerMappings,handlerMappings 用于存放命名空间和该命名空间handler类的映射.
public XmlReaderContext createReaderContext(Resource resource) {
   return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
         this.sourceExtractor, this, getNamespaceHandlerResolver());
}

public NamespaceHandlerResolver getNamespaceHandlerResolver() {
   if (this.namespaceHandlerResolver == null) {
      this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
   }
   return this.namespaceHandlerResolver;
}


protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
   ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
   return new DefaultNamespaceHandlerResolver(cl);
}

1.10 registerBeanDefinitions

protected void doRegisterBeanDefinitions(Element root) {
   
   BeanDefinitionParserDelegate parent = this.delegate;
   // 构件BeanDefinitionParserDelegate
   this.delegate = createDelegate(getReaderContext(), root, parent);
   // 1. 校验Root节点命名空间是否是默认的命名空间
   if (this.delegate.isDefaultNamespace(root)) {
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         // We cannot use Profiles.of(...) since profile expressions are not supported
         // in XML config. See SPR-12458 for details.
         // 2. 校验profile
         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;
}