Spring相关源码解读

news2025/1/13 14:52:36

框架

  • 1.ApplicationContext refresh的流程
  • 2.spring bean 的生命周期
  • 3.spring事务失效的几种场景以及原因
  • 4.springMVC执行流程
  • 5.一些注解
    • (1)@Configuration
    • (2)@Import
    • (3)@SpringBootApplication
  • 6.spring中有哪些设计模式
  • 7.循环依赖
    • (1)创建代理
    • (2)缓存
    • (3)构造循环依赖
    • (4)总结

1.ApplicationContext refresh的流程

spring调用refresh()方法来初始化容器

(1)prepareRefresh

这一步是为后续步骤做准备工作

创建和准备了 Environment 对象,Environment存储一些键值对
Environment 作用:
(1)为后续 @Value ,值注入时提供键值

(2)obtainFreshBeanFactory

这一步是获取(或创建) BeanFactory

(1)BeanFactory作用:
负责bean的创建、依赖注入和初始化
(2)BeanDefinition的作用:
作为bean的设计蓝图,规定了bean的特征,如单例多例、依赖关系、初始销毁方法等。BeanDefinition的来源多种多样,可以
通过xml获得、通过配置类获得、通过组件扫描获得,也可以是编程添加

(3)prepareBeanFactory

完善BeanFactory

StandardBeanExpressionResolver:用来解析EL表达式 #{}
ResourceEditorRegistrar:会注释类型解释器,并应用ApplicationContext提供的 Environment完成${}解析
registerResolvableDependency:注册特殊的bean 指 beanFactory以及ApplicationContext
ApplicationContextAwareProcessor:解析Aware接口

(4)postprocessBeanFactory

这一步是空实现,留给子类扩展

一般 Web 环境的ApplicationContext都要利用它注册新的Scope,完善Web下的BeanFactory
体现了模板方法设计模式

(5)invokeBeanFactoryPostProcessors

BeanFactory的后处理器,充当BeanFactory的拓展点,可以用来补充或修改BeanDefinition

例如:
ConfigurationClassPostProcessor:用来解析@Configuration @Bean @Import @PropertySource
PropertySourcesPlaceHolderConfigurer:用来替换BeanDefinition中的${}

(6)registerBeanPostProcessors

bean的后处理器,可以充当bean的扩展点,可以工作在bean的实例化、依赖注入、初始化阶段

(7)initMessageSource

实现国际化
从容器中找一个名为messageSource的bean,如果没有,则提供空的MessageSource实现

(8)initApplicationEventMulticaster

事件广播器

用来发布事件给监听器
从容器中找一个名为applicationEventMulticaster的bean作为事件广播器,如果没有,也会新建默认的事件广播器
可以调用ApplicationContext.publishEvent(事件对象)来发布事件

(9)onRefresh

空实现,留给子类扩展

springBoot中的子类可以在这里准备WebServer,即内嵌web容器
体现了模板方法设计模式

(10)registerListeners

事件监听器

用来接收事件
一部分监听器是事先编程添加的、另一部分监听器来自容器中的bean、还有一部分来自于@EventListener的解析
实现ApplicationListtener接口,重写其中的onApplicationEvent(E e)方法即可

(11)finishBeanFactoryInitialization

conversionService:用来类型转换的,作为对PropertyEditor的补充
embeddedValueResolvers:内迁至解析器用来解析@Value中的${},借用的是Environment的功能
singletonObjects:初始化所有非延迟单例对象,缓存所有的单例对象

(12)finishRefresh

lifecycleProcessor:生命周期处理器,控制容器中需要生命周期管理的bean
容器中有名称为lifecycleProcessor的bean就使用,否则创建默认的生命周期处理器

调用context的start,即可触发所有实现LifeCycle接口bean的start
调用context的stop,即可触发所有实现LifeCycle接口bean的stop

总结

2.spring bean 的生命周期

(1)处理名称,检查缓存

(1)先把别名解析为实际名称,再进行后续处理
(2)若要获取 FactoryBean 本身,需要使用 &名称 获取

缓存中查找对象,缓存中有就直接用,没有就创建
(3)singletonObjects是一级缓存,放单例成品对象。找对象先从一级缓存开始找
(4)singletonFactories是三级缓存,放单例工厂。可以解决循环依赖
(5)earlysingletonObjects是二级缓存,放单例工厂的产品,可称为提前单例对象。解决需要创建代理对象时产生的依赖

(2)检查父工厂

如果容器中配置了父容器,如果缓存中没有找到对象,就从父容器中找,如果父容器中找到就直接使用,否则就创建。

父子容器的bean名称可以重复
优先找子容器的bean,找到了直接返回,找不到继续到父容器中找

(3)检查 DependsOn

DependsOn 用在非显式依赖的 bean 的创建顺序控制
例如:A DependsOn B,那么就先创建B再创建A

(4)按Scope创建bean

singleton Scope:表示从单例池范围内获取bean,如果没有,则创建并放入单例池
prototype Scope:表示从不缓存bean,每次都创建新的
request Scope:表示从request对象范围内获取bean,如果没有,则创建并放入request

(5)创建bean

(1)创建阶段:
AutowiredAnnotationBeanPostProcessor选择构造:优先选择带@Autowired注解的构造;若有唯一的带参构造,也会入选
采用默认构造:如果上面的后处理器和BeanDefiniation都没找到构造,次啊用默认构造,即使是私有的

(2)依赖注入:
AutowiredAnnotationBeanPostProcessor(注解匹配):识别@Autowired及@Value标注的成员,封装为InjectionMetadata进行依赖注入
CommonAnnotationBeanPostProcessor(注解匹配):识别@Resource标注的成员,封装为InjectionMetadata进行依赖注入
AUTOWIRE_BY_NAME(根据名字匹配):根据成员名字找bean对象,修改mbd的propertyValues,不会考虑简单类型的成员
AUTOWIRE_BY_TYPE(根据类型匹配):根据成员类型执行resolveDependency找到依赖注入的值,修改mbd的propertyValues
applyPropertyValues(精确指定):根据mbd的propertyValues进行依赖注入
优先级最高的是精确指定,下来是根据名称/类型匹配,最后才是注解匹配

(3)初始化:
处理Aware接口:进行初始化,优先级最高
      @PostConstruct:通过实现后处理器实现功能
      实现InitializingBean接口:通过接口回调初始化执行
      initMethod:根据 BeanDefinition 得到的初始化方法执行初始化
创建aop代理:通过实现后处理器实现功能,优先级最低

(4)注册可销毁bean:
判断是否为可销毁bean的依据:
  如果实现了DisposableBean接口或AutoCloseable接口,则为可销毁bean
  如果自定义了destroyMethod,则为可销毁bean
  如果采用了@Bean没有指定destroyMethod,则采用自动推断的方式获取销毁方法名(close,shutdown)
  如果有@PreDestroy标注的方法
存储位置:
  singleton Scope的可销毁bean会存储于beanFactory的成员中
  自定义的scope的可销毁bean会存储于对应的域对象中
  prototype Scope不会存储,需要自己找到此对象销毁
  存储时都会封装为DisposableBeanAdapter类型对销毁方法的调用进行适配

(6)类型转换

如果getBean的requiredType参数与实际得到的对象类型不同,会尝试进行类型转换

(7)销毁bean

singleton bean:的销毁在ApplicationContext.close时,此时会找到所有DisposableBean的名字,注意销毁
自定义 scope bean 的销毁在作用域对象生命周期结束时
prototype bean的销毁可以通过自己动手调用AutowireCapableBeanFactory.destroyBean方法执行销毁

总结

3.spring事务失效的几种场景以及原因

(1)检查异常

语法上有强制要求,需要 throws或者try catch的异常

原因:spring默认只会回滚非检查异常,spring不会对检查异常进行回滚

解决:在处理此业务的类上面加 @Transactional(rollbackFor = Exception.class)这个注解

(2)错误try-catch

业务方法内自己加try-catch异常导致事务不能正确回滚

原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉

解决:(1)需要将异常抛出去,(2)调用 TransactionStatus.setRollbackOnly()方法通知回滚事务

(3)切面顺序

aop切面顺序导致事务不能正确回滚

原因:事务切面优先级最低,但如果自定义的切面的优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出
异常,则事务切面不能接收到异常,就不能回滚

解决:(1)需要将异常抛出去(推荐)   (2)调用 TransactionStatus.setRollbackOnly()方法通知回滚事务
	 (3)@Order(Ordered.LOWEST_PRECEDENCE - 1)

(4)非public方法

原因:spring为方法创建代理、添加事务通知、前提条件都是该方法是public的

解决:@Transactional 注解必须加在public方法上,不能加在其他方法上,必须加public修饰符

(5)父子容器

原因:子容器的扫描范围过大,把未加事务配置的service扫描进来

解决:(1)各扫描各的,不要图简便     (2)不要用父子容器,把所有的bean放在同一容器

(6)本类方法调用

调用本类方法导致传播行为失效

原因:本类方法调用不经过代理,因此无法增强

解决:(1)依赖注入自己(代理)来调用    (2)通过AopContext拿到代理对象,来调用

(7)原子性失效

@Transactional没有保证原子行为

原因:事务的原子性仅涵盖 insert  uodate  delete  select..for update 语句,select方法并不阻塞

(8)锁失效

@Transactional方法导致的synchronized失效

原因:synchronized保证的仅是目标方法的原子性,环绕目标方法的还有commit等操作,它们并未处在synchronized块内

解决:(1)synchronized范围应扩大至代理方法调用   (2)使用 select..for update 替换 select(推荐使用)

4.springMVC执行流程

(1)初始化阶段

(1)在Web容器第一次用到DispatcherServlet的时候,会创建其对象并执行init方法
(2)init方法内会创建spring web 容器,并调用容器的refresh方法
(3)refresh过程中会创建并初始化springMVC中的重要组件
(4)容器初始化后,会将上一步初始化好的重要组件,赋值给DispatcherServlet的成员变量,留待后用

(2)匹配阶段

(1)用户发送的请求同一到达前端控制器DispatcherServlet
(2)DispatcherServlet遍历所有HandlerMapping,找到与路径匹配的处理器
(3)将HandlerMethod连同匹配到的拦截器,生成调用链对像HandlerExecutionChain返回
(4)遍历HandlerAdapter处理器适配器,找到能处理HandlerMethod的适配器对象,开始调用

(3)执行阶段

(1)执行拦截器preHandle
(2)由HandlerAdapter调用HandlerMethod,调用前处理不同类型的参数,调用后处理不同类型的返回值
(3)如果第二步没有异常:
     返回ModelAndView
     执行拦截器postHandle
     解析试图,得到View对象,进行视图渲染
(4)如果第二步有异常,进入HandlerExceptionResolver异常处理流程
(5)最终都会执行拦截器的afterCompletion方法
(6)如果控制器方法标注了@ResponseBody注解,则在第2步就会生成json结果,并标记ModelAndView已处理,这样就不会执行
第3步的视图渲染

第二步没有异常

第二步有异常

5.一些注解

(1)@Configuration

(1)配置类相当于一个工厂,标注@Bean注解的方法相当于工厂方法
(2)@Bean不支持方法重载,如果有多个重载方法,仅有一个能入选为工厂方法
(3)@Configuration默认会为标注的类生成代理,其目的是保证@Bean方法相互调用时,仍然能保证其单例特性
(4)@Configuration中如果含有bean工厂后处理器,则实例工厂方法会导致配置类提前创建,造成依赖注入失败。
解决:改用静态工厂方法

(2)@Import

(1)引入单个bean:@Import(Bean1.class)
(2)引入一个配置类:@Import(OtherConfig.class)
(3)引入多个配置类,通过 Selector 选择器
(4)通过beanDefinition注册器

(3)@SpringBootApplication

(1)@SpringBootConfiguration:表示当前类是一个配置类
(2)@ComponentScan:扫描
(3)@EnableAutoConfiguration:
@AutoConfigurationPackage:所标注类的包名会被记下来,放到容器中
@Import(AutoConfigurationImportSelector .class):分离主配置和从属配置,避免强耦合;执行优先级低,先保证主配置,再解析从属配置

6.spring中有哪些设计模式

(1)单例模式

(1)singleton bean 并非实现了单例模式,它只能保证每个容器内,相同的id的bean单实例

(2)Builder模式

(1)比较灵活的构建产品对象
(2)在不执行最后build方法前,产品对象都不可用
(3)构建过程采用链式调用

(3)工厂方法模式

让接口和实现相分离,降低耦合

如:ApplicationContext 和 BeanFactory中的getBean

(4)Adapter适配器模式

把一套接口转换为另一套调用者期望的接口

(5)组合模式

把分散的调用集中起来,统一调用入口

(6)装饰器模式

对一个对象动态的增加职责和功能,避免子类继承父类所有的方法

(7)Proxy 代理模式

控制目标的访问

(8)责任链模式

(9)观察者模式

用来解耦合

(10)策略模式

(11)模板方法设计模式

7.循环依赖

(1)创建代理

@Aspect:标注的类称为切面类
@Around @Before @After :标注的方法称为切面方法
@Around("execution(* car())") : execution(* car()):切入点表达式

(2)缓存

(1)一级缓存

作用:限制bean在beanFactory中只存一份,即实现 singleton scope

问题:解决不了set循环依赖


(2)二级缓存

作用:解决set循环依赖

问题:不能解决set循环依赖中有代理的情况


(3)三级缓存

作用:解决set循环依赖中代理创建过晚的问题

(3)构造循环依赖

三级缓存不能解决构造循环依赖

解决:(1)用 @Lazy 注解(加在方法的参数前边),使用B的代理对象
	 (2)用 ObjectFactory ,使用B的工厂对象
	 (3)用 Provider,与ObjectFactory作用一样
	 (4)用 @Scope 注解(加在类上面)



(4)总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/46528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BP神经网络详解,Python实现求解异或问题

BP神经网络 符号及其含义 nln_lnl​表示第lll层神经元的个数;f(⋅)f()f(⋅)表示神经元的激活函数;W(l)∈Rni∗ni−1W^{(l)}\in\mathbb R^{n_i*n_{i-1}}W(l)∈Rni​∗ni−1​表示第l−1l-1l−1层到第lll层的权重矩阵;wij(l)w_{ij}^{(l)}wij(l…

基于tensorflow的ResNet50V2网络识别动物

前言 之前很多人在,如何进行XXX的识别,对应的神经网络如何搭建。对应神经网络怎么搭建,我也是照本宣科,只能说看得懂而已,没有对这块进行深入的研究,但是现在tensorflow,paddle这些工具&#x…

长期稳定的项目—steam搬砖

大家好,我是阿阳 steam搬砖项目一直稳稳定定的进行着,有些朋友基本都观察了近2年 所以很多人问我公众号的项目是不能做了吗?怎么最近做新的去了?很明显这是几乎不可能的事情,steam做2年了,本公众号都能翻到…

这几个数据分析项目,让我看到了什么才叫专业!!

大家好,我是小一 新的一周又来了,从今天开始,会出一个新的系列《数分实验室》 实验室会介绍一些有内核、有科技的数据分析实战项目。 项目数据集、源代码都是公开的,非常适合想练手但是又没数据、没参考案例的同学 今天先热热…

ES的基础概念

1、ES是什么 Elasticsearch 是一个分布式可扩展的实时搜索和分析引擎,一个建立在全文搜索引擎 Apache Lucene(TM) 基础上的搜索引擎.当然 Elasticsearch 并不仅仅是 Lucene 那么简单,它不仅包括了全文搜索功能,还可以进行以下工作:分布式实时文件存储&am…

6-1分支限界法

6-1分支限界法 1.分支限界法与回溯法的不同 (1)求解目标: 回溯法的求解目标是找出解空间树中满足约束条件的所有解(或一个最优解), 而分支限界法的求解目标则是找出满足约束条件的一个解(或最优解&#x…

组织机器学习代码

组织机器学习代码 从note本转移到 Python 脚本时组织代码。 Intuition 有组织的代码就是有可读的、可重现的、健壮的代码。您的团队、经理,最重要的是,您未来的自己,将感谢您为组织工作付出的最初努力。在本课中,将讨论如何将代码…

pytest测试框架入门1

pytest单元测试框架 单元测试是指在软件开发当中,针对软件的最小单位(函数,方法)进行正确性的检查测试 单元测试框架主要做什么 测试发现:从多个文件里面找到我们的测试用例测试执行:按照一定的顺序和规则…

初学者指南: 使用NumPy数组进行图像处理

这里写自定义目录标题初学者指南: 使用NumPy数组进行图像处理1、加载图像2、裁剪图像3、分离颜色4、转换5、灰度转换6、图像分割结语初学者指南: 使用NumPy数组进行图像处理 由于图像也可以被视为由数组组成,因此我们也可以使用NumPy执行不同的图像处理任务。在本文…

【Lilishop商城】No2-6.确定软件架构搭建五(本篇包括定时任务xxl-job)

仅涉及后端,全部目录看顶部专栏,代码、文档、接口路径在: 【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇只介绍重点架构逻辑,具体编写看源代码就行,读起来也不复杂~ 谨慎&#xf…

如何配置一台适合oc渲染器的电脑?

众所周知,Octane 是最流行的渲染引擎之一。此外,Octane 是一个 GPU 渲染引擎,它使用一种计算最终生成的图片的方法,试图达到照片般的真实感。Octane 是一种利用 GPU 技术的无偏渲染引擎,非常接近物理精度。一台好的 PC…

计算机组成原理习题课第三章-2(唐朔飞)

计算机组成原理习题课第三章-2(唐朔飞) ✨欢迎关注🖱点赞🎀收藏⭐留言✒ 🔮本文由京与旧铺原创,csdn首发! 😘系列专栏:java学习 💻首发时间:&…

天宇优配|平台助企“抱团出海” “小而美”中觅“先机”

天津华图轿车物流有限公司一批二手新能源车从连云港装船发往阿联酋迪拜。(采访方针供图) 最近,一笔100.8万美元的出口信誉稳妥保单融资借款,被划到了天津华图轿车物流有限公司的账户上。正值客户“下单”高峰期,这笔及…

Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一)

Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一) 本文目录: 一、【旋转的精灵女孩】案例运行效果 二、Three.js简介 三、Three.js代码正常运行显示条件 (1)不载入任何纹理贴图的网页 (2&…

双十二蓝牙耳机啥牌子好?2022年度热销蓝牙耳机排名

这期双十二数码好物分享,工作室打算来跟大家说说蓝牙耳机这个话题,它已经成为出行必带的装备,上班族、学生党、游戏党都离不开蓝牙耳机。今年我们测评过数十款型号了,本期我们盘点了今年热销的蓝牙耳机排名,让大家直观…

【学习笔记】《Python深度学习》第五章:深度学习用于计算机视觉

文章目录1 卷积神经网络简介1.1 卷积运算1.2 最大池化运算2 在小型数据集上从头开始训练一个卷积神经网络2.1 下载数据2.2 构建网络2.3 数据预处理2.4 数据增强3 使用预训练的卷积神经网络3.1 特征提取3.2 微调模型3.3 小结4 卷积神经网络的可视化4.1 可视化中间激活4.2 可视化…

新手想开一个传奇该如何操作?开一个传奇必须掌握哪些知识要点

对于这个问题,近期问的人比较多,相比这也是热爱传奇这个游戏的朋友会问到的一个问题,因为喜欢玩这个游戏,也想要自己去开一个 经营一个 不管是电脑端也好 还是手机端也好,但是对于一些新手确实不知道该如何开始操作 从…

H3C opsf/rip/ftp/telent/nat/acl综合

实验拓扑 拓扑下载 https://sharewh2.xuexi365.com/share/84b85b32-acb7-4f62-a389-6188680a19f3?t3 图 1-1 注:如无特别说明,描述中的 R1 或 SW1 对应拓扑中设备名称末尾数字为 1 的设备,R2 或 SW2 对应拓扑中设备名称末尾数字为 2 的设备…

三天入门Redis【快速浏览版】

文章目录第一天1.1 Redis基础1.1.1 NoSql引入1.1.2 NoSql特点1.1.3 NoSql数据库1.1.4 Redis概述1.1.5 Redis文件的作用1.1.6 Redis相关介绍1.2 常用的五大类型及操作⭐️1.2.1 Redis键(key)1.2.2 库的一些操作1.2.3 Redis字符串1.2.4 Redis列表&#xff…

MATLAB数据导入

MATLAB数据导入 在编写一个程序时,经常需要从外部读入数据。MATLAB使用多种格式打开数据。本章将要介绍MATLAB中数据的导入。 MATLAB中导入数据的方式有两种,分别是在命令行通过代码把数据导进去和通过MATLAB的数据导入向导导入数据。本节将为大家介绍第…