前言
- 还是Spring的经典使用代码
public class SpringClient { public static void main(String[] args) { Resource resource = new ClassPathResource("applicationContext.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(factory); beanDefinitionReader.loadBeanDefinitions(resource); Student student = (Student) factory.getBean("student"); System.out.println(student.getName() +"\n" + student.getAge()); } } - 前面讲解过Resource和BeanFactory的创建,但是还需要有个组件来将它们关联起来。
本篇将会讲解到的内容
- xml的解析涉及到的类。
- xml解析的具体步骤。
- Spring是如何将现实的内容进行抽象化,变成对应的类的,也就是面向对象。
- 比如,BeanDefinitionReader、Environment、Document、BeanDefinition等等,这些接口都是哪些事物的抽象。
- 如果没有手动配置bean的id,Spring会自动生成独一无二的id。
- Resource和BeanFactory是如何关联的。
- bean工厂起到哪些功能。
- 最主要的就是Spring bean注册的具体过程。
- xml解析完后的信息存储在哪个位置。
- 涉及到哪些设计模式。
BeanDefinitionReader
- 你可以将它称之为配置信息读取器。
- 是bean 定义读取器的简单接口。 使用资源和字符串位置参数指定加载方法。具体的 bean定义读取器当然可以为 bean 定义添加额外的加载和注册方法,特定于它们的 bean 定义格式。
- 它的实现类
XmlBeanDefinitionReader
它是BeanDefinitionReader的实现类
- 构造方法
从构造方法看
BeanDefinitionRegistry和BeanFactory想必有些血缘关系。 下面介绍它的spuer()
AbstractBeanDefinitionReader
- 为给定的 bean 工厂创建一个新的 AbstractBeanDefinitionReader。
- 如果传入的 bean factory 不仅实现了 BeanDefinitionRegistry 接口,还实现了 ResourceLoader 接口,那么它也会被用作默认的 ResourceLoader。这通常是org.springframework.context.ApplicationContext的实现类。
- 如果给定一个普通的 BeanDefinitionRegistry,默认的 ResourceLoader 将是一个PathMatchingResourcePatternResolver 。
- 如果传入的 bean 工厂也实现了EnvironmentCapable它的环境将被这个读者使用。否则,reader将初始化并使用StandardEnvironment 。所有的 ApplicationContext 实现都是 EnvironmentCapable,而普通的 BeanFactory不是。
- 看看构造方法的代码
- 策略模式可以用来解决大量if...else的情况。
- 策略模式(Strategy Pattern),定义了一系列的算法,将每一种算法封装起来并可以相互替换使用,策略模式让算法独立于使用它的客户应用可独立变化,接口就是定义行为的。
- 详细看看
Environment接口
也就是说该接口表示配置文件和各种程序所需的属性。
至此,XmlBeanDefinitionReader,实例化完毕。
beanDefinitionReader.loadBeanDefinitions(resource)
loadBeanDefinitions(resource)方法简介
- 将BeanFactory和Resource关联起来的关键;
- 该方法会解析xml文件,并将信息解析成bean放置到Factory。显然该方法的工作量较大。
- 方法说明如下
看看源码
- 会将Resource重新封装成EncodedResource
继续讲解loadBeanDefinitions(EncodedResource)的try...catch部分
- 可以学到try with resource语法糖,以及它是用SAX解析器解析XML文件
doLoadBeanDefinitions(InputSource, Resource)方法
- 上面讲到loadBeanDefinitions(EncodedResource)中的doLoadBeanDefinitions(InputSource, Resource)是其核心方法。
- 在Spring中,do开头的方法一般都是protected的核心方法。
- 先将XML变成Document对象。
- registerBeanDefinitions(Documen, Resource),解析和注册bean进容器
BeanDefinitionDocumentReader是用于解析包含 Spring bean 定义的 XML 文档的 SPI。由 XmlBeanDefinitionReader用于实际解析 DOM 文档。
- 获取之前的Bean定义
- 创建XmlReaderContext,以便使用NamespaceHandler,它可以处理xml文件中自定义命名空间的问题
- bean解析和注册的具体过程。
重点看看
processBeanDefinition(ele, delegate);--- 下面介绍改方法的局部变量
bdHolder是怎么来的 ---有涉及到两个类
AbstractBeanDefinition是接口BeanDefinition的实现,里面有一个重要的成员变量
解析的过程中有个步骤还有创建独一无二的
beanName的步骤
- 如果xml文件中不指定id,Spring会给你生成一个beanName。
![]()
decorateBeanDefinitionIfRequired方法是根据基于自定义的东西进行装饰。
--- 至此bdHolder创建完成了 ---
--- 最后就是registerBeanDefinition---注意一下工厂中有个重要的成员变量
真正的注册阶段
![]()
--- 至此bean就注册好了 ---
---processBeanDefinition(ele, delegate);的最后 ---- 历经重重,bean就算注册完了,要注意,这里是将xml中bean的信息解析之后,将信息存入BeanDefinition中。
- 而BeanDefinition又在BeanDefinitionHolder中,只字未提bean的实例化,也就是bean的创建。这里只涉及bean的注册。
最后小结—Spring Bean注册的流程
- 定义好spring的配置文件。
- 通过
Resource对象将Spring配置文件进行抽象,抽象成一个具体的Resource对象(如ClassPathResource) 。 - 定义好将要使用的Bean工厂(各种BeanFactory)。
- 定义好
XmlBeanDefinitionReader对象,并将工广对象作为参数传递进去,从而构建好二者之间的关联关系。 - 通过
XmlBeanDefinitionReader对象读取之前所抽取出的Resource对象。 - 流程开始进行xml解析。
- 针对xml文件进行各种元素以及元素属性的解析,这里面,真正的解析是通过
BeanDefinitionParserDelegate对象来完成的(委托[Delegate]模式)。 - 通过
BeanDefinitionParserDelegate对象在解析xml文件时,又使用到了模板方法模式(pre, process, post)。 - 当所有的bean标签元素都解析完毕后,开始定义一个
BeanDefinition对象,该对象是一个非常重要的对象,里面容纳了一个Bean相关的所有信息。 BeanDefinition对象创建完毕后,Spring又会创建一个BeanDefinitionHolder对象来持有这个BeanDefinition对象。BeanDefinitionHolder对象主要包含两部分内容:beanName与BeanDefinition还有bean的别名数组。- 工厂会将解析出来的Bean信息存放到内部的一个
ConcurrentHashMap中,该Map的键是beanName。(唯一),值是BeanDefinition对象。 - 调用Bean解析完毕的触发动作,从而触发相应的监听器的方法的执行(观察者模式)。
- 再提一嘴,注意bean注册和创建(实例化)的区别。
- 将Resource重新封装成EncodedResource,是装饰者模式。
- ResourceLoader是策略接口,是策略模式。
- 本篇就讲解了
beanDefinitionReader.loadBeanDefinitions(resource);。这一行程序员手写的代码。