Spring源码解析(一)-XML+注解解析

@[TOC] spring解析流程----》 xml+注解
#spring是目前java开发中一个非常重要的框架,基本上目前各种项目都和他有关,spring完美的实现了对象和创建和管理,使得我们的开发人员不用关注对象创建和管理,主需要关注自己的业务实现即可,这样也提高了开发人员的开发效率。所以我们有必要了解spring的基本原理,spring是一个综合性的框架,所以涉及的内容非常多,我会分多篇文章进行介绍spring相关的重要知识点,本篇文章主要从源码讲解spring加载xml和解析注解的实现原理。

part1:BeanDefinition对象

在解析之前,先介绍一个对象:BeanDefinition对象,在spring中,对象的时间,只有一种途径,就是所有对象都是先封装成BeanDefinition对象,然后spring对BeanDefinition进行实例化,所以我们又必要了解这个对象的基本信息,这里主要讲GenericBeanDefinition这个对象: Spring源码解析(一)-XML+注解解析_第1张图片
上面这些属性都是xml,获取注解解析的时候需要填充的信息
(1)、id:Bean 的唯一标识名。它必须是合法的 XMLID,在整个 XML 文档中唯一。
(2)、name:用来为 id 创建一个或多个别名。它可以是任意的字母符合。多个别名之间用逗号或空格分开。
(3)、class:用来定义类的全限定名(包名+类名)。只有子类 Bean 不用定义该属性。
(4)、parent:子类 Bean 定义它所引用它的父类 Bean。这时前面的 class 属性失效。子类 Bean 会继承父类 Bean 的所有属性,子类 Bean 也可以覆盖父类 Bean 的属性。注意:子类 Bean 和父类 Bean 是同一个 Java 类。
(5)、abstract(默认为”false”):用来定义 Bean 是否为抽象 Bean。它表示这个 Bean 将不会被实例化,一般用于父类 Bean,因为父类 Bean 主要是供子类 Bean 继承使用。
(7)、lazy-init(默认为“default”):用来定义这个 Bean 是否实现懒初始化。如果为“true”,它将在 BeanFactory 启动时初始化所有的 SingletonBean。反之,如果为“false”,它只在 Bean 请求时才开始创建 SingletonBean。
(8)、autowire(自动装配,默认为“default”):它定义了 Bean 的自动装载方式。
1、“no”:不使用自动装配功能。
2、“byName”:通过 Bean 的属性名实现自动装配。
3、“byType”:通过 Bean 的类型实现自动装配。
4、“constructor”:类似于 byType,但它是用于构造函数的参数的自动组装。
5、“autodetect”:通过 Bean 类的反省机制(introspection)决定是使用“constructor”还是使用“byType”。
(10)、depends-on(依赖对象):这个 Bean 在初始化时依赖的对象,这个对象会在这个 Bean 初始化之前创建。
(11)、init-method:用来定义 Bean 的初始化方法,它会在 Bean 组装之后调用。它必须是一个无参数的方法。
(12)、destroy-method:用来定义 Bean 的销毁方法,它在 BeanFactory 关闭时调用。同样,它也必须是一个无参数的方法。它只能应用于 singletonBean。
(13)、factory-method:定义创建该 Bean 对象的工厂方法。它用于下面的“factory-bean”,表示这个 Bean 是通过工厂方法创建。此时,“class”属性失效。
(14)、factory-bean:定义创建该 Bean 对象的工厂类。如果使用了“factory-bean”则“class”属性失效。
(15)、autowire-candidate:采用 xml 格式配置 bean 时,将元素的 autowire-candidate属性设置为 false,这样容器在查找自动装配对象时,将不考虑该 bean,即它不会被考虑作为其它 bean自动装配的候选者,但是该 bean 本身还是可以使用自动装配来注入其它 bean 的。
(16)、MutablePropertyValues:用于封装标签的信息,其实类里面就是有一个 list,list里面是 PropertyValue 对象,PropertyValue 就是一个 name 和 value 属性,用于封装标签的名称和值信息
(17)、ConstructorArgumentValues:用于封装标签的信息,其实类里面就是有一个 map,map 中用构造函数的参数顺序作为 key,值作为 value 存储到 map 中
(18)、MethodOverrides:用于封装 lookup-method 和 replaced-method 标签的信息,同样的类里面有一个 Set 对象添加 LookupOverride 对象和 ReplaceOverride 对象

part2:xml解析+默认标签解析

      虽然现在大部分项目主要是springboot和springcloud,我们都知道这两个框架主要是以注解来进行开发了,现在很少使用xml的配置了。但是这里还是要先探索xml的方式,主要的是因为xml解析是最基本的,注解的解析也是在xml的基础上进行的,所以我们有必要对xml的解析原理了解。
	spring初始化的入口的方式有多种ClassPathXmlApplicationContext,FileSystemXmlApplicationContext,AnnotationConfigApplicationContext等,这里使用ClassPathXmlApplicationContext作为讲解。
	当我们实例化的时候, applicationContext = new ClassPathXmlApplicationContext("spring.xml");可以看到源码的构造函数的解析入口:

Spring源码解析(一)-XML+注解解析_第2张图片
我们可以看到refresh方法是跳到了父类去执行具体解析
Spring源码解析(一)-XML+注解解析_第3张图片
接下来来到这个refresh方法的内部,我们这里关注一些核心方法obtainFreshBeanFactory这个方法就是我们解析的主要逻辑,里面包含了xml和注解的解析,下面进行一步一步介绍
Spring源码解析(一)-XML+注解解析_第4张图片
中间直接点击方法,来到refreshBeanFactory,因为我们spring中所以得对象都是交给spring来管理,但是这个管理是有一个工厂来管理的就是beanFactory,所以这里先创建beanFactory,把创建的beanFactory一层层的传递到后面的解析过程中,便于后面的实例化过程。
Spring源码解析(一)-XML+注解解析_第5张图片
我们来到loadBeanDefinitions这个方法里面,可以看到,虽然我们传进来的是beanFactory,但是后面解析的时候,我们是先创建一个beanDefinitionReader来解析,这里是一个委托设计模式的应用 ,然后把beanDefinitionReader解析器传到loadBeanDefinitions方法中进行解析。
Spring源码解析(一)-XML+注解解析_第6张图片
然后我们一直跟踪源码到loadBeanDefinitions,可以看到首先是拿到xml的文件的Resource对象,接下来就可以解析xml文件的内容
Spring源码解析(一)-XML+注解解析_第7张图片
接下来我们看到loadBeanDefinitions这个方法这里使用了一种模板设计模式,就是父类定义了一个模板方法,由子类实现,不同的子类实现的方法是不一样的,这里我们来到XmlBeanDefinitionReader中的实现
Spring源码解析(一)-XML+注解解析_第8张图片
Spring源码解析(一)-XML+注解解析_第9张图片
我们可以看到首先是把编码的Resource信息encodedResource转换成InputStream流对象
Spring源码解析(一)-XML+注解解析_第10张图片
然后把流对象封装成Document对象,然后对Document对象进行解析
Spring源码解析(一)-XML+注解解析_第11张图片
这个时候又把解析过程转给documentReader来解析,又使用了一个委托模式
Spring源码解析(一)-XML+注解解析_第12张图片
接下来spring把Document对象的root节点传到后面的解析过程进行解析,这里终于看到默认标签和自定义标签解析的分流:
Spring源码解析(一)-XML+注解解析_第13张图片
从spring.xml文件可以看一下默认标签和自定义标签:
Spring源码解析(一)-XML+注解解析_第14张图片
接下来我们先看一下默认标签的解析,默认标签我们主要看一下bean标签的解析,来到
在这里插入图片描述
然后进入方法,一步步进入到parseBeanDefinitionElement方法中,这里就是具体的解析过程了,因为***spring实例化的唯一途径就是通过BeanDefinition对象进行实例化***,所以spring也是先把xm中所以得bean标签解析成对应的BeanDefinition对象,便于后面实例化。对于BeanDefinition对象,文章最后会有一个详细的介绍,在这里我们只需要知道,spring会把所以得标签属性解析到BeanDefinition对象中。看下面的源码也知道是这样操作的,首先创建一个BeanDefinition,然后把xml中bean标签的属性解析出来填充到BeanDefinition中。解析完成后,经过一系列的封装,一步步返回,默认标签就完成了,我们可以看到默认标签的解析还是比较简单的。下面就开始自定义标签的解析
Spring源码解析(一)-XML+注解解析_第15张图片

part2:自定义标签解析

   在上一步中,我们了解了spring对默认标签的解析,接下来我们主要看一下spring对自定义标签的解析过程。首先也是把Document对象的root根节点传到后面进行解析,可以看到源码是三步。

Spring源码解析(一)-XML+注解解析_第16张图片
第一步就是在spring.xml中通过标签头获取namespaceuri
Spring源码解析(一)-XML+注解解析_第17张图片
第二步就是根据获取到的uri到spring.handlers获取对应的handle处理类,完成标签元素解析类的初始化
Spring源码解析(一)-XML+注解解析_第18张图片
Spring源码解析(一)-XML+注解解析_第19张图片
Spring源码解析(一)-XML+注解解析_第20张图片
第三步,从第二步获取到的handler,进行parse解析操作
Spring源码解析(一)-XML+注解解析_第21张图片
我们进入ComponentScanBeanDefinitionParser查看具体解析流程
Spring源码解析(一)-XML+注解解析_第22张图片
进入doscan方法中,首先扫描类文件,就是递归扫描文件和文件夹,找到class后缀的文件,判断当前类上是否有注解(Service,Component等),有注解的话,解析注解到BeanDefinition对象中,然后会看当前类是否有其他注解@Lazy @DependOn等,有的话,把这些注解的属性填充到BeanDefinition对象中
Spring源码解析(一)-XML+注解解析_第23张图片
扫描完Service,Component等注解后,还会注册对于这些注解的支撑的一些DI注解(Autowired,Resource等),因为后面对象实例化的时候,会有对象的DI注入操作,所以这里也需要提前把支持DI注入的注解的解析类注册到beanDefinitions中,自此自定义标签解析过程已经完成,然后整个解析过程已经结束:
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
Spring源码解析(一)-XML+注解解析_第24张图片
解析完成,所有的xml和注解解析类,添加了注解的类都已经解析成对应的 beanDefinition对象中,然后注册到了beanFactory中,回到解析的入口:
Spring源码解析(一)-XML+注解解析_第25张图片
解析来就是bean的实例化过程,将在下篇文章重点介绍,敬请期待!!!
Spring源码解析(一)-XML+注解解析_第26张图片
最后总结了整个解析流程的时序图,由于图比较大,所以截图是缩小版,如果需要原版,请加关注后私信!!!
Spring源码解析(一)-XML+注解解析_第27张图片

你可能感兴趣的:(Spring源码解析(一)-XML+注解解析)