该如何学习spring源码以及解析bean定义的注册?
谢谢邀请!我将从以下几点介绍源码及Spring是怎样解析Bean定义并注册的
目录:
学习源码的重要性?
学习Spring源码需要基础吗?
怎样把Spring源码在本地运行?
Bean定义的加载过程
bean定义加载的流程图
总结
学习源码的重要性?(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
(2) gradle下载,gradle需要JDK8版本
(3) 到下载的spring源码路径执行gradle命令,gradlew :spring-oxm:compileTestJava
(4) 用idea打开spring源码工程,在idea中安装插件kotlin,重启idea
(5) 把编译好的源码导入到工程中
Bean定义的加载过程1、首先找到程序的入口
① 找到其构造方法:
② 调用 AnnotationConfigApplicationContext 构造方法,最终会调用父类 GenericApplicationContext的无参方法
③ 调用父类 AnnotationConfigApplicationContext 无参构造方法,生成bean定义读取器和Bean定义扫描器
上面方法的功能是: 实例化注解的Bean定义扫描器,定义类类路径下的bean定义扫描器
3.1 为Bean定义读取器赋值
3.1.1 为容器 中注册系统的Bean定义信息
上面代码主要是注册系统的Bean定义信息,包含以下几种:
① ConfigurationClassPostProcessor
是一个BeanFactory的后置处理器,主要功能是参与BeanFactory的构建,在这个类中,会解析@Configuration的配置类,解析@ComponentScan、@ComponentScans注解扫描的包,以及解析@Import等注解。
② AutowiredAnnotationBeanPostProcessor
AutowiredAnnotationBeanPostProcessor 实现了BeanPostProcessor,当Spring容器启动的时候,AutowiredAnnotationBeanPostProcessor 将扫描Spring容器中的所有Bean,当发现Bean中拥有
@Autowired 注解的时候就会找到与其匹配的Bean,并注入到对应的中去。
那么在什么时候调用的呢?我们可以看下debug堆栈;
③ RequiredAnnotationBeanPostProcessor
RequiredAnnotationBeanPostProcessor 是BeanPostProcessor实现.的,注释应用于bean属性的setter方法,它表明 受影响的bean 属性在配置时是否必须,如果配置了,没有此bean,则容器就会抛出一个BeanInitializationException 异常。
④ .CommonAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor 这个BeanPostProcessor 通过继承 InitDestroyAnnotationBeanPostProcessor 对 @PostConstruct 和 @PreDestroy注解的支持,以及对bean的依赖注入@Resource的支持。
⑤ EventListenerMethodProcessor
使用EventListenerMethodProcessor处理器来解析方法上的 @EventListener;
执行时机: 实在所有Bean都实例化以后执行的
④ 创建类路径下的bean定义扫描器
上述方法 是注册默认的扫描规则
⑤ 读取配置类
上述方法annotatedClasses为我们配置的mainConfig
annotatedClasses 就是MainConfig
此时上面主要解析 MainConfig,解析成BeanDefinition对象
上述的字段都是什么意思呢?
id: Bean的唯一标识名name: 用来为id 创建一个或者多个别名。class : 用来定义类的全限定名(包名 + 类名)parent: 子类bean定义它所引用它的父类的bean。abstract : 默认为false,用来定义bean是否为抽象bean,它表示这个Bean将不会被实例化,一般用于父类Bean,因为父类bean主要供子类bean继承使用。lazy-init: 用来定义这个bean是否实现懒初始化。如果为true,它将在BeanFactory启动时初始化所有的单例bean,反之,如果为false,它只在Bean请求使用时才开始创建SingletonBean。autowired : 自动装配,它定义了Bean 的自动装配方式。depends-on:依赖对象:这个Bean在初始化时依赖的对象,这个对象会在这个Bean初始化之前创建。init-method: 用来定义Bean的初始化方法,它会在Bean组装之后调用,它必须是一个无参的构造 方法。destroy-method: 用来定义Bean的销毁方法,它在BeanFactory关闭时调用。同样,它也必须是一个无参 的构造方法。只能适用于单例Bean.factory-method: 定义创建该Bean对象的 工厂方法。factory-bean:定义创建该 Bean对象的工厂类。那么 BeanDefinitionHolder 又是什么意思呢?
BeanDefinitionHolder 只是封装了BeanDefinition对象,并且添加了beanName 和 alias 属性。
为什么这样设计呢?因为 我们定义bean时,可以定义多个别名的。
BeanDefinitionRegistry 又是什么呢?
BeanDefintion属性来看,我们并没有看到id 和 name属性没有体现在定义中,原因是ID其左右当前Bean的存储key注册到BeanDefinitionRegistry注册器中。name作为别名key注册到AliasRegistry注册中心。最后都是指向其对应的BeanDefinition。
2、AnnotatedBeanDefinitionReader(Bean定义读取)
BeanDefinition 中存储了Bean的信息,而BeanDefintiionRegistry是基于ID和name保存了Bean的定义。从Bean到BeanDefinition然后再注册到BeanDefintionRegistry整个过程。
从上图看出Bean的定义是由AnnotatedBeanDefinitionReader从@Bean的注解中构建出的,然后基于别名注册到BeanDefinitionRegistry。
BeanDefintionReader 的结构图如下:
2.1 bean定义的加载过程
(1) org.springframework.context.support.AbstractApplicationContext#refresh
注册Bean的 代码
invokeBeanFactoryPostProcessors(beanFactory);
(2) org.springframework.context.support.AbstractApplicationContext
#invokeBeanFactoryPostProcessors
然后实例化 容器初始化 的 ConfigurationClassPostProcessor Bean,然后调用其 的postProcessBeanDefinitionRegistry方法
BeanDefinitionRegistryPostProcessor 这个接口的调用分为三部分:
(1) 调用实现PriorityOrdered 排序接口
(2) 调用实现了Ordered排序接口
(3) 没有实现接口的调用
这个接口的理解如下: 获取BeanDefinition 对象,获取到这个对象就可以获取这个对象中注册的 所有BeanDefiniton对象,我们拥有这个对象后,我们就可以对里面所有的BeanDefinition 对象进行修改。
org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry 方法
最终调用
org.springframework.context.annotation.ConfigurationClassParser
最终调用 org.springframework.context.annotation.ConfigurationClassParser
#doProcessConfigurationClass 方法
下面方法主要功能如下:
解析 @PropretySource注解解析@ComponentScan注解解析@Import解析@ImportResource解析@Bean methods处理其他bean定义加载的流程图总结Spring 对注解的处理有两种方式:
1、直接将注解Bean注册到容器中
可以在初始化容器的时候注册,也可以在容器创建之后手动调用注册方法向容器中注册,然后通过手动刷新容器,使得容器对注册的注解Bean进行处理
2、通过扫描指定的 包及其子包下的所有类
在初始化注解容器的时指定要自动扫描的路径。如果容器创建以后,如果再向容器中添加注解Bean,则 需要手动调用容器扫描的方法,然后手动刷新容器,使得容器对所注册的Bean进行处理。
Copyright © 广州京杭网络科技有限公司 2005-2024 版权所有 粤ICP备16019765号
广州京杭网络科技有限公司 版权所有