spring bean源码

谢请!我将从以下几点介绍源码及Spring是怎样解析Bean定义并注册的

目录:

  1. 学习源码的重要性?

  2. 学习Spring源码需要基础吗?

  3. 怎样把Spring源码在本地运行?

  4. Bean定义的加载过程

  5. bean定义加载的流程图

  6. 总结

学习源码的重要性?

(1) 可以提升技术功底,Spring源码也沉淀了很多年,有非常多的精华所在,不管我们什么水平,通过不断的阅读源码,能对我们的技术有很大的提升,并且工作中遇到类似问题的时候,可以借鉴源码中是怎么处理的。

(2) 深度的掌握技术框架:源码看多了,对于新的框架的学习和掌握都是很快的,看下框架的demo,就知道底层是怎么实现的。

比如,学习了Spring 中的AOP,就知道底层是用了JDK的动态代理。然后我们学习mybatis的时候,就在想Mybatis 为什么Service可以直接嗲用Dao接口,就可以直接查询数据库了呢 ?其实也是Spring底层对给接口做了动态代理。

(3) 对线上的问题可以进行快速的定位: 当生产上遇到问题时,能够快速的进行定位,这个能力可以快速秒杀别人。

(4) 对面试有很大的好处,特别是BAT大厂,一般都是问道源码级别的,你如果不会,可能第一轮就会被刷掉。

学习Spring源码需要基础吗?

答案是肯定的,需要,那么需要哪些基础呢?

(1)Java 的技术功能

(2) 反射

(3) 设计模式: 简单工厂、工厂方法、单例模式、原型模式、代理模式、策略模式、模板方法模式、委派模式、适配器模式、装饰器模式、观察者模式

(4) Lambda表达式的知识

怎样把Spring源码在本地运行?

(1) git clone https://github.com/spring-projects/spring-framework.git

spring bean源码图1

(2) gradle下载,gradle需要JDK8版本

(3) 到下载的spring源码路径执行gradle命令,gradlew :spring-oxm:compileTestJava

(4) 用idea打开spring源码工程,在idea中安装插件kotlin,重启idea

(5) 把编译好的源码导入到工程中

Bean定义的加载过程

1、首先找到程序的入口

① 找到其构造方法:

spring bean源码图2

② 调用 AnnotationConfigApplicationContext 构造方法,最终会调用父类 GenericApplicationContext的无参方法

spring bean源码图3

③ 调用父类 AnnotationConfigApplicationContext 无参构造方法,生成bean定义读取器和Bean定义扫描器

spring bean源码图4

上面方法的功能是: 实例化注解的Bean定义扫描器,定义类类路径下的bean定义扫描器

3.1 为Bean定义读取器赋值

spring bean源码图5

3.1.1 为容器 中注册系统的Bean定义信息

spring bean源码图6

spring bean源码图7

上面代码主要是注册系统的Bean定义信息,包含以下几种:

① ConfigurationClassPostProcessor

是一个BeanFactory的后置处理器,主要功能是参与BeanFactory的构建,在这个类中,会解析@Configuration的配置类,解析@ComponentScan、@ComponentScans注解扫描的包,以及解析@Import等注解。

② AutowiredAnnotationBeanPostProcessor

AutowiredAnnotationBeanPostProcessor 实现了BeanPostProcessor,当Spring容器启动的时候,AutowiredAnnotationBeanPostProcessor 将扫描Spring容器中的所有Bean,当发现Bean中拥有

@Autowired 注解的时候就会找到与其匹配的Bean,并注入到对应的中去。

那么在什么时候调用的呢?我们可以看下debug堆栈;

spring bean源码图8

③ RequiredAnnotationBeanPostProcessor

RequiredAnnotationBeanPostProcessor 是BeanPostProcessor实现.的,注释应用于bean属性的setter方法,它表明 受影响的bean 属性在配置时是否必须,如果配置了,没有此bean,则容器就会抛出一个BeanInitializationException 异常。

④ .CommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor 这个BeanPostProcessor 通过继承 InitDestroyAnnotationBeanPostProcessor 对 @PostConstruct 和 @PreDestroy注解的支持,以及对bean的依赖注入@Resource的支持。

⑤ EventListenerMethodProcessor

使用EventListenerMethodProcessor处理器来解析方法上的 @EventListener;

执行时机: 实在所有Bean都实例化以后执行的

spring bean源码图9

④ 创建类路径下的bean定义扫描器

spring bean源码图10

上述方法 是注册默认的扫描规则

⑤ 读取配置类

spring bean源码图11

上述方法annotatedClasses为我们配置的mainConfig

spring bean源码图12

annotatedClasses 就是MainConfig

spring bean源码图13

此时上面主要解析 MainConfig,解析成BeanDefinition对象

spring bean源码图14上述的字段都是什么意思呢?

  1. id: Bean的唯一标识名
  2. name: 用来为id 创建一个或者多个别名。
  3. class : 用来定义类的全限定名(包名 + 类名)
  4. parent: 子类bean定义它所引用它的父类的bean。
  5. abstract : 默认为false,用来定义bean是否为抽象bean,它表示这个Bean将不会被实例化,一般用于父类Bean,因为父类bean主要供子类bean继承使用。
  6. lazy-init: 用来定义这个bean是否实现懒初始化。如果为true,它将在BeanFactory启动时初始化所有的单例bean,反之,如果为false,它只在Bean请求使用时才开始创建SingletonBean。
  7. autowired : 自动装配,它定义了Bean 的自动装配方式。
  8. depends-on:依赖对象:这个Bean在初始化时依赖的对象,这个对象会在这个Bean初始化之前创建。
  9. init-method: 用来定义Bean的初始化方法,它会在Bean组装之后调用,它必须是一个无参的构造 方法。
  10. destroy-method: 用来定义Bean的销毁方法,它在BeanFactory关闭时调用。同样,它也必须是一个无参 的构造方法。只能适用于单例Bean.
  11. factory-method: 定义创建该Bean对象的 工厂方法。
  12. factory-bean:定义创建该 Bean对象的工厂类。

那么 BeanDefinitionHolder 又是什么意思呢?

spring bean源码图15

BeanDefinitionHolder 只是封装了BeanDefinition对象,并且添加了beanName 和 alias 属性。

为什么这样设计呢?因为 我们定义bean时,可以定义多个别名的。

BeanDefinitionRegistry 又是什么呢?

spring bean源码图16

BeanDefintion属性来看,我们并没有看到id 和 name属性没有体现在定义中,原因是ID其左右当前Bean的存储key注册到BeanDefinitionRegistry注册器中。name作为别名key注册到AliasRegistry注册中心。最后都是指向其对应的BeanDefinition。

2、AnnotatedBeanDefinitionReader(Bean定义读取)

BeanDefinition 中存储了Bean的信息,而BeanDefintiionRegistry是基于ID和name保存了Bean的定义。从Bean到BeanDefinition然后再注册到BeanDefintionRegistry整个过程。

spring bean源码图17

从上图看出Bean的定义是由AnnotatedBeanDefinitionReader从@Bean的注解中构建出的,然后基于别名注册到BeanDefinitionRegistry。

BeanDefintionReader 的结构图如下:

spring bean源码图18

2.1 bean定义的加载过程

(1) org.springframework.context.support.AbstractApplicationContext#refresh

注册Bean的 代码

invokeBeanFactoryPostProcessors(beanFactory);

(2) org.springframework.context.support.AbstractApplicationContext

#invokeBeanFactoryPostProcessors

spring bean源码图19

然后实例化 容器初始化 的 ConfigurationClassPostProcessor Bean,然后调用其 的postProcessBeanDefinitionRegistry方法

BeanDefinitionRegistryPostProcessor 这个接口的调用分为三部分:

(1) 调用实现PriorityOrdered 排序接口

(2) 调用实现了Ordered排序接口

(3) 没有实现接口的调用

这个接口的理解如下: 获取BeanDefinition 对象,获取到这个对象就可以获取这个对象中注册的 所有BeanDefiniton对象,我们拥有这个对象后,我们就可以对里面所有的BeanDefinition 对象进行修改。

spring bean源码图20

  1. org.springframework.context.annotation.ConfigurationClassPostProcessor

#postProcessBeanDefinitionRegistry 方法

spring bean源码图21

spring bean源码图22

最终调用

org.springframework.context.annotation.ConfigurationClassParser

最终调用 org.springframework.context.annotation.ConfigurationClassParser

#doProcessConfigurationClass 方法

下面方法主要功能如下:

  • 解析 @PropretySource注解
  • 解析@ComponentScan注解
  • 解析@Import
  • 解析@ImportResource
  • 解析@Bean methods
  • 处理其他

spring bean源码图23

bean定义加载的流程图

spring bean源码图24

总结

Spring 对注解的处理有两种方式:

1、直接将注解Bean注册到容器中

可以在初始化容器的时候注册,也可以在容器创建之后手动调用注册方法向容器中注册,然后通过手动刷新容器,使得容器对注册的注解Bean进行处理

2、通过扫描指定的 包及其子包下的所有类

在初始化注解容器的时指定要自动扫描的路径。如果容器创建以后,如果再向容器中添加注解Bean,则 需要手动调用容器扫描的方法,然后手动刷新容器,使得容器对所注册的Bean进行处理。

本文来自投稿,不代表天一生活立场,如若转载,请注明出处:http://tiyigo.com/baike/3288.html

(0)
上一篇 2022-11-24 16:00
下一篇 2022-11-24 16:12

相关推荐