Spring 速记

wuchangjian2021-11-15 08:11:59编程学习

Spring

Spring Core 核心模块, Spring 其他所有的功能基本都需要依赖于该类库,主要提供 IoC 依赖注入功能的支持。

Spring Aspects 该模块为与 AspectJ 的集成提供支持。

Spring AOP 提供了面向切面的编程实现。

Spring Data Access
spring-jdbc
spring-tx
spring-jms

Spring Web
spring-web spring-webmvc spring-websocket
spring-webflux : 提供对 WebFlux 的支持。WebFlux 是 Spring Framework 5.0 中引入的新的响应式框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步.

Spring Test


Spring IOC

IOC---- 控制反转 / 反转控制。
是一种 思想 而不是 技术 实现的。
描述: java开发 领域对象的 创建 以及 管理问题。
IOC 容器 是 Spring 用来 实现 IOC 的载体, IOC 容器 实际上 就是个 Map ,Map 中 存放 的 是 各种 对象。

如: 现有类 A 依赖于 类 B

传统的开发方式 :
往往 实在 类A 手动 通过 new 关键字 来 new 一个 B的对象 出来。

使用 IOC 思想 的 开发方式 :
不是 通过 new关键字 来创建 对象 , 而是 通过 IOC 容器(Spring框架)帮助我们 实例化对象 ,需要哪个对象 ,直接 从 IOC 容器里面 取即可。

这两种开发方式 :
我们 失去了 创建和管理对象的能力 ,从而 不用 在 考虑 对象的创建 管理等一系列的事。

** 为什么 叫 控制反转 ? **
控制 : 指的是 对象 的 创建和管理 的 权力。
反转: 指的是 控制权给外部环境 (Spring框架 IOC容器)

** IOC 解决了 什么问题 ? **
IOC 的 思想 就是 双方之间 不存在 依赖关系 ,而是 交由 第三方容器 管理相关资源。

对象之间 的 耦合性 减低。
资源 变得 易于管理。 (比如 使用Spring容器 很容易 实现 一个 单例)


Spring AOP

切 :指的是横切逻辑,原有业务逻辑代码不动,只能操作横切逻辑代码,所以面向横切逻辑

面 :横切逻辑代码往往要影响的是很多个方法,每个方法如同一个点,多个点构成一个面。这里有一个面的概念

AOP 解决了什么问题
通过上面的分析可以发现,AOP 主要用来解决:在不改变原有业务逻辑的情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。

面向切面编程(AOP),在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。
而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。
AOP底层是动态代理,如果是接口采用JDK动态代理,如果是类采用CGLIB方式实现动态代理

AOP编程术语
● 切面(Aspect)

切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice)。实际就是对主业务逻辑的一种增强。

● 织入(Weaving)

织入是指将切面代码插入到目标对象的过程。上例中MyInvocationHandler类中的invoke() 方法完成的工作,就可以称为织入。

● 连接点(JoinPoint)

连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。

● 切入点(Pointcut)

切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。

被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

● 目标对象(Target)

目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。上例中的StudentServiceImpl的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然,不被增强,也就无所谓目标不目标了。

● 通知(Advice)

通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。 切入点定义切入的位置,通知定义切入的时间。

● 代理(Proxying)
代理是通知目标对象后创建的对象。从客户端的角度看,代理对象和目标对象是一样的。


Spring bean

bean 所代理的 就是被 IOC 容器 所 管理的 对象

通过 xml 主键 java配置类 配置

Bean 的 作用域 —>scope

通过

singleton: 单例模式 默认

prototype: 每次请求都会 创建 一个 新的 bean 实例

request: 每一次 HTTP 请求 都会 产生 一个 bean 实例 , 该bean 只在 HTTP request中生效

session: 每一次 新session的 HTTP 请求 都会 产生 一个 bean 实例 , 只在 HTTP session 生效

global-session 全局 session 作用域 ,使用很少

单例模式下 singleton 下的 线程 安全?

主要原因 是 在多线程下 操作 同一对象 的 时候 存在 资源竞争的情况

1.在bean中 避免 定义 可变的成员变量
2.使用 ThreadLocal 定义 成员变量, 将需要的可变成员变量 保存在 ThreadLocal中
3.使用 sychornized 加锁

不过大部分情况下 如 dao service 都是 没有实例变量的, 线程安全的


bean的生命周期
实例化bean 设置对象属性(依赖注入) 处理 Aware 接口 BeanPostProcessor InitialzingBean和 init-method

如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;
由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

DisposableBean destroy-menthod

1.实例化bean

对于 BeanFactory 容器来说, 当 客户向 容器 请求 一个 尚未初始化的 bean时, 或者 初始化bean的时候需要注入另外一个尚未初始化的依赖时,
容器就会 调用 createBean 进行 实例化

对于 AppliactionContext 容器 , 当 容器 启动结束后, 通过 获取 BeanDefinition 对象中的信息,实例化 所有的bean

2.设置对象属性(依赖注入)

实例化后的对象被封装在 BeanWrapper 中 , 然后Spring 通过 BeanDefinition 中的 信息 以及 通过 BeanWapper 提供的 设置属性的接口完成依赖注入

3.处理 Aware 接口

Spring 会 检测 对象 是否 实现了 xxxAware 接口, 并将 相关的 xxxAware 实例 注入Bean

①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String
beanId)方法,此处传递的就是Spring配置文件中Bean的id值;

②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传
递的是Spring工厂自身。

③如果这个Bean已经实现了ApplicationContextAware接口,会调用
setApplicationContext(ApplicationContext)方法,传入Spring上下文;

  1. BeanPostProcessor
    如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会
    调用postProcessBeforeInitialization(Object obj, String s)方法。

5.InitializingBean 与 init-method
如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

6.如果这个Bean实现了BeanPostProcessor接口,将会调用
postProcessAfterInitialization(Object obj, String s)方法;
由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

7.DisposableBean
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现
的destroy()方法;

8.destroy-method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方

总结:
1.容器启动,初始化 bean对象
2.设置对象属性
3.检测是否实现了 xxxAware接口,并将 相关的 xxxAware 实例 注入Bean
如果实现 BeanNameAware 接口 就会调用 setBeanName()方法
如果实现 BeanFactoryAware 接口 就会调用 setBeanFactory()方法
如果实现 ApplicationContextAware 接口 就会调用 setAppliactionContext()方法

4.如果 想对 Bean 做 自定义处理
就让Bean 实现 BeanPostProcessor接口 ,进行前置处理,调用 预初始化方法 postProcessBeforeInitialization(Object obj, String s)方法

5.如果Bean 实现了InitializingBean接口, 执行afeterPropertiesSet()方法

6.如果在配置文件 设置了 init-method 属性,
就会知道调用 其配置的 初始化方法

7.如果实现了 BeanPostProcessor接口 ,进行后置处理,调用 后初始化方法 postProcessBeforeInitialization(Object obj, String s)方法

8.如果 bean 不在需要 ,并且实现了 DisposableBean 接口, 就会调用其实现的 destroy()方法

9.如果Bean配置了 destory-method 属性, 就会自动调用 销毁方法

Spring基于xml注入bean的几种方式:
1.set
2.构造器注入
①通过index设置参数的位置;②通过type设置参数类型;
3.静态工厂注入
4.实例工厂注入

Spring容器的 启动流程:

1.初始化Spring容器,注册内置的 BeanPostProcessor的BeanDefintion到容器中
2.将配置类的BeanDefinition注册到容器中
3.调用refresh()方法刷新容器

1.初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中:
① 实例化BeanFactory【DefaultListableBeanFactory】工厂,用于生成Bean对象
② 实例化BeanDefinitionReader注解配置读取器,用于对特定注解(如@Service、@Repository)的类进行读取转化成
BeanDefinition 对象,
(BeanDefinition 是 Spring 中极其重要的一个概念,它存储了 bean 对象的所有特征信息,如是否单例,是否懒加载,factoryBeanName 等)
③ 实例化ClassPathBeanDefinitionScanner路径扫描器,用于对指定的包目录进行扫描查找 bean 对象


BeanFactory和ApplicationContext有什么区别?

都可以当做 Spring的容器

BeanFactory 是 Spring 最底层的接口, IOC 的核心, 定义了 IOC 的 基本功能
包含了 Bean的定义 加载 实例化 依赖注入和生命周期管理

ApplicationContext 接口 作为 BeanFactory 的子类 , 提供了 除 BeanFactory 功能外, 提供了 更完整的 框架功能

  1. 继承 MessageSource 支持国际化
  2. 资源文件访问, url和文件
  3. 载入多个(继承关系)上下文(即同时配置多个配置文件),使得每一个上下文都专注于特定的层次,如web层
  4. 提供在监听器注册 bean事件

①BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。
这样,我们就不能提前发现一些存在的Spring的配置问题。
如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。

②ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。
能够发现配置的错误, 有利于 检查 所依赖属性 是否注入

③ApplicationContext启动后预载入所有的单实例Bean,所以在运行的时候速度比较快,因为它们已经创建好
但 不足的是 占用内存空间, 当 配置Bean 较多 的时候, 就会出现程序启动慢的情况

BeanFactory和ApplicationContext都支持 BeanPostProcessor、BeanFactoryPostProcessor的使用,
但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册

BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。

Spring的自动装配:
在spring中,使用autowire来配置自动装载模式,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象。

1.在Spring框架xml配置中共有5种自动装配: 以下使用 autowire="xxx"注入

no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean
byName: 通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配
byType: 通过参数的数据类型进行自动装配
constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配
autodete : 自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配

2.基于注解的自动装配方式:
使用@Autowired、@Resource注解来自动装配指定的bean。

在使用@Autowired注解之前需要在Spring配置文件进行配置。
在启动spring IoC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,
当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。

在使用@Autowired时,首先在容器中查询对应类型的bean:
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。

@Autowired可用于:构造函数、成员变量、setter方法
注:@Autowired和@Resource之间的区别:

(1) @Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。

(2) @Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。


Spring如何解决循环依赖问题:

循环依赖问题在Spring中主要有三种情况:

(1)通过构造方法进行依赖注入时产生的循环依赖问题。
(2)通过setter方法进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
(3)通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。

在Spring中,只有第(3)种方式的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。这是因为:

第一种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
第二种setter方法(多例)的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,
最终就会导致OOM问题的出现。
Spring在单例模式下的setter方法依赖注入引起的循环依赖问题,主要是通过二级缓存和三级缓存来解决的,其中三级缓存是主要功臣。
解决的核心原理就是:在对象实例化之后,依赖注入之前,Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。


Spring事务
1.编程式事务管理

1.通过 TransactionTemplate 或者 TransactionManager 手动管理事务

@Autowired
private TransactionTemplate transactionTemplate;

@Autowired
private PlatformTransactionManager transactionManager;

2.声明式事务管理 (AOP 实现 @Transactional )

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

声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过

@Transactional注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。
唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

spring的事务传播机制:
spring事务的传播机制说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
事务传播机制实际上是使用简单的ThreadLocal实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。

① PROPAGATION_REQUIRED:(默认传播行为)如果当前没有事务,就创建一个新事务;如果当前存在事务,就加入该事务。

② PROPAGATION_REQUIRES_NEW:无论当前存不存在事务,都创建新事务进行执行。

③ PROPAGATION_SUPPORTS:如果当前存在事务,就加入该事务;如果当前不存在事务,就以非事务执行。‘

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

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

⑥ PROPAGATION_MANDATORY:如果当前存在事务,就加入该事务;如果当前不存在事务,就抛出异常。

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

Spring中的隔离级别
① ISOLATION_DEFAULT:这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。

② ISOLATION_READ_UNCOMMITTED:读未提交,允许事务在执行过程中,读取其他事务未提交的数据。

③ ISOLATION_READ_COMMITTED:读已提交,允许事务在执行过程中,读取其他事务已经提交的数据。

④ ISOLATION_REPEATABLE_READ:可重复读,在同一个事务内,任意时刻的查询结果都是一致的。

⑤ ISOLATION_SERIALIZABLE:所有事务逐个依次执行。


Spring 框架中都用到了哪些设计模式?
Spring设计模式的详细使用案例可以阅读这篇文章:Spring中所使用的设计模式_张维鹏的博客-CSDN博客_spring使用的设计模式

(1)工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象

(2)单例模式:Bean默认为单例模式

(3)策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略

(4)代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术

(5)模板方法:可以将相同部分的代码放在父类中,而将不同的代码放入不同的子类中,用来解决代码重复的问题。
比如RestTemplate, JmsTemplate, JpaTemplate

(6)适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式,Spring MVC中也是用到了适配器模式适配Controller

(7)观察者模式:Spring事件驱动模型就是观察者模式的一个经典应用。

(8)桥接模式:可以根据客户的需求能够动态切换不同的数据源。
比如我们的项目需要连接多个数据库,客户在每次访问中根据需要会去访问不同的数据库


Spring框架中有哪些不同类型的事件?
Spring 提供了以下5种标准的事件:

(1)上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。

(2)上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。

(3)上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。

(4)上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。

(5)请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。

如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。


SpringMVC 的 工作原理

1.客户端(游览器) 发送请求, 直接请求到 DispatcherServlet
2.DispatcherServlet 根据请求信息 调用 HandlerMapping, 解析相应的 Handler
3.解析到对应的 Handler(也就是 controller处理器),开始 由 HandlerAdapter 适配器处理
4.HandlerAdapter 适配器 就根据 Handler 来调用 真正的 处理器处理请求, 并处理相应的 业务
5.处理器 处理完业务后, 返回一个 ModelAndView 对象, Model 返回的数据对象 , View 逻辑上的视图
6.ViewResolver 根据 View 查找 实际的 View
7.DispatcherServlet 会把 Model 传给 View (视图渲染)
8.把View 返回给 请求者(游览器)

相关文章

flink窗口介绍

1.窗口分类 滚动窗口(滚动时间窗口,滚动计数窗口...

津市绿色新材料产业园一期项目举行开工仪式

津市绿色新材料产业园一期项目举行开工仪式

2022-08-29 13:15:10 近日,湖南省津市市绿色新材料产...

好消息!盼了这么久的降温要来了

好消息!盼了这么久的降温要来了

2022-08-22 17:45:09 7月8日以来的高温过程综合强度...

湘伢子唐凯夺羽量级金腰带,成中国首个男子MMA世界冠军

湘伢子唐凯夺羽量级金腰带,成中国首个男子MMA世界冠军

2022-08-27 13:15:10 北京时间8月26日晚,ONE羽...

快手联合百家政务号发起谣言治理专项行动

2022-08-28 12:25:10 8月26日,快手宣布联合百家政...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。