文档章节

spring源码解析bean定义加载一

天河2018
 天河2018
发布于 05/10 19:52
字数 1486
阅读 12
收藏 2
点赞 0
评论 0

前言

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

接着上次的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
粉丝 26
博文 47
码字总数 61729
作品 0
郑州
深入理解Spring源码(一)-IOC容器的定位,载入,注册

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

Meet相识_bfa5
05/01
0
0
Spring源码解析系列之IOC容器(一)

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

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

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

天河2018
05/10
0
0
向Spring大佬低头——大量源码流出解析

用Spring框架做了几年的开发,只停留在会用的阶段上,然而Spring的设计思想和原理确实一个巨大的宝库。大部分人仅仅知道怎么去配,或着加上什么属性就能达到什么效果,这些东西都可以通过查文...

Java团长17
07/11
0
0
Spring源码分析之Bean的解析

作者: 一字马胡 转载标志 【2017-12-28】 更新日志 前言 Spring源码分析是一个系列,源码是Spring 4.X,本系列主要分析Spring的代码执行流程,过于细节的内容将不会涉及太多,主要是为了理清...

疼丸李白
2017/12/28
0
0
Spring对注解(Annotation)处理源码分析1——扫描和读取Bean定义

1.从Spring2.0以后的版本中,Spring也引入了基于注解(Annotation)方式的配置,注解(Annotation)是JDK1.5中引入的一个新特性,用于简化Bean的配置,某些场合可以取代XML配置文件。开发人员对注...

李长春
2011/10/08
0
0
Dubbo源码-Dubbo是如何随心所欲自定义XML标签的

叨叨 今天考虑了很久要不要写这篇文章。 距离《Dubbo源码》系列的开篇到现在已经快两个月时间了。当时是想着工作上的RPC框架使用存在一些让人头疼的问题,就来看看Dubbo给出了一套什么样的解...

Jackie_Zheng
05/20
0
0
看看Spring的源码(一)——Bean加载过程

本文发表于我的独立博客:Geeekr 最近几天跟同事聊起Spring的一些问题,对一些地方有些疑问,趁这两天有点空,看看Spring的源码,了解下具体的实现细节。本文基于Spring 4.0.5版本。 首先Web...

gongzili
2014/08/18
0
7
Spring IOC 实现原理

Spring IOC 实现原理 IOC: Inversion of Control ,即 "控制反转" , 不是什么技术,而是一种思想。原先需要自行实例化的对象, 交给IOC容器去实现。那么控制反转,谁被控制? 谁被反转 ? 在...

起个名忒难
05/17
0
0
Spring Bean注册解析(一)

Spring是通过IoC容器对Bean进行管理的,而Bean的初始化主要分为两个过程:Bean的注册和Bean实例化。Bean的注册主要是指Spring通过读取配置文件获取各个bean的声明信息,并且对这些信息进行注...

张旭峰
05/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

tomcat虚拟路径的几种配置方法

tomcat虚拟路径的几种配置方法 一般我们都是直接引用webapps下面的web项目,如果我们要部署一个在其它地方的WEB项目,这就要在TOMCAT中设置虚拟路径了,Tomcat的加载web顺序是先加载 $Tomcat_ho...

Helios51
10分钟前
1
0
Mac 安装jupyter notebook的过程

MAC台式机 python:mac下自带Python 2.7.10 1.先升级了pip安装工具:sudo python -m pip install --upgrade --force pip 2.安装setuptools 工具:sudo pip install setuptools==33.1.1 3.安装......

火力全開
16分钟前
0
0
导航守卫解释与例子

“导航”表示路由正在发生改变。 正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。 记住...

tianyawhl
16分钟前
0
0
Java日志框架-logback配置文件多环境日志配置(开发、测试、生产)(原始解决方法)

说明:这种方式应该算是最通用的,原理是通过判断标签实现。 <!-- if-then form --> <if condition="some conditional expression"> <then> ... </then> </if> ......

浮躁的码农
30分钟前
1
0
FTP传输时的两种登录方式和区别

登录方式 匿名登录 用户名为: anonymous。 密码为:任何合法 email 地址。 授权登录 用户名为:用户在远程系统中的用户帐号。 密码为:用户在远程系统中的用户密码。 区别 匿名登录 只能访问...

寰宇01
31分钟前
0
0
plsql developer 配置监听(不安装oracle客户端)

plsql developer 配置监听(不安装oracle客户端)

微小宝
38分钟前
1
0
数据库(分库分表)中间件对比

本人的宗旨就是,能copy的,绝对不手写。 分区:对业务透明,分区只不过把存放数据的文件分成了许多小块,例如mysql中的一张表对应三个文件.MYD,MYI,frm。 根据一定的规则把数据文件(MYD)和索...

奔跑吧代码
42分钟前
2
0
Netty与Reactor模式详解

在学习Reactor模式之前,我们需要对“I/O的四种模型”以及“什么是I/O多路复用”进行简单的介绍,因为Reactor是一个使用了同步非阻塞的I/O多路复用机制的模式。 I/O的四种模型 I/0 操作 主要...

hutaishi
48分钟前
1
0
【2018.07.16学习笔记】【linux高级知识 20.16-20.19】

20.16/20.17 shell中的函数 20.18 shell中的数组 20.19 告警系统需求分析

lgsxp
54分钟前
0
0
用 D3.js 画一个手机专利关系图, 看看苹果,三星,微软间的专利纠葛

用 D3.js 画一个手机专利关系图, 看看苹果,三星,微软间的专利纠葛 前言 本文灵感来源于Mike Bostock 的一个 demo 页面 原 demo 基于 D3.js v3 开发, 笔者将其使用 D3.js v5 进行重写, 并改为...

ssthouse_hust
58分钟前
23
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部