这里先从最简单的一个Spring例子开始。

下面是Spring的context的配置

  1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:context="http://www.springframework.org/schema/context"
4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5 xsi:schemaLocation="http://www.springframework.org/schema/context
6 http://www.springframework.org/schema/context/spring-context.xsd
7 http://www.springframework.org/schema/beans
8 http://www.springframework.org/schema/beans/spring-beans.xsd">
9
10 <!-- 自动扫描web包 ,将带有注解的类 纳入spring容器管理 -->
11 <context:component-scan base-package="com.tuhooo.practice"></context:component-scan>
12 </beans>

一个简单的Service类,MyBean.java

  1 @Service
2 public class MyBean {
3 public void printStr() {
4 System.out.println("你好吗......");
5 }
6 }

main方法中获取bean,并调用MyBean.java中的方法。

  1 public class App {
2 public static void main( String[] args ) {
3 XmlBeanFactory applicationContext = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
4 MyBean myBean = (MyBean) applicationContext.getBean("myBean");
5 myBean.printStr();
6 }
7 }

当我在使用XmlBeanFactory这个类的时候,已经被标记为@deprecated,也就是这个类已经被抛弃了,但是并不妨碍它作为我们滴入口类进行分析呀。

XmlBeanFactory.java

  1 @Deprecated
2 @SuppressWarnings({"serial", "all"})
3 public class XmlBeanFactory extends DefaultListableBeanFactory {
4
5 /**
6 * 加载Bean定义的Reader
7 */
8 private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
9
10 /**
11 * 通过给定的可通过DOM解析的资源来创建一个XmlBeanFactory
12 * @param resource 用来加载bean定义的XML资源
13 * @throws BeansException 加载或者解析时候的错误
14 */
15 public XmlBeanFactory(Resource resource) throws BeansException {
16 this(resource, null);
17 }
18
19 /**
20 * 通过给定的可通过DOM解析的输入流来创建一个XmlBeanFactory
21 * @param resource 用来加载bean定义的XML资源
22 * @param parentBeanFactory 父代的bean factory
23 * @throws BeansException 加载或者解析时候的错误
24 */
25 public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
26 super(parentBeanFactory);
27 this.reader.loadBeanDefinitions(resource); /*这个是我们要进入, 并分析的方法*/
28 }
29 }

接下来看一下XmlBeanDefinitionReader中的loadBeanDefinitions(resource)的逻辑是怎样的。

XmlBeanDefinitionReader.loadBeanDefinitions(resource)

XmlBeanDefinitionReader的类图如下:

下面是XmlBeanDefinitionReader.java这个类截取的一部分:

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
/**
* Load bean definitions from the specified XML file.
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
} /**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
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());
} 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());
}
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();
}
}
}
}

可以对着这个时序图,看来下面的代码:

下面我们进入到XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)方法内部看一下。

这里EncodedResource验重的逻辑是:

public class EncodedResource implements InputStreamSource {
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof EncodedResource)) {
return false;
}
EncodedResource otherResource = (EncodedResource) other;
return (this.resource.equals(otherResource.resource) &&
ObjectUtils.nullSafeEquals(this.charset, otherResource.charset) &&
ObjectUtils.nullSafeEquals(this.encoding, otherResource.encoding));
}
}

这个loadBeanDefinitions方法还是很清晰的,你说像我这种制杖都看懂了,就想说一句,还有谁看不懂~~~

但仍有几个地方需要后续再跟进看一下:

1. 这里有个局部线程变量来存放正在加载的资源,this.resourceCurrentlyBeingLoaded.get(),可以我们看到线程在哪儿?

2. 你说搞encodedResource我理解,但是为什么在doLoadBeanDefinitions方法中既有inputSource,又有encodedResource,不会显得很多余么?

XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource, Resource)

  1 public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
2
3 /**
4 * Actually load bean definitions from the specified XML file.
5 * @param inputSource the SAX InputSource to read from
6 * @param resource the resource descriptor for the XML file
7 * @return the number of bean definitions found
8 * @throws BeanDefinitionStoreException in case of loading or parsing errors
9 * @see #doLoadDocument
10 * @see #registerBeanDefinitions
11 */
12 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
13 throws BeanDefinitionStoreException {
14 try {
15 Document doc = doLoadDocument(inputSource, resource);
16 return registerBeanDefinitions(doc, resource);
17 }
18 catch (BeanDefinitionStoreException ex) {
19 throw ex;
20 }
21 catch (SAXParseException ex) {
22 throw new XmlBeanDefinitionStoreException(resource.getDescription(),
23 "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
24 }
25 catch (SAXException ex) {
26 throw new XmlBeanDefinitionStoreException(resource.getDescription(),
27 "XML document from " + resource + " is invalid", ex);
28 }
29 catch (ParserConfigurationException ex) {
30 throw new BeanDefinitionStoreException(resource.getDescription(),
31 "Parser configuration exception parsing XML from " + resource, ex);
32 }
33 catch (IOException ex) {
34 throw new BeanDefinitionStoreException(resource.getDescription(),
35 "IOException parsing XML document from " + resource, ex);
36 }
37 catch (Throwable ex) {
38 throw new BeanDefinitionStoreException(resource.getDescription(),
39 "Unexpected exception parsing XML document from " + resource, ex);
40 }
41 }
42 }

最终把硬盘上的配置文件以Document的形式读到了内存中。

XmlBeanDefinitionReader.registerBeanDefinitions(Document, Resource)

  1 public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
2
3 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
4 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
5 int countBefore = getRegistry().getBeanDefinitionCount();
6 documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
7 return getRegistry().getBeanDefinitionCount() - countBefore;
8 }
9
10 /**
11 * Create the {@link BeanDefinitionDocumentReader} to use for actually
12 * reading bean definitions from an XML document.
13 * <p>The default implementation instantiates the specified "documentReaderClass".
14 * @see #setDocumentReaderClass
15 */
16 protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
17 return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
18 }
19
20 /**
21 * Create the {@link XmlReaderContext} to pass over to the document reader.
22 */
23 public XmlReaderContext createReaderContext(Resource resource) {
24 return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
25 this.sourceExtractor, this, getNamespaceHandlerResolver());
26 }
27 }
28

首先不看别的,直接看这段代码就行:

1. 构造一个BeanDefinitionDocumentReader,然后用它来注册BeanDefinition,它还需要一个读取时候的上下文;

2. 返回的是新加入Registry的BeanDefinition的个数

3. createBeanDefinitionDocumentReader的作用还没看太明白

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions

再去看一下doRegisterBeanDefinitions方法

真正处理的方法又流转到了parseBeanDefinitions中,但是我没明白delegate是干啥啊?

Resource的类图

public class ClassPathResource extends AbstractFileResolvingResource {

    /**
* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
* A leading slash will be removed, as the ClassLoader resource access
* methods will not accept it.
* <p>The thread context class loader will be used for
* loading the resource.
* @param path the absolute path within the class path
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
*/
public ClassPathResource(String path) {
this(path, (ClassLoader) null);
} /**
* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
* A leading slash will be removed, as the ClassLoader resource access
* methods will not accept it.
* @param path the absolute path within the classpath
* @param classLoader the class loader to load the resource with,
* or {@code null} for the thread context class loader
* @see ClassLoader#getResourceAsStream(String)
*/
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
}

在代码new ClassPathResource("beanFactoryTest.xml");中其实没有啥动作,就是对ClassPathResource的几个属性赋值了,尤其是类加载器。也就是说这个时候其实并没有读取文件内容。

Spring源码学习(一)资源加载的更多相关文章

  1. Mybatis源码学习之资源加载(六)

    类加载器简介 Java虚拟机中的类加载器(ClassLoader)负责加载来自文件系统.网络或其他来源的类文件.Java虚拟机中的类加载器默认使用的是双亲委派模式,如图所示,其中有三种默认使用的类加载 ...

  2. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  3. 【Spring源码分析】Bean加载流程概览(转)

    转载自:https://www.cnblogs.com/xrq730/p/6285358.html 代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. ...

  4. Spring源码分析:Bean加载流程概览及配置文件读取

    很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...

  5. 【Spring源码分析系列】加载Bean

    /** * Create a new XmlBeanFactory with the given input stream, * which must be parsable using DOM. * ...

  6. flutter源码学习笔记-图片加载流程

    本文基于1.12.13+hotfix.8版本源码分析. 0.大纲 Image ImageProvider 图片数据加载 ImageStream.ImageStreamCompleter 缓存池 Pai ...

  7. Spring源码解析-配置文件的加载

    spring是一个很有名的java开源框架,作为一名javaer还是有必要了解spring的设计原理和机制,beans.core.context作为spring的三个核心组件.而三个组件中最重要的就是 ...

  8. 五、spring源码阅读之ClassPathXmlApplicationContext加载beanFactory

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml&q ...

  9. Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件

    写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...

  10. Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

随机推荐

  1. java面试题之哨兵如何判断主服务器是否下线?

    通过流言协议来接收关于主服务器是否下线的信息,并使用投票协议来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器.

  2. [转] Makefile 基础 (1) —— Makefile 介绍

    该篇文章为转载,是对原作者系列文章的总汇加上标注. 支持原创,请移步陈浩大神博客:(最原始版本) http://blog.csdn.net/haoel/article/details/2886 我转自 ...

  3. La 3942 字符串+dp

    题目大意:一个字符串,可以分解成若干英语单词的连接(单词可以重复使用),求有多少种方法? #include<iostream> #include<cstdio> #includ ...

  4. javaweb学习总结(十)——HttpServletRequest对象(一)(转)

    (每天都会更新至少一篇以上,有兴趣的可以关注)转载自孤傲苍狼 一.HttpServletRequest介绍 HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器 ...

  5. JS中使用EL表达式方法与获取工程名字

    关键: 在js中使用el表达式一定要使用双引号      分两种情况 1. JS代码在JSP页面中, 这可以直接使用EL表达式. 如: <script type="text/javas ...

  6. 二、 java中的变量与数据类型及类型转换

    标识符:凡是可以自己命名的地方都叫标识符,如:类名.方法名.接口名... 1.标识符命名的规则: 由26个英文字母大小写,0-9,_或$组成,不遵守会报错. 不可以用数字开头. 不能使用关键字和保留字 ...

  7. Cryptography I 学习笔记 --- 绪论

    课程地址 1. 密码学可以用于保证消息传递的机密性(第三方不可能得到明文)与完整性(密文如果被第三方篡改,有手段可以检测到) 2. 数字签名(用私钥加密待签名文件的hash值) 3. 匿名通信(mix ...

  8. 2019南昌邀请赛 C. Angry FFF Party 大数矩阵快速幂+分类讨论

    题目链接 https://nanti.jisuanke.com/t/38222 题意: 定义函数: $$F(n)=\left\{\begin{aligned}1, \quad n=1,2 \\F(n- ...

  9. SpringCloud 分布式事务解决方案

    目录 TX-LCN分布式事务框架 TX-LCN分布式事务框架 随着互联化的蔓延,各种项目都逐渐向分布式服务做转换.如今微服务已经普遍存在,本地事务已经无法满足分布式的要求,由此分布式事务问题诞生. 分 ...

  10. 洛谷——P2737 [USACO4.1]麦香牛块Beef McNuggets

    https://www.luogu.org/problemnew/show/P2737 题目描述 农夫布朗的奶牛们正在进行斗争,因为它们听说麦当劳正在考虑引进一种新产品:麦香牛块.奶牛们正在想尽一切办 ...