您的位置:首页 > 编程语言 > Java开发

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

2017-12-12 23:42 513 查看
我们经常使用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

[html]
view plain
copy

<?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">  
17bf2

  

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

  

</beans>  

HelloSpring.java

[java]
view plain
copy

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

[java]
view plain
copy

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吗?

其实整个过程还是比较容易理解的,就是里面的包装解析很复杂
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: