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. <bean id="hello" class="bean.HelloSpring" lazy-init="false"> </bean>
  6. </beans>
HelloSpring.java
  1. package bean;
  2. /**
  3. * Created by yuyufeng on 2016/11/17.
  4. */
  5. public class HelloSpring {
  6. private String name;
  7. public HelloSpring() {
  8. System.out.println( "##HelloSpring.HelloSpring初始化……………………………………");
  9. }
  10. public HelloSpring(String name) {
  11. this.name = name;
  12. }
  13. public void sayHello(String something){
  14. System.out.println( "hello"+something);
  15. }
  16. public String getName() {
  17. return name;
  18. }
  19. public void setName(String name) {
  20. this.name = name;
  21. }
  22. @Override
  23. public String toString() {
  24. return "HelloSpring{" +
  25. "name='" + name + '\'' +
  26. '}';
  27. }
  28. }

BeanFactoryTest.java
  1. package spring.ioc;
  2. import bean.HelloSpring;
  3. import org.springframework.beans.factory.BeanFactory;
  4. import org.springframework.beans.factory.xml.XmlBeanFactory;
  5. import org.springframework.core.io.ClassPathResource;
  6. import org.springframework.core.io.Resource;
  7. /**
  8. * Created by yuyufeng on 2016/11/18.
  9. * Spring中Bean的加载过程
  10. */
  11. public class BeanFactoryTest {
  12. public static void main(String[] args) {
  13. //spring如何初始化有两种方式 beanFactory applicationContext
  14. Resource resource = new ClassPathResource( "spring/ioc/beans.xml");
  15. BeanFactory beanFactory = new XmlBeanFactory(resource);
  16. HelloSpring helloSpring = (HelloSpring) beanFactory.getBean( "hello");
  17. helloSpring.sayHello( "张三");
  18. }
  19. }


先从表面上可以看到 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 ;方法


扫描二维码关注公众号,回复: 1813766 查看本文章
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吗?
其实整个过程还是比较容易理解的,就是里面的包装解析很复杂

猜你喜欢

转载自blog.csdn.net/weixin_38964895/article/details/80856651