创作不易,转载请篇首注明 作者:掘金@小希子 + 来源链接~
如果想了解更多Spring源码知识,点击前往其余逐行解析Spring系列
一、前言
最近在看spring
源码,发现之前看的很多细节已经忘了,于是决定在看源码的过程中也把主要的流程用博客记载下来,希望自己能坚持下来吧。
spring
已经发展很久,整个体系已经变得很庞大了。为了能更好的把源码看下去,我决定从最基础也是最核心的IOC
开始切入,并且从最原始的xml
解析开始看。面对这样一个庞大的体系,我认为从最原始的方式开始学习,才能更好的看懂它的设计和实现思路。
这一系列文章会默认你对于spring
的使用已经熟悉,并且不抗拒读源码。因为很多的文字会在源码片段上注释-对于源码解析的文章,我暂时也找不到更好的表述方法了。
二、一个简单的示例
首先我们配置一个bean
:
1 | xml复制代码<bean class="com.xiaoxizi.spring.service.AccountServiceImpl" |
对应的类:
1 | java复制代码public class AccountServiceImpl implements AccountService { |
测试类:
1 | java复制代码@Test |
运行结果:
1 | shell复制代码com.xiaoxizi.spring.service.AccountServiceImpl@3e78b6a5 |
三、源码解析
1. beanDefinition注册流程
我们知道,spring
容器启动的逻辑在refresh()
方法里面。所以,话不多说,直接点进refresh()逻辑,具体位置是 org.springframework.context.support.AbstractApplicationContext#refresh
1 | java复制代码public void refresh() throws BeansException, IllegalStateException { |
本篇博文主要讲xml解析的逻辑,暂时我们只关注 obtainFreshBeanFactory()
1 | java复制代码protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { |
继续往下跟refreshBeanFactory()
,实际上方法位置在org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
1 | java复制代码@Override |
继续往下 org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
1 | java复制代码protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { |
经过一系列解析、包装、加载逻辑之后… org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
1 | java复制代码protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) |
细看DocumentReader
解析过程 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
1 | java复制代码protected void doRegisterBeanDefinitions(Element root) { |
这里解释一下 自定义标签、自定义命名空间、默认标签、默认命名空间的含义
1 | xml复制代码<!-- 标签前面有 xxx:即是spring的自定义标签,我们也可以自己定义一个xiaozize:的标签-之后会讲到 --> |
我们先看默认标签的解析过程
1 | java复制代码private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { |
2. bean标签解析
再正真解析bean标签前,我们先看一下spring
的bean
标签都有哪些属性和默认子标签
1 | xml复制代码<bean |
逐一说一下bean
标签中属性作用:
属性 | 作用 |
---|---|
class | 指明bean所属的类 |
id | bean在ioc容器中的唯一标识,如果不填将取别名中的第一个 |
name | bean的别名 |
scope | bean的scope,一般日常开发都是使用默认的singleton单例,还有prototype多例。事实上web环境还有request和session。而且我们也可以自定义scope(之后会讲到的) |
abstract | 是否是抽象的,抽象的bean不会被实例化,只能被继承,用的很少 |
parent | 指定父bean,可以结合abstract一起使用,当然parent指向的bean并不一定要是抽象的 |
autowire | 被自动装配的模式,有byType,byName等可选 |
autowire-candidate | 是否能被其他bean自动装配,false的话该bean将不能被其他bean注入,读者可以自行尝试一下 |
primary | 如果自动装配时匹配到多个bean,标记为primary的bean将被优先注入。 |
depends-on | 依赖,依赖的bean将会先被实例化 |
init-method | bean实例化之后将会调用的方法 |
destroy-method | bean销毁时将会调用的方法,需要主要的是,只有单例的bean,IOC容器才持有其引用,IOC容器销毁 –> bean销毁时才会触发这个方法。 |
factory-bean | 工厂bean的名称,需要与factory-method结合使用,创建bean时将会调用factory-bean.factory-method()来获取当前类实例。 |
factory-method | 工厂bean方法,其实@Bean注解就是通过factory-bean、factory-method属性的功能实现的。 |
lazy-init | 是否是懒加载的 |
bean
标签的默认子标签的作用:
子标签 | 作用 |
---|---|
description | 没啥作用,就是个描述而已 |
constructor-arg | 构造器注入时使用的标签,标明每个参数需要的值。用得少,因为可能会导致不能处理的循环依赖 |
property | 为bean中的属性注入值,常用 |
meta | bean的元数据信息,业务开发中比较少用,之后跟源码过程中能看到使用的地方 |
qualifier | 与@Qualifier作用一致,当注入时出现多个匹配的bean时,将会注入qualifier限定的bean |
lookup-method | 可以理解为覆盖/重写方法,把当前bean中的指定方法委托给指定的bean执行(例:把当前beanA.test() 方法委托给 beanB.test(),方法名必须一致),应用场景比较少。 |
replaced-method | 与lookup-method类似,只是该标签多了子标签用来准确定位方法,当待委托的方法有多个重名方法(重载)时可以使用。 |
继续往下看解析过程org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement
1 | java复制代码public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { |
解析方法parseBeanDefinitionElement
中我们将把xml bean标签中的信息解析并封装到 一个BeanDefinition
中,而之后(初始化流程),我们将会根据BeanDefinition
中的属性来创建bean
实例。现在,先让我们看一下这个BeanDefinition
的结构:
1 | java复制代码// 默认使用的是 GenericBeanDefinition |
好,我们继续往下解析bean
标签
1 | java复制代码public AbstractBeanDefinition parseBeanDefinitionElement( |
那么致此,我们的一个默认的bean
标签就解析完毕了,并且把所有的信息封装到了一个BeanDefinition
实例中,然后这个beanDefinition
将会注册到我们的IOC
容器中去,为下一步生成实例做准备。org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition
1 | java复制代码protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { |
我们主要看一下注册beanDefinition
的过程
1 | java复制代码public static void registerBeanDefinition( |
四、总结
spring xml bean
标签的解析就完整了,其实简单来讲,就是通过一系列手段,拿到xml bean标签中配置的各种属性,封装成一个BeanDefinition
对象,然后把这个对象存到我们IOC
容器的 beanDefinitionMap
、beanDefinitionNames
中。这两个容器在之后的bean实例创建的过程中将会用到。
第一次写这种源码类的博客(其实算是第一次写博客?emmm),感觉写的很干,希望自己能坚持下来并且有进步吧。
下一篇将会讲spring 自定义标签的解析以及应用~
创作不易,转载请篇首注明 作者:掘金@小希子 + 来源链接~
如果想了解更多Spring源码知识,点击前往其余逐行解析Spring系列
٩(* ఠO ఠ)=3⁼³₌₃⁼³₌₃⁼³₌₃嘟啦啦啦啦。。。
这里是新人博主小希子,大佬们都看到这了,左上角点个赞再走吧~~
本文转载自: 掘金