我们经常使用Spring,并且也都了解其大概原理。我想我们一定会对Spring源码的解读有迫切的渴望。
我也如此。所以,我打算阅读一下Spring的源码。再此之前,我也为此准备了很多。包括,去复习熟练java反射,理解常用的设计模式。当然,这些复习笔记也会在今后的复习中顺便记录在我的csdn博客。(当然,可能写的不好,也可能理解不正确(可以一起交流嘛)。但是乐于分享总归是好的。)
首先看下spring的各个组件。
可以看到,在Core Container(核心容器)中包含有Core、Beans、Context和Spring Expression Language.Core和Beans模块是Spring框架的基础部分,提供IoC控制反转和依赖注入的特性。
Core模块主要包含着Spring框架基本的核心工具类,供其它组件使用。
Beans模块是所有应用都要用到的,它包含访问配置文件、创建和管理bean以及ioc、依赖注入。
Context模块构建于Core和Beans之上。是spring的上下文环境,为Spring核心提供了大量的扩展,天津挨了对国际化、事件传播、资源加载等支持。ApplicationContext接口是Context模块的关键。
Spring Expression Language为Spring提供了一个强大的表达式语言用于在运行时查询草操纵对象 。
现在我们已经了解了Spring的基础组件,我们现在就在代码中跟踪一下Spring Bean的创建和获取过程。
beans.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
- <bean id="hello" class="bean.HelloSpring" lazy-init="false"></bean>
- </beans>
HelloSpring.java
- package bean;
- /**
- * Created by yuyufeng on 2016/11/17.
- */
- public class HelloSpring {
- private String name;
- public HelloSpring() {
- System.out.println("##HelloSpring.HelloSpring初始化……………………………………");
- }
- public HelloSpring(String name) {
- this.name = name;
- }
- public void sayHello(String something){
- System.out.println("hello"+something);
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- @Override
- public String toString() {
- return "HelloSpring{" +
- "name='" + name + '\'' +
- '}';
- }
- }
BeanFactoryTest.java
- package spring.ioc;
- import bean.HelloSpring;
- import org.springframework.beans.factory.BeanFactory;
- import org.springframework.beans.factory.xml.XmlBeanFactory;
- import org.springframework.core.io.ClassPathResource;
- import org.springframework.core.io.Resource;
- /**
- * Created by yuyufeng on 2016/11/18.
- * Spring中Bean的加载过程
- */
- public class BeanFactoryTest {
- public static void main(String[] args) {
- //spring如何初始化有两种方式 beanFactory applicationContext
- Resource resource = new ClassPathResource("spring/ioc/beans.xml");
- BeanFactory beanFactory = new XmlBeanFactory(resource);
- HelloSpring helloSpring = (HelloSpring) beanFactory.getBean("hello");
- helloSpring.sayHello("张三");
- }
- }
先从表面上可以看到 bean的加载可大致可以分为:从xml读取bean的信息加载到Spring容器中,通过xml配置的id从Spring容器反射得到这个类的实例对象。
现在,我们进行详细分析
1.Resource resource = new ClassPathResource("spring/ioc/beans.xml");
我们通过Sring Core模块的工具从本地获得了xml资源,并生成Resource对象。这一过程就不详细跟进了。
2.通过XmlBeanFactory来创建BeanFactory对象。
直接debug进入
3.首先我们会跟进 DefaultSingletonBeanRegistry 其中有静态对象需要实例化。至于为什么会跟进这个类,我们来看下类的继承关系就知道了(为什么会先实例其中的静态类,可以复习以下java对象的实例顺序)
4.接着会进入DefaultListableBeanFactory创建里面的静态对象实例以及执行里面的静态模块
5.通过类加载器注入DefaultListableBeanFactory对象
然后又实例化了一个存放DefaultListableBeanFactory的map
6.接着再执行XmlBeanFactory的构造方法,其中把配置文件Resource赋值给了resource
7.执行this.reader.loadBeanDefinitions(resource); //可以看到这个步骤是要把resource加载到容器中去了。这里是整个资源加载进入的切入点。
8.接着再跟进,直到进入public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException ;方法
9.对导入资源再进行一定包装处理后进入doLoadBeanDefinitions(inputSource, encodedResource.getResource()); //对于encode我们是比较熟悉的 肯定是处理编码相关的
10.现在已经进入到了XmlBeanDefinitionReader.java,
再包装处理(毕竟xml文件规则什么的验证啊 获取比较麻烦,不知道你晕了没有)
xml还是包装成了Document委托给DocumentLoader去处理执行
11.现在又进入public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException ;
程序结果以上的处理,已经获取了xml文档的document,已经可以准备提取注册bean了。
12.经过documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
最终我们获取到了root,protected void doRegisterBeanDefinitions(Element root)这个方法,开始真正的解析已经处理过的资源。
13.解析完成后就是注册了,
debug到如下代码
14.可以看到在这里,把bean存到了beanDefinitionMap中,
对于beanDefinitionMap是什么,就是存在内存中的map,bean就存在里面供外部获取。
跟踪了这么多的源代码,肯定有点乱。做下总结吧。
Spring中bean的加载过程
1.获取配置文件资源
2.对获取的xml资源进行一定的处理检验
3.处理包装资源
4.解析处理包装过后的资源
5.加载提取bean并注册(添加到beanDefinitionMap中)
至于bean的获取,那就比上面的简单多了。
断点进入AbstractBeanFactory
入口
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);进入之后
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException { // Quick check on the concurrent map first, with minimal locking. RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName); if (mbd != null) { return mbd; } return getMergedBeanDefinition(beanName, getBeanDefinition(beanName)); }再进入
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));我们发现又进入了
DefaultListableBeanFactory.java,是不是有种熟悉的感觉。
当你看到这条语句,你就豁然开朗了,
BeanDefinition bd = this.beanDefinitionMap.get(beanName);就是之前加载bean放入到的map吗?
其实整个过程还是比较容易理解的,就是里面的包装解析很复杂