文档章节

spring源码解析bean定义加载一

天河2018
 天河2018
发布于 05/10 19:52
字数 1486
阅读 24
收藏 2

前言

本文转自“天河聊技术”微信公众号

接着上次的spring上下文加载过程的源码解析

 

正文

上次主要介绍到刷新beanFactory,找到这个方法org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory的这行代码

@Override
   protected final void refreshBeanFactory() throws BeansException {
//    如果存在beanFactory
      if (hasBeanFactory()) {
//       销毁bean
         destroyBeans();
//       关闭beanFactory
         closeBeanFactory();
      }

bean销毁介绍完了,找到这行代码

closeBeanFactory();
@Override
protected final void closeBeanFactory() {
   synchronized (this.beanFactoryMonitor) {
      if (this.beanFactory != null) {
         this.beanFactory.setSerializationId(null);
         this.beanFactory = null;
      }
   }
}

操作beanFactory是同步的。

找到这行代码,创建banFactory

DefaultListableBeanFactory beanFactory = createBeanFactory();
protected DefaultListableBeanFactory createBeanFactory() {
   return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
@Nullable
   protected BeanFactory getInternalParentBeanFactory() {
//    如果父上下文是配置上下文
      return (getParent() instanceof ConfigurableApplicationContext) ?
            ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
   }

返回到这个方法

org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

customizeBeanFactory(beanFactory);
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
//    是否允许bean定义覆盖
      if (this.allowBeanDefinitionOverriding != null) {
         beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
      }
//    是否允许bean之间循环引用
      if (this.allowCircularReferences != null) {
         beanFactory.setAllowCircularReferences(this.allowCircularReferences);
      }
   }

返回到这个方法

org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

loadBeanDefinitions(beanFactory);

这一行代码,加载bean定义。

进入到这个方法

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)通过XmlBeanDefinitionReader加载bean定义。

@Override
   protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
      // Create a new XmlBeanDefinitionReader for the given BeanFactory.为给定的BeanFactory创建一个新的XmlBeanDefinitionReader。
      XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

      // Configure the bean definition reader with this context's
      // resource loading environment.使用该上下文的资源加载环境配置bean定义阅读器。
      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.允许子类提供读取器的自定义初始化,然后继续加载bean定义。
      initBeanDefinitionReader(beanDefinitionReader);
//    加载bean定义
      loadBeanDefinitions(beanDefinitionReader);
   }
//     加载资源
      beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
public ResourceEntityResolver(ResourceLoader resourceLoader) {
   super(resourceLoader.getClassLoader());
   this.resourceLoader = resourceLoader;
}

进入调用了父类构造器的方法

public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
//    创建DTD解析器
      this.dtdResolver = new BeansDtdResolver();
//    创建schema解析器
      this.schemaResolver = new PluggableSchemaResolver(classLoader);
   }

可以看到解析的是这个DTD

public class BeansDtdResolver implements EntityResolver {

// dtd解析
   private static final String DTD_EXTENSION = ".dtd";

   private static final String DTD_NAME = "spring-beans";
public PluggableSchemaResolver(@Nullable ClassLoader classLoader) {
   this.classLoader = classLoader;
   this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION;
}

org.springframework.beans.factory.xml.PluggableSchemaResolver

//  spring.schemas解析,这个路径下面的schemas都会被解析 META-INF/spring.schemas
   public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";

从这个地方加载的schema

找到这个方法

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)的这行代码

//     初始化bean定义
      initBeanDefinitionReader(beanDefinitionReader);
//     加载bean定义
      loadBeanDefinitions(beanDefinitionReader);
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}
//     获取配置文件路径
      String[] configLocations = getConfigLocations();
@Nullable
protected String[] getConfigLocations() {
   return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}

我们看下configLocations变量在哪里赋值的

public void setConfigLocation(String location) {
   setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}

我们看下location变量在哪里赋值的

找到了这个方法org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext

String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
   wac.setConfigLocation(configLocationParam);
}
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";

是从servlet容器启动时初始化的参数配置中获取的

返回到这个方法

org.springframework.context.support.AbstractRefreshableConfigApplicationContext#setConfigLocation

public void setConfigLocation(String location) {
   setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}

看下这个常量org.springframework.context.ConfigurableApplicationContext#CONFIG_LOCATION_DELIMITERS

String CONFIG_LOCATION_DELIMITERS = ",; \t\n";

bean定义配置文件可以是,或;或空格或换行符分开都可以。

进入到这个方法

org.springframework.context.support.AbstractRefreshableConfigApplicationContext#setConfigLocations

public void setConfigLocations(@Nullable String... locations) {
   if (locations != null) {
      Assert.noNullElements(locations, "Config locations must not be null");
      this.configLocations = new String[locations.length];
      for (int i = 0; i < locations.length; i++) {
         this.configLocations[i] = resolvePath(locations[i]).trim();
      }
   }
   else {
      this.configLocations = null;
   }
}
//           解析配置文件后把配置文件路径放到spring上下文中
            this.configLocations[i] = resolvePath(locations[i]).trim();

bean定义配置文件中如果有占位符就替换占位符

protected String resolvePath(String path) {
   return getEnvironment().resolveRequiredPlaceholders(path);
}
@Override
   public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
      if (this.strictHelper == null) {
         this.strictHelper = createPlaceholderHelper(false);
      }
//    替换占位符
      return doResolvePlaceholders(text, this.strictHelper);
   }

返回到这个方法

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)的这一行代码

//        加载bean定义
         reader.loadBeanDefinitions(configLocations);
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
   Assert.notNull(locations, "Location array must not be null");
   int counter = 0;
   for (String location : locations) {
      counter += loadBeanDefinitions(location);
   }
   return counter;
}

org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)

@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(location, null);
}

org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)

这段代码

//     从classpath下面加载
      if (resourceLoader instanceof ResourcePatternResolver) {
         // Resource pattern matching available.
         try {
            Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
            int loadCount = loadBeanDefinitions(resources);
            if (actualResources != null) {
               for (Resource resource : resources) {
                  actualResources.add(resource);
               }
            }
            if (logger.isDebugEnabled()) {
               logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
            }
            return loadCount;
         }
         catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                  "Could not resolve bean definition resource pattern [" + location + "]", ex);
         }
      }

找到这行代码

Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);

进入到这个方法

org.springframework.core.io.support.PathMatchingResourcePatternResolver#getResources

@Override
   public Resource[] getResources(String locationPattern) throws IOException {
      Assert.notNull(locationPattern, "Location pattern must not be null");
//    配置路径以classpath*:开头
      if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
         // a class path resource (multiple resources for same name possible)
         if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            // a class path resource pattern 从classpath*路径下匹配的的jar,war包等文件中查找配置文件
            return findPathMatchingResources(locationPattern);
         }
         else {
            // all class path resources with the given name 从classpath下面加载jar、war文件中的配置文件
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
         }
      }
      else {
         // Generally only look for a pattern after a prefix here,
         // and on Tomcat only after the "*/" separator for its "war:" protocol.
         int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
               locationPattern.indexOf(':') + 1);
         if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
            // a file pattern
            return findPathMatchingResources(locationPattern);
         }
         else {
            // a single resource with the given name
            return new Resource[] {getResourceLoader().getResource(locationPattern)};
         }
      }
   }

返回到这个方法

org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)

递归调用
            int loadCount = loadBeanDefinitions(resources);
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   Assert.notNull(resources, "Resource array must not be null");
   int counter = 0;
   for (Resource resource : resources) {
      counter += loadBeanDefinitions(resource);
   }
   return counter;
}

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)

@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(new EncodedResource(resource));
}

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)

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());
      }

//    resourcesCurrentlyBeingLoaded threadLocal保证线程安全
      Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
      if (currentResources == null) {
         currentResources = new HashSet<>(4);
         this.resourcesCurrentlyBeingLoaded.set(currentResources);
      }
      if (!currentResources.add(encodedResource)) {
         throw new BeanDefinitionStoreException(
               "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
      }
      try {
         InputStream inputStream = encodedResource.getResource().getInputStream();
         try {
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
               inputSource.setEncoding(encodedResource.getEncoding());
            }
//          加载bean定义
            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();
         }
      }
   }

找到这行代码

//           加载bean定义
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());

内容太多,下篇文章接着解析。

 

最后

本次介绍到这里,以上内容仅供参考。大家从spring源码解析第一篇文章开始看,更容易点,否则可读性可能不台好,因为spring源码体系太庞大,不太好归类,按每篇文章的篇幅不太好拆分。

© 著作权归作者所有

共有 人打赏支持
天河2018
粉丝 28
博文 56
码字总数 72707
作品 0
郑州
私信 提问
深入理解Spring源码(一)-IOC容器的定位,载入,注册

前言:Spring源码继承,嵌套层次非常多,读起来非常容易晕,小伙伴们在看文章的时候一定要跟着文章的思路自己去源码里点一点,看一看,并且多看几次。就会越来越清晰。下面开始正题 1.Spring...

Meet相识_bfa5
05/01
0
0
【Spring】BeanFactory解析bean详解

在该文中来讲讲Spring框架中BeanFactory解析bean的过程,该文之前在小编原文中有发表过,要看原文的可以直接点击原文查看,先来看一个在Spring中一个基本的bean定义与使用。 Spring配置文件r...

weknow
2017/04/05
0
0
【Spring】BeanFactory解析bean详解

本文是Spring源码分析中的一篇,来讲讲Spring框架中BeanFactory解析bean的过程,先来看一个在Spring中一个基本的bean定义与使用。(也可以点击公号查看) Spring配置文件root.xml定义如下: ...

weknow
09/09
0
0
Spring源码解析系列之IOC容器(一)

前言 实际上我所有的博客都是原来对原来印象笔记里笔记内容的加工,关于Spring源码自己已经解析了很多遍,但是时间长总是忘记,写一篇博客权当加强记忆,也算再次学习下大师们的设计思想,思...

后厂村老司机
06/02
0
0
spring源码解析上下文初始化ContextLoaderListener

前言 本文转自“天河聊技术”微信公众号 从本篇文章开始主要介绍spring源码解析相关的spring上下文初始化、bean定义解析、beanFactory创建、初始化、bean定义注册到beanFactory、bean实例化、...

天河2018
05/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

在Flutter中嵌入Native组件的解决方案

摘要: 引言 在漫长的从Native向Flutter过渡的混合工程时期,要想平滑地过渡,在Flutter中使用Native中较为完善的控件会是一个很好的选择。本文希望向大家介绍AndroidView的使用方式以及在此...

阿里云官方博客
38分钟前
1
0
aws S3 util demo

package com.example.demo;import com.amazonaws.AmazonClientException;import com.amazonaws.AmazonServiceException;import com.amazonaws.auth.BasicAWSCredentials;import co......

经常把天聊死的胖子
58分钟前
4
0
linux下查看cpu、memo、io、swap性能数据脚本

直接贴脚本: 1、cpu #!/bin/bashCurrentDate=`date -d today '+%Y%m%d'`CurrentTime=`date -d today '+%Y%m%d%H%M'`mytext="$CurrentTime\t`top -b -n 1 | grep Cpu\(s\......

郑加威
今天
5
0
MySQL之——查询重复记录、删除重复记录方法大全

MySQL之——查询重复记录、删除重复记录方法大全

安小乐
今天
2
0
spring容器启动,停止,关闭事件监听-ApplicationEvent

ApplicationEvent ApplicationEvent相当于一个事件,所有自定义事件都需要继承这个抽象类。在Eclipse中Ctrl+Shift+H调用类的层次结构列表,可以看到如下 Application下抽象子类ApplicationCo...

tantexian
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部