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

 
  1. <?xml version="1.0" encoding="UTF-8"?>

  2. <beans xmlns="http://www.springframework.org/schema/beans"

  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

  5.  
  6. <bean id="hello" class="bean.HelloSpring" lazy-init="false"></bean>

  7.  
  8. </beans>

HelloSpring.java

 
  1. package bean;

  2.  
  3. /**

  4. * Created by yuyufeng on 2016/11/17.

  5. */

  6. public class HelloSpring {

  7. private String name;

  8.  
  9. public HelloSpring() {

  10. System.out.println("##HelloSpring.HelloSpring初始化……………………………………");

  11. }

  12.  
  13. public HelloSpring(String name) {

  14. this.name = name;

  15. }

  16.  
  17. public void sayHello(String something){

  18. System.out.println("hello"+something);

  19. }

  20.  
  21. public String getName() {

  22. return name;

  23. }

  24.  
  25. public void setName(String name) {

  26. this.name = name;

  27. }

  28.  
  29. @Override

  30. public String toString() {

  31. return "HelloSpring{" +

  32. "name='" + name + '\'' +

  33. '}';

  34. }

  35. }


BeanFactoryTest.java

 
  1. package spring.ioc;

  2.  
  3. import bean.HelloSpring;

  4. import org.springframework.beans.factory.BeanFactory;

  5. import org.springframework.beans.factory.xml.XmlBeanFactory;

  6. import org.springframework.core.io.ClassPathResource;

  7. import org.springframework.core.io.Resource;

  8.  
  9. /**

  10. * Created by yuyufeng on 2016/11/18.

  11. * Spring中Bean的加载过程

  12. */

  13. public class BeanFactoryTest {

  14. public static void main(String[] args) {

  15. //spring如何初始化有两种方式 beanFactory applicationContext

  16. Resource resource = new ClassPathResource("spring/ioc/beans.xml");

  17. BeanFactory beanFactory = new XmlBeanFactory(resource);

  18. HelloSpring helloSpring = (HelloSpring) beanFactory.getBean("hello");

  19. helloSpring.sayHello("张三");

  20. }

  21. }

 

先从表面上可以看到 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吗?

其实整个过程还是比较容易理解的,就是里面的包装解析很复杂

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值