Spring中Bean定义加载过程解析

Posted 踩踩踩从踩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring中Bean定义加载过程解析相关的知识,希望对你有一定的参考价值。

Spring中IOC容器初始化过程解析_踩踩踩从踩的博客-CSDN博客

前言

本篇文章会继续上篇文章 applicationcontext 体系结构、 beanfactory体系结构、以及 ioc  容器启动 的初始化、设置配置路径、以及 refresh部分大致的一个结构,本篇文章会继续解读 bean定义、 加载过程, 注册部分 包括在启动容器时怎么做到bean定义的加载 的,包括beandefition体系结构,以及 beandifinitionregister部分 这个体系结构都需要我们去学习的。  

紧紧的把握几个过程 加载资源、构建bean定义、beanfactory注册。

BeanDefinition接口定义

首先先从加载 beandefinition进行入手,他是从AbstractRefreshableApplicationContext这个类中可以看到 loadBeanDefinitions 方法

往下深入 加载beandefinition方法  就是通过子类去实现的

 

这里面就会又bean定义的加载部分。 而加载bean定义  所有的委派给 XmlBeanDefinitionReader  这里创建了个新的 读取器。   而这里 最终构造 成为这样的方式,也是spring利用解耦的方式,来替代出 的加载bean定义的方式。

描述bean实例,相关特性。 包括懒加载  自动注入 等等  这些属性。以及构造参数 等等。

 这个 bean定义继承的体系

 

 并且可以配置 相关的定义。

 自定义的一些属性 借助于 atrributeacces

这些针对  bean定义的一种扩展,来自一定的实例。

相关的实现。

bean定义的一种实现。

 在不同的实现的bean定义 ,  以及 genricbeandefinition.这种通用的 bean定义 和 applicationcontext 很像, 将公共的实现 给抽象出来。 也有 childbean定义 上

 以及root的bean定义   都是对比出来的  将父类传过来

通用的实现,bean定义 ,转载的  而且 在继承体系中 有个 实现是AnnotatedGenericBeanDefinition

2.5 前用 RootBeanDefinition ChildBeanDefinition 。在 2.5 版本后新增了四个接口类,推荐使用, GenericBeanDefinition、 AnnotatedGenericBeanDefinition 两个类都是 2.5 版本后引入的类、接口。

AnnotatedGenericBeanDefinition  在这个中提供了 annotaionmetadata    的实现方法

 

 增加对注解的提升, 添加新的特性, 元注解信息,  用作扩展。

给配置文件,bean定义。

BeanDefinitionRegistry Bean 定义注册
BeanDefinitionRegistry 接口定义了 bean 定义信息的注册行为。 BeanDefinitionRegistry 中定义的行
为:

构建的bean定义的。

BeanDefinitionRegistry 继承体系
通过 BeanDefinitionRegistry 的继承体系可以发现, SimpleBeanDefinitionRegistry
DefaultLlistableBeanFactory GenericApplicationContext 中实现了接口的方法,我们可以通过他们进行注册bean 定义。

对应的实现, 

查看 GenericApplicationContext 源码,它本质上是通过 DefaultLlistableBeanFactory 实例来完成 Bean 定义注册的。正在原生实现BeanDefinitionRegistry 接口的只有两个类:
SimpleBeanDefinitionRegistry DefaultLlistableBeanFactory 。在目前的环境中
SimpleBeanDefinitionRegistry 没有被使用到。

这里通过 factory中的 registerbean定义  可以 打断点,从入口往注册bean定义  这个 方法栈就可以看出。一步一步进去,就可以了解spring怎么样给我们怎么一步一步的进去 最后进行加载bean定义的。

xml加载bean定义

 到refresh方法 中时。

在准备 bean定义的时候, 可以看出 怎么样去装载bean定义的。

 而这里的loadbeandinifintion方法如何最后做到加载数据到 registerbeandefintion的,这个也是spring给我们做的操作。

这里 可以根据不同的报名来区分开 某个类去处理操作的过程, 加载 bean的过程 统一都在xmlbeandefinitionreader  中 这些 说明  applictioncontext  就是委托 这个类 去加载的,而自己只需要结果就行了。

 根据不同的加载bean定义的参数 从 string 的 路径 到 resource  说明这里  每个方法中做的操作,然后 到 document  这样一段段 的进行操作。

整个流程  string 、resource  document、element  beandefinitionholder  不断的进行封装 直到注入进去。

  先看整体,在看部分,在看局部。

这是整个源码给我们达到的方式。

在 将bean工厂 交给 xmlbeandefinitionreader  然后进行加载。

 将配置放进去 ,不断进行解读。

返回bean定义的个数。  然后这里怎么 根据 location地址 然后 转换为 resource .

 环境变量相关的信息。 解读出来的资源    没有变成beandefinition   然后判断 多少加载bean定义。

 

 都在不断的增加,将element  不断的提升。

 创建 一个 bean定义 documentreader 怎么查找到 需要加载的bean定义的。  通过 beanfactory的实例,  先将原来的获取到,将加载成功的减去 原来的数量,最后就是  加载成功的,以保证哪些最后加载成功。

 这里怎么读取加载  bean定义解析器 ,如何document解析成 bean定义的。

配置 环境的方式  不同的bean  配置环境  这里 加载的地方就可以加上了。

做处理。

 自定义的元素节点。

 根据命名空间。 这是xml的形式 进行解析出来。不是默认的命名空间 就交给 代理去处理。

如果不是默认的元空间怎么解析的。

那需要实现解析器,这种方式,具体的元素解析。

 也就是针对不同 标签 ,在代码 中实现的,找到不同的命名空间。

判断 解读 readercontext , 解读 命名空间。

获得到处理者, 所以 如果 想动态增加,那么 就采用 这种方式去解决。 

解读器。

 mapping 

 如果 是空的就创建一个。

默认的spring.handlers 配置文件上  对应好每个 处理器。

定义好命名空间,

 而dubbo就是采用这样的方式,完成操作。

 内部包含的  进行拆分开。 解析成 一个 bean定义holder

 解析成bean定义的。然后判断 bean定义中是否需要一些必要的属性。注册bean定义这些操作 

然后接下来 怎么 将 element解析成 beandefinition的。

相当于一个中转站,进行操作。

这里解析 element为beandefinition部分  重要的就是上面的方法,进行创建beandefintion

 然后设置 各种参数。

最后就注册到beanfactory中。

对于注解的方式加载bean定义

扫描包的方式进行加载 bean定义。

扫描class 文件,指定包下面的class文件。而对于 注解的方式,就不没有xml对象, 需要扫描注解,得到类信息;

 这里也出现了annotationbeandefintionreader的一种解析器。   也是相当于 xml的解析器的一种方式。

也是  解析的时候,先走到解析器中,后面又回到了 applicationcontext中

bean的方式,beandefinition的方式,构造函数的方式。自动完成刷新。

 对扫描的准备工作。把扫描器和注解解析器 进行操作。

 在读取器 的地方 会注册器给传入进去。

 

把bean定义 给获取到,方法做什么用的。类名

 这里就要anntationconfigUtil进行内部的 注解扫描解析过程。

计算生成后置的处理器,解包装。

准备了大量的解析处理器。  包括  jdk 注解的解析。

这么多的 processor ,都是些什么 Processor ?点第一个的类名 ConfigurationClassPostProcessor 进去 看看。
ConfigurationClassPostProcessor 接口
看注释,启动阶段用来处理 @Configuration 注解的 BeanFactoryPostProcessor ,但是没有看到
BeanFactoryPostProcessor 接口的继承,但是有一个 BeanDefinitionRegistryPostProcessor

这里面会做一些包括不同的bean定义扫描操作,注册 postprocesser

ConfigurationClassPostProcessor 的继承结构
通过 IDE 查看继承体系结构,果不然,它的爷爷就是 BeanFactoryPostProcessor
BeanDefinitionRegistryPostProcessor 扩展了 BeanFactoryPostProcessor ,增加了
BeanDefinitionRegistry 位置的处理,即它可以提前对注册好的 BeanDefinitionRegistry 进行前置处理

bean定义后缀处理器, 资源加载通知,加载优先级   用于启动阶段 注解的类,也就是这里会用到的接口,在注册bean定义之前做一些操作,允许我们修改ioc容器 里面 属性和vlaue值,修改ioc容器,内部bean工厂,bean定义装置好的,

 

那么此时我们明白了这些 Processor 的作用,这样用 Spring 注解配置的类,都能通过这些 Processor
Bean 对象来进行处理相关的逻辑了。
这是 springIOC 中给我们提供的又一个【扩展点】,让我们可以在 beanFactory 开始创建 Bean 实例前对beanFactory进行一些处理。

 

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">     
<property name="locations" value="classpath:other.properties"/> 
</bean>
请把 BeanFactoryPostProcessor 加到类图中,它是重要一员。根据前面的调用栈就不难发现他们的关系

 

注解的扫描
接下来,我们看看注解的扫描过程,添加断点

 

 到此处然后释放拦截,到DefaultListableBeanFactory.registerBeanDefinition断点得到调用栈

 找到ClassPathBeanDefinitionScanner#doScan方法,设置断点进行分析

高效的扫描给定包,返回注册的 bean 定义结果。
找到指定包下所有候选组件(配置的 bean
注册 bean 定义到 BeanFactory

 

 第二步,真正完成的Bean定义的扫描工作,进入findCandidateComponents方法。

这段代码的逻辑:如果有组件索引并且索引支持包含的 filters ,就从组件索引中获取获选组
件 否则,进行类目录下指定包的扫描

 扫描器做的事情,组件索引:

Core Technologies (spring.io)

接下来查看,包下类执行扫描的方法:
ClassPathScanningCandidateComponentProvider#scanCandidateComponents ,重点代码片段
构建扫描包的路径表达式,类似切点表达式

// ResourcePatternResolver 万的扫描包获取到 .class 文件
// 扫描的逻辑:在包下找 .class 文件,这里要求能够灵活指定包,就需要用到模式匹配
// 默认用到的 Ant Path 模式匹配,如指定的包 edu.dongnao.**.service

这里面用的就是 scan的bean定义器。

 

 进入getResourcePatternResolver方法

模式匹配
进入 PathMatchingResourcePatternResolver 类,看类的注释,成员变量。
能将指定的资源位置路径解析为一个或多个匹配的资源。可以是一对一的资源指定,也可以是一对多的匹配模式,通过ant path 格式、 classpath*: 前缀。

 

做了模式匹配的动态增加属性,注解对应的方法这些操作,所需要的东西。

 

ScannedGenericBeanDefinition
ScannedGenericBeanDefinition 类的内部实现

 

继承自 GenericBeanDefinition 类,基于 ASM ClassReader AnnotatedBeanDefinition 实现,
支持注解元数据。

 

 需要来解读,ScannedGenericBeanDefinitionAnnotatedBeanDefinition接口的实现类 

 通过metadataReader 进行解读的。

注解的元数据都交给了 这个类。 自定义的一些扩展 需要添加 或者 不希望扫描的都可以通过这个方式去掉。

 

 类似过滤的。

 这个和  springboot中 的exclude的很像的,这里做的匹配优化的地方,过滤掉所有的需要过滤的类。

解读所有的单例的 一些 处理器等等。

AnnotationMetadata MethodMetaData ClassMetadata 所谓的元信息
Spring 中定义的一套方案来描述一个类、一个类上的注解、一个方法等的描述接口,里面定义一些
相关的操作。
比如:
类:类的名称是什么,它有什么修饰符等
注解:类上的注解的名字,注解的元注解信息有什么、注解的注释信息是什么,注解里面有哪
些属性等。

MetadataReader
MetadataReader接口定义如下,  通过 ASM ClassReader 读取类的元信息,提供一个简单的外观模式。
Spring 中通过 ASM 字节码操作库来读取的类信息、注解信息,同学们又学到了一种方式来获取类
中的信息,它们的类关系图如下。

 

 

MetadataReader ScannedGenericBeanDefinition
MetadataReader 读取到类信息、注解信息后,如何进行判断及创建 BeanDefinition 的,往
BeanDefintion 中给入了哪些信息。
@Configuration @Controller @Service @Repository 注解中类似 @Service ,都包含 @Component
注解

 这里面做的操作,也是通过各种操作进行总结的。

 

以上是关于Spring中Bean定义加载过程解析的主要内容,如果未能解决你的问题,请参考以下文章

Spring加载Bean流程解析

Spring中Bean实例创建过程

源码解析:Spring源码解析笔记启动过程(中)

spring加载bean流程解析

07.Spring Bean 加载 - BeanDefinitionReader

Spring中AOP源码