Spring源码阅读——Bean的加载和获取过程

我们经常使用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吗?
其实整个过程还是比较容易理解的,就是里面的包装解析很复杂




发布了154 篇原创文章 · 获赞 142 · 访问量 35万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览