首页 > 其他分享 >你真正了解Spring的工作原理吗

你真正了解Spring的工作原理吗

时间:2023-07-26 09:55:55浏览次数:28  
标签:事务 销毁 Spring bean 依赖 创建 原理 真正

 Spring 

 1.1 什么是Spring IOC 和DI ? 

 ① 控制反转(IOC):Spring容器使用了工厂模式为我们创建了所需要的对象,我
们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控
制反转的思想。
② 依赖注入(DI):Spring使用Java Bean对象的Set方法或者带参数的构造方法为
我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本
思想。 

 1.2 有哪些不同类型的依赖注入实现方式?
 依赖注入分为Setter方 法注入和构造器注入
构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类
有一系列参数,每 个参数代表一个对其他类的依赖。
Setter方法注入:Setter方法注入是容器通过调用无参构造器或无参static工厂 方
法实例化bean之 后,调用该bean的setter方法,即实现了基于setter的依赖注
入。 

 1.3 Spring支持的几种bean的作用域 

 Spring框架支持以下五种bean的作用域:


singleton : bean在每个Spring ioc 容器中只有一个实例。


prototype:一个bean的定义可以有多个实例。


request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring
ApplicationContext情形下有效。


session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的 Spring ApplicationContext情形下有效。


application:属于应用程序域,应用程序启动时bean创建,应用程序销毁时bean销毁。该作用域仅在基于web的ServletContext. 

 

 1.4 Spring框架中的单例bean是线程安全的吗? 

 不是线程安全的


当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线
程会并发执行该请求对应的业务逻辑(成员方法),如果该处理逻辑中有对该单
列状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题。


Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程
安全和并发问题需要开发者自行去搞定。


Spring bean并没有可变的状态(比如Service类和DAO类),所以在某种程度上说
Spring的单例bean是线程安全的。


如果你的bean有多种状态的话(比如 View Model对象),就需要自行保证线程
安全。最浅显的解决办法就是将多态bean的作用由“singleton”变更为
“prototype”。 

 

 1.5 Spring自动装配 bean 有哪些方式? 

 spring的自动装配功能的定义:无须在Spring配置文件中描述javaBean之间的依
赖关系(如配置 <property>、<constructor-arg> )。
自动装配模式:
1、no:这是 Spring 框架的默认设置,在该设置下自动装配是关闭的,开发者需
要自行在 bean 定义中用标签明确的设置依赖关系 。


2、byName:该选项可以根据bean名称设置依赖关系 。 当向一个bean中自动装
配一个属性时,容器将根据bean的名称自动在在配置文件中查询一个匹配的
bean。 如果找到的话,就装配这个属性,如果没找到的话就报错 。


3、byType:该选项可以根据 bean 类型设置依赖关系 。 当向一个 bean 中自动
装配一个属性时,容器将根据 bean 的类型自动在在配置文件中查询一个匹配的
bean。 如果找到的话,就装配这个属性,如果没找到的话就报错 。


4、constructor :构造器的自动装配和byType模式类似,但是仅仅适用于与有构
造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean ,
那么将会抛出异常 。


5、default:该模式自动探测使用构造器自动装配或者byType自动装配 。 首先会
尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在
bean内部没有找到相应的 构造器或者是无参构造器,容器就会自动选择 byTpe
的自动装配方式 。 

 比如如下注入 :

 可以改造为: 

 

 1.6 你用过哪些重要的Spring注解? 

 1、@Component- 用于服务类 

@Service
@Repository
@Controller

 2、@Autowired - 用于在 spring bean 中自动装配依赖项。通过类型来实现自动
注入bean。和@Qualifier注解配合使用可以实现根据name注入bean。


3、@Qualifier - 和@Autowired一块使用,在同一类型的bean有多个的情况下可
以实现根据name注入的需求。


4、@Scope - 用于配置 spring bean 的范围。


5、@Configuration,@ComponentScan 和 @Bean - 用于基于 java 的配置。


6、@Aspect,@Before,@After,@Around,@Pointcut - 用于切面编程(AOP) 

 

 

 1.7 Spring中的事务是如何实现的 

 

 Spring支持编程式事务管理和声明式事务管理两种方式!
编程式事务控制:需要使用TransactionTemplate来进行实现,这种方式实现对业
务代码有侵入性,因此在项目中很少被使用到。


声明式事务管理:声明式事务管理建立在AOP之上的。其本质是通过AOP功能,
对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在
目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者
回滚事务。


声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需
在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可
以将事务规则应用到业务逻辑中。 

 

 1.8 Spring中事务失效的场景? 

 因为Spring事务是基于代理来实现的,所以某个加了@Transactional的⽅法只有
是被代理对象调⽤时, 那么这个注解才会⽣效 , 如果使用的是目标对象调用, 那
么@Transactional会失效


同时如果某个⽅法是private的,那么@Transactional也会失效,因为底层cglib是
基于⽗⼦类来实现 的,⼦类是不能重载⽗类的private⽅法的,所以⽆法很好的
利⽤代理,也会导致@Transactianal失效


如果在业务中对异常进行了捕获处理 , 出现异常后Spring框架无法感知到异常,
@Transactional也会失效


@Transactional中的配置的Rollback的默认是异常是:runTimeException,更改为

 

Spring的事务传播行为 Exception 

 1. PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当
前存在事务,就 加入该事务,该设置是最常用的设置。


2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事
务,如果当前不 存在事务,就以非事务执行。


3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该
事务,如果当前 不存在事务,就抛出异常。


4. PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创
建新事务。


5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事
务,就把当前 事务挂起。


6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异
常。


7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当
前没有事务,则 按REQUIRED属性执行 

 1.10 什么是AOP , 你们项目中有没有使用到AOP 

 AOP一般称为面向切面编程,作为面向对象的一种补充,用于 将那些与业务无
关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模
块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块
间的耦合度,同时 提高了系统的可维护性。
在我们的项目中我们自己写AOP的场景其实很少 , 但是我们使用的很多框架的功
能底层都是AOP , 例如 :
1、统一日志处理
2、spring中内置的事务处理 

记录操作日志
需求:是谁,在什么时间,修改了数据(修改之前和修改之后),删除了什
么数据,新增了什么数据
要求:方法命名要规范
实现步骤:
1,定义切面类
2,使用环绕通知,根据方法名和参数,记录到表中

 

 1.11 JDK动态代理和CGLIB动态代理的区别 

 Spring中AOP底层的实现是基于动态代理进行实现的。
常见的动态代理技术有两种:JDK的动态代理和CGLIB。
两者的区别如下所示:
1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类
2、Cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
进行增强,但是因为采用的是继承,所以该类或方法最好不要声明为final,对于
final类或方法,是无法继承的。
Spring如何选择是用JDK还是cglib?
1、当bean实现接口时,会用JDK代理模式
2、当bean没有实现接口,会用cglib实现
3、可以强制使用cglib 

 在springboot项目可以配置以下注解,强制使用cglib 

 @EnableAspectJAutoProxy(proxyTargetClass = true)
 

 

 spring的bean的生命周期 

 bean 的生命周期从调用 beanFactory 的 getBean 开始,到这个 bean 被销毁,可
以总结为以下七个阶段:
1. 处理名称,检查缓存
2. 处理父子容器
3. 处理 dependsOn
4. 选择 scope 策略
5. 创建 bean (关键阶段)
6. 类型转换处理
7. 销毁 bean 

 

 spring框架在创建的bean的时候都会调用AbstractBeanFactory类中的doGetBean
方法

 1. 处理名称,检查缓存
  这一步会处理别名,将别名解析为实际名称

对 FactoryBean 也会特殊处理,如果以 & 开头表示要获取 FactoryBean 本身,否则表示要获取其产品


这里针对单例对象会检查一级、二级、三级缓存
  singletonFactories 三级缓存,存放单例工厂对象
  earlySingletonObjects 二级缓存,存放单例工厂的产品对象
    如果发生循环依赖,产品是代理;无循环依赖,产品是原始对象
  singletonObjects 一级缓存,存放单例成品对象
  2. 处理父子容器
  如果当前容器根据名字找不到这个 bean,此时若父容器存在,则执行父容器的 getBean 流程
  
  父子容器的 bean 名称可以重复 

 3. 处理 dependsOn
    如果当前 bean 有通过 dependsOn 指定了非显式依赖的 bean,这一步会提前创建这些 dependsOn 的 bean
    
    所谓非显式依赖,就是指两个 bean 之间不存在直接依赖关系,但需要控制它们的创建先后顺序 

 

 4. 选择 scope 策略
  对于 singleton scope,首先到单例池去获取 bean,如果有则直接返回,没有再进入创建流程

  对于 prototype scope,每次都会进入创建流程


  对于自定义 scope,例如 request,首先到 request 域获取 bean,如果有则直接返回,没有再进入创建流程 
   

 5.1 创建 bean - 创建 bean 实例 

要点 总结
AutowiredAnnotationB
eanPostProcessor
① 优先选择带 @Autowired 注解的构造;② 若有
唯一的带参构造,也会入选
采用默认构造 如果上面的后处理器和 BeanDefiniation 都没找到构
造,采用默认构造,即使是私有的

 5.2 创建 bean - 依赖注入 

要点 总结
AutowiredAnnotation
BeanPostProcessor
识别 @Autowired 及 @Value 标注的成员,封装为
InjectionMetadata 进行依赖注入
CommonAnnotationBe
anPostProcessor
识别 @Resource 标注的成员,封装为
InjectionMetadata 进行依赖注入
AUTOWIRE_BY_NAME 根据成员名字找 bean 对象,修改 mbd 的
propertyValues,不会考虑简单类型的成员
AUTOWIRE_BY_TYPE 根据成员类型执行 resolveDependency 找到依赖注入
的值,修改 mbd 的 propertyValues
要点 总结
applyPropertyValues 根据 mbd 的 propertyValues 进行依赖注入(即xml中
<property name ref|value/>)

 5.3 创建 bean - 初始化 

要点 总结
内置 Aware
接口的装配
包括 BeanNameAware,BeanFactoryAware 等
扩展 Aware
接口的装配
由 ApplicationContextAwareProcessor 解析,执行时机在
postProcessBeforeInitialization
@PostConstr
uct
由 CommonAnnotationBeanPostProcessor 解析,执行时机在
postProcessBeforeInitialization
InitializingBe
an
通过接口回调执行初始化
initMethod 根据 BeanDefinition 得到的初始化方法执行初始化,即
<bean init-method> 或 @Bean(initMethod)
创建 aop 代
由 AnnotationAwareAspectJAutoProxyCreator 创建,执行时机
在 postProcessAfterInitialization

 

 5.4 创建 bean - 注册可销毁 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 方法执行销毁
同一 bean 中不同形式销毁方法的调用次序
优先后处理器销毁,即 @PreDestroy
其次 DisposableBean 接口销毁
最后 destroyMethod 销毁(包括自定义名称,推断名称,AutoCloseable 接
口 多选一)
 

标签:事务,销毁,Spring,bean,依赖,创建,原理,真正
From: https://www.cnblogs.com/DarylJi/p/17581668.html

相关文章

  • SpringBoot+Prometheus+Grafana实现系统可视化监控
    场景SpringBoot中集成Actuator实现监控系统运行状态:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/124272494基于以上Actuator实现系统监控,还可采用如下方案。PrometheusPrometheus,是一个开源的系统监控和告警的工具包,其采用Pull方式采集时间序列的度量数据(也......
  • (笔记)电流趋肤效应原理
     趋肤效应的原因非常简单:电流总是沿着阻抗低的方向分布。好比一条河流,沿着河流正中间有一大块石头,那么原本需要从中间流过的一部分水就会被挤到了旁边。我们首先需要明确一点,如果考虑单独的一个导体:其阻抗并非只有导体的电阻,而是由电阻,感抗构成的;——“容抗”需要......
  • 我开源了团队内部基于SpringBoot Web快速开发的API脚手架v1.6.0更新
    什么是rest-api-spring-boot-starterrest-api-spring-boot-starter适用于SpringBootWebAPI快速构建让开发人员快速构建统一规范的业务RestFullAPI不在去关心一些繁琐。重复工作,而是把重点聚焦到业务。动机每次WebAPI常用功能都需要重新写一遍。或者复制之前的项目代码......
  • springboot 解决高并发下的商品少卖多卖的问题
    1.商品秒杀-超卖在开发中,对于下面的代码,可能很熟悉:在Service里面加上@Transactional事务注解和Lock锁。控制层:Controller@ApiOperation(value="秒杀实现方式——Lock加锁")@PostMapping("/start/lock")public Result startLock(long skgId){    try {        ......
  • springboot整合junit
       ......
  • 多环境开发兼容问题(Maven与Springboot)
          ......
  • spring-boot-yaml的用法
    1.yaml简洁以数据为核心·基本语法大小写敏感数值前必须要有空格,作为分割符·数据格式对象数组(使用“-”表示数组每个元素)常量·参数引用$server:port:2023#数据的定义name:lisi#对象的定义person:name:${lisi}age:12......
  • SpringMVC
    SpringMVC简介SpringMVC是一种基于Java实现MVC模型的轻量级Web框架。优点:使用简单,开发便捷(相比于Servlet)灵活性强入门案例【第一步】创建web工程(Maven结构);在pom.xml设置tomcat服务器,加载web工程(tomcat插件)1<build>2<plugins>3<plugin>4......
  • springboot五
    SpringBoot集成Thymeleaf模板引擎1.Thymeleaf介绍Thymeleaf是适用于Web和独立环境的现代服务器端Java模板引擎。Thymeleaf的主要目标是为开发工作流程带来优雅的自然模板,既可以在浏览器中正确显示的HTML,也可以用作静态原型,从而在开发团队中实现更强大的协作。传统......
  • day119 - spring-获取bean
    获取bean根据id获取上一篇的入门文章讲解的就是根据id获取bean的方式根据类型获取@TestpublicvoidtestIOC(){//获取ioc容器ApplicationContextioc=newClassPathXmlApplicationContext("spring-ioc.xml");//获取beanStudentstudent=ioc.getBean......