Spring源码系列:核心概念解析
前言
本文旨在为读者解析Spring源码中的关键类,以便读者在深入阅读源码时,能够了解关键类的作用和用途。在阅读Spring源码时,经常会遇到一些不熟悉的概念,了解关键类的作用可以帮助读者更好地理解这些概念。
BeanDefinition
BeanDefinition是Spring框架中的一个重要概念,它定义了一个Bean的基本属性和行为,比如:
- BeanClassName,当前的bean名字
- Scope,是否单例,具体枚举:#SCOPE_SINGLETON、#SCOPE_PROTOTYPE
- LazyInit,是否懒加载,默认不是
- DependsOn,是否依赖其他bean,如果依赖,则会先创建依赖bean
- InitMethodName,初始化方法名称
- DestroyMethodName,销毁类方法名称
- ......还有更多,但是这几个大体已经差不多了
BeanDefinition的作用非常重要,它可以帮助Spring容器更好地管理Bean的生命周期和依赖关系。在Spring框架中,我们经常会通过注解方式来定义Bean:
- < bean/>
- @Bean
- @Component(@Controller、@Service)
这些都是被称为申明式定义Bean。就是使用Spring提供好的封装。
除了注解方式,我们还可以通过编程方式来定义Bean,这时就需要直接使用BeanDefinition来创建BeanDefinition对象,并设置对应的属性,然后将其注册到Spring容器中,比如
// 创建一个Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
//当然还可以设置其他上面我说的其他属性:懒加载什么的
applicationContext.registerBeanDefinition("userService", beanDefinition);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
无论是通过注解方式还是编程方式来定义Bean,最终都是需要使用BeanDefinition来描述Bean的基本属性和行为,然后将其放入Spring容器中进行管理。
BeanDefinitionReader
BeanDefinitionReader是Spring框架中的一个重要组件,主要用于读取和操作BeanDefinition对象。虽然我们在使用Spring框架时很少直接使用BeanDefinitionReader,但在Spring源码中却扮演着非常重要的角色,相当于Spring源码的基础设施。
BeanDefinitionReader的核心方法包括以下几个:
- BeanDefinitionRegistry,用来注册bean定义,相当于一个工厂
- BeanNameGenerator,用来生成bean名字的生成器
- loadBeanDefinitions,从资源中加载bean
XmlBeanDefinitionReader
XmlBeanDefinitionReader是BeanDefinitionReader的子类,可以用于从XML文件中读取BeanDefinition并注册到Spring容器中。使用XmlBeanDefinitionReader的步骤如下:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(context);
//加载xml中配置的所有<bean>
int i = xmlBeanDefinitionReader.loadBeanDefinitions("spring.xml");
System.out.println(context.getBean("userService"))
AnnotatedBeanDefinitionReader
细心的朋友,应该可以发现AnnotatedBeanDefinitionReader是一个单独的类,不是BeanDefinitionReader的子类,但它的方法与BeanDefinitionReader基本相同,官方说是方便的适配器,用于编程注册bean类,他可以解析@Conditional,@Scope、@Lazy、@Primary、@DependsOn、@Role、@Description相关注解,具体操作如下:
// 创建一个Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
// AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
// beanDefinition.setBeanClass(UserService.class);
// applicationContext.registerBeanDefinition("userService", beanDefinition);
new AnnotatedBeanDefinitionReader(applicationContext).registerBean(UserService.class);
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
同样的,他也可以让我们注册的bean走完创建的整个生命周期过程。
ClassPathBeanDefinitionScanner
ClassPathBeanDefinitionScanner也是一个用于注册BeanDefinition的工具类,与BeanDefinition接口没有直接关系。ClassPathBeanDefinitionScanner可以扫描指定包路径下带有特定注解的类,并将其解析成BeanDefinition,注册到Spring容器中。主要是他有个scan方法对我们定义的basepackage包路径进行解析扫描所有带有@component、@ManagedBean(JSR-250标准)、@Named(JSR-330标准)
使用ClassPathBeanDefinitionScanner的步骤如下:
// 创建一个Spring容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
new ClassPathBeanDefinitionScanner(applicationContext).scan("com.xiaoyu");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.test();
BeanFactory
BeanFactory是Spring框架中的一个重要接口,他就是Spring用于管理Bean对象的创建和管理,看他的几个主要方法就知道了:
- getBean,可以根据name、type等获取bean对象
- containsBean,是否bean工厂中有某个对象
- isSingleton,判断是否是单例
- isTypeMatch,判断改name是否匹配类型
- getType,根据bean名字获取类型
- getAliases。获取别名数组
看着主要几个接口实现,基本都是围绕bean所做的,然后根据接口再看他的实现类就方便许多了,
DefaultListableBeanFactory
如果看过源码的朋友肯定对这个实现类不陌生,如果对这个实现类陌生的朋友,那请记住这个重要的实现类,它实现了很多接口、且继承了多层父类,所以他的功能也是相当之多。我们来看看他的主要方法:
- containsBeanDefinition,查看是否包含某个bean定义,因为该类维护了一个Map<String, BeanDefinition> beanDefinitionMap属性。
- determineAutowireCandidate,决定注入哪个bean,@Primary-->优先级最高--->name
- doResolveDependency,解析依赖,进行注入
- registerBeanDefinition,注册bean定义到beanDefinitionMap属性
- preInstantiateSingletons,进行创建bean实例
具体使用操作也是基本类似的
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition().getBeanDefinition();
beanDefinition.setBeanClass(UserService.class);
defaultListableBeanFactory.registerBeanDefinition("userService",beanDefinition);
UserService userService1 = (UserService) defaultListableBeanFactory.getBean("userService");
userService1.test();
从他的结构图也能看出来:
AbstractBeanFactory
该类是抽象bean,介绍他主要目的就是getbean时,走的主要逻辑就是该类实现的dogetbean方法(请记住这个重要的方法),所以确实需要关注下,主要方法如下:
- doGetBean,获取bean的主要逻辑,没有则创建
- getMergedBeanDefinition,bean定义的合并逻辑,主要是将父类beanfactory中的属性被子类覆盖
AbstractAutowireCapableBeanFactory
继承自刚才提到的AbstractBeanFactory,主要方法如下:
- autowireByName,按照name注入
- autowireByType,根据类型
- createBean,创建bean流程,实例化前可以使用BeanPostProcessors后置处理器
- createBeanInstance,正在创建bean,这边使用到了之前入门讲过的推断构造器实现实例化
- doCreateBean,创建bean,循环依赖、属性填充、初始化
- initializeBean,初始化bean,包括初始化前、初始化、初始化后
- instantiateUsingFactoryMethod,利用factorymethod初始化bean
- invokeAwareMethods,初始化bean时的回调函数Aware接口
- populateBean,初始化之前,属性赋值
可以从他的关键方法看出,主要作用就是初始化bean的全过程,也是很重要的类
HierarchicalBeanFactory
这里说下HierarchicalBeanFactory类,他只是一个接口类,但是如果想要使用beanfactory的层次结构,例如获取父beanfactory,那就必须实现HierarchicalBeanFactory类,比如前面说的bean定义的合并逻辑,就需要获取父beanfactory,从而实现父子bean定义的覆盖合并
ApplicationContext
ApplicationContext是个接口,实际上也是一个BeanFactory,不过比BeanFactory
更加强大,它本身并没有太多方法,但是它继承了很多接口,因为接口之间是可以多继承的。
关于他的父接口,这里不做多说明,详情的话请看下子文章(后续更新)。
AnnotationConfigApplicationContext
一看这个类,大家都知道了,我们用的实例全是用这个类去启动我们的spring的,我们看看他的主要方法:
- AnnotationConfigApplicationContext,构造器,会初始化DefaultListableBeanFactory、AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner;然后开始调用refresh()方法。
- register,会使用编程式定义将bean注入spring容器。就如我们的APPConfig
- scan,走ClassPathBeanDefinitionScanner的scan,扫描包路径,将声明式的bean注入进spring容器
- setBeanNameGenerator,bean名称生成器
ClassPathXmlApplicationContext
主要就是去解析xml配置的bean定义将其注入到spring容器中,功能其实跟AnnotationConfigApplicationContext类似,但是却没有AnnotationConfigApplicationContext强大,比如不能注册BeanDefinition。
BeanPostProcessor
BeanPostProcess表示Bena的后置处理器,可以有多个BeanPostProcessor,我们自己也可以去定义一个BeanPostProcessor;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if ("userService".equals(beanName)) {
System.out.println("userService");
return new User();
}
System.out.println("MyBeanPostProcessor.postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("MyBeanPostProcessor.postProcessAfterInitialization");
return bean;
}
我们可以通过实现bean的后置处理器,来对某一个bean或者所有bean的进行干预,博主只是随便写了一个,没有什么太大意义。
BeanFactoryPostProcessor
BeanFactoryPostProcessor表示Bean工厂的后置处理器,其实和BeanPostProcessor类似,BeanPostProcessor是干涉Bean的创建过程,BeanFactoryPostProcessor是干涉BeanFactory的创建过程,我们也可以自定义:
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("加工beanFactory");
}
}
FactoryBean
FactoryBean和BeanFactory不是一个东西,大家不要混淆两个概念,BeanFactory是管理我们注入的bean等,而FactoryBean本身也会被Spring管理,一旦Spring知道我们的bean实现了FactoryBean,那么会自动调用getObject方法获取我们自己创建的bean,这个bean完完全全交给我们自己创建了,我们可以这样定义一个FactoryBean:
@Component
public class MyFactoryBean implements FactoryBean {
@Override
public Object getObject() throws Exception {
UserService service = new UserService();
return service;
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
但是需要注意的是,这些注入UserService时,是不会有属性依赖注入的,毕竟他没有走bean的生命创建周期。细心的朋友会发现,这根我在配置类中写@bean形式的类有啥区别,现象来讲,他俩都可以被创建出来,但是值得一提的是,FactoryBean创建出来的bean是没走spring定义的bean生命周期的。
MetadataReader、ClassMetadata、AnnotationMetadata
Spring启动时需要扫描指定包路径下的所有类文件来获取需要注入或管理的Bean信息。然而,并非所有类都是需要的,这时可以使用ASM技术来解析类文件的元数据信息,包括类上的注解信息和类的基本信息。ASM技术可以在运行时动态生成和修改Java字节码,从而高效地解析类文件的元数据信息,避免了大量的IO操作和类加载,提高了应用的性能。以下是一个简单的实例:
SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.xiaoyu.service.UserService");
System.out.println(metadataReader.getClassMetadata().getClassName());
metadataReader.getAnnotationMetadata().getAnnotationTypes().forEach(System.out::println);
结语
通过本文的解析,我们大致了解了Spring框架中的一些关键组件及其用途,这有助于我们在深入理解Spring源码过程中建立起一个整体框架。Spring源码量很大,要真正理解透彻还需要投入大量时间进行细致学习和总结。但如果先对一些关键组件有一个大致的认识,有助于我们进行针对性学习,避免迷失在繁杂的细节中。希望本文能够对读者有一定的帮助,更希望读者在学习Spring源码的过程中,不断总结和提高,并在一定阶段有所突破。祝读者顺利!
Spring源码系列:核心概念解析的更多相关文章
- Spring源码系列(二)--bean组件的源码分析
简介 spring-bean 组件是 Spring IoC 的核心,我们可以使用它的 beanFactory 来获取所需的对象,对象的实例化.属性装配和初始化等都可以交给 spring 来管理. 本文 ...
- Ioc容器beanDefinition-Spring 源码系列(1)
Ioc容器beanDefinition-Spring 源码系列(1) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器 ...
- Spring源码系列 — 注解原理
前言 前文中主要介绍了Spring中处理BeanDefinition的扩展点,其中着重介绍BeanDefinitionParser方式的扩展.本篇文章承接该内容,详解Spring中如何利用BeanDe ...
- Spring源码系列 — Bean生命周期
前言 上篇文章中介绍了Spring容器的扩展点,这个是在Bean的创建过程之前执行的逻辑.承接扩展点之后,就是Spring容器的另一个核心:Bean的生命周期过程.这个生命周期过程大致经历了一下的几个 ...
- Spring源码系列 — BeanDefinition
一.前言 回顾 在Spring源码系列第二篇中介绍了Environment组件,后续又介绍Spring中Resource的抽象,但是对于上下文的启动过程详解并未继续.经过一个星期的准备,梳理了Spri ...
- Spring源码系列(三)--spring-aop的基础组件、架构和使用
简介 前面已经讲完 spring-bean( 详见Spring ),这篇博客开始攻克 Spring 的另一个重要模块--spring-aop. spring-aop 可以实现动态代理(底层是使用 JD ...
- Spring源码系列(四)--spring-aop是如何设计的
简介 spring-aop 用于生成动态代理类(底层是使用 JDK 动态代理或 cglib 来生成代理类),搭配 spring-bean 一起使用,可以使 AOP 更加解耦.方便.在实际项目中,spr ...
- 事件机制-Spring 源码系列(4)
事件机制-Spring 源码系列(4) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProcess ...
- Ioc容器BeanPostProcessor-Spring 源码系列(3)
Ioc容器BeanPostProcessor-Spring 源码系列(3) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Io ...
- AOP执行增强-Spring 源码系列(5)
AOP增强实现-Spring 源码系列(5) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器BeanPostProc ...
随机推荐
- AT ARC092F Two Faced Edges
题意:给定一个有向图,保证无重边自环,求将图中的每条边反向后强联通分量的个数是否会改变. 数据范围:$n$ $≤$ $1e3$,$m$ $≤$ $2e5$. 首先考虑一条边的影响. 因为一条边只能连接 ...
- requests使用socks代理
requests在2.10.0版本开始支持socks代理 自己搭了个服务器所以就想顺便用一下. import requests url = 'xxx' my_proxies={"http&q ...
- C/S 架构 和 B/S 架构
C/S架构的理解: 官方称:clinet-server 客户端需要下载的软件: 今日头条,爱奇艺等 能在手机和浏览器打开的软件 B/S架构的理解: 官方称:web-server 客户端为浏览器的: ...
- 查看shell 用户连接数
w | grep pts |wc -l
- sql生成code
- C# 循环给多个连续编号的控件赋值
C# 循环给多个连续编号的控件赋值 我们经常在 winform 界面上用很多文本框用来显示一组数据,文本框前面有Label标识.我们得到的数据也经常是一个list 或者数组的形式的.需要给这些文本框赋 ...
- maven常用镜像源
<mirrors> <mirror> <id>ibiblio</id> <mirrorOf>central</mirrorOf> ...
- PerfDog的使用教程
一.介绍: 移动全平台iOS/Android性能测试.分析工具平台,快速定位分析性能问题.PerfDog支持移动平台所有应用程序(游戏.APP应用.浏览器.小程序.小游戏.H5.后台系统进程等).An ...
- Mybatis-plus常见报错
1.提示数据库(表)不存在,如图: 原因:mybatis-plus默认查询的表名字为实体类的名字User,并转小写. 解决:添加注解@TableName设置表名
- element表格样式修改
HTML代码: <el-table :data="tableData" style="width: 100%" border :row-class-nam ...