Spring相关知识点

组成

  • aop
  • beans
  • context
  • core
  • jdbc
  • test
  • web

生命周期

分为四个阶段

  • 实例化
  • 属性赋值
  • 初始化
  • 销毁
1
2
3
4
5
6
7
8
9
doCreateBean() {
// InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation
createBeanInstance() // 实例化
// InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation
populateBean() // 属性赋值
// BeanPostProcessor.postProcessBeforeInitialization
initializeBean() // 初始化
// BeanPostProcessor.postProcessAfterInitialization
}

Aware 都是在初始化阶段之前调用的

设计模式

  • 单例:Bean 默认为单例模式
  • 工厂:BeanFactory 就是简单工厂模式的体现,用来创建对象的实例
  • 代理:Spring 的 AOP 功能用到了 JDK 的动态代理和 CGLIB 的字节码生成技术
  • 模板方法:用来解决代码重复的问题,比如 RestTemplate,JpaTemplate
  • 观察者:当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被强制更新,比如 ApplicationListener

事务传播行为

指的是当多个事务同时存在的时候,Spring 如何处理这些事务的行为

propagation:

  • required:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,最常用
  • requires_new:无论当前存不存在事务,都创建新事务
  • supports:如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行
  • not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
  • never:以非事务方式执行,如果当前存在事务,则抛出异常
  • nested:如果当前存在事务,则在嵌套事务中执行,如果当前没有事务,则按 required 属性执行
  • mandatory:如果当前存在事务,就加入,不存在则抛出异常

隔离级别

  • isolation_default
  • isolation_read_uncommitted
  • isolation_read_committed
  • isolation_repeatable_read
  • isolation_serializable

Bean 的作用域

  • singleton:bean 在每个 Spring IoC 容器中只有一个实例
  • prototype:一个 bean 的定义可以有多个实例
  • request:每次 http 请求都会创建一个 bean,该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效
  • session:在一个 http session 中,一个 bean 定义对应一个实例,该作用域仅在基于 web 的 Spring ApplicationContext 情形下有效
  • global-session:在一个全局的 http session 中,一个 bean 定义对应一个实例,该作用域仅在基 web 的 Spring ApplicationContext 情形下有效

默认是 singleton,使用 prototype 作用域需要慎重的考虑,因为频繁创建和销毁 bean 会带来很大的性能开销

单例 bean 是线程安全的吗

不是,Spring 框架中的单例 bean 不是线程安全的

Spring 中的 bean 默认是单例模式,Spring 框架并没有对单例 bean 进行多线程的封装处理

实际上大部分时候 Spring bean 是无状态的(比如 dao),所以某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view mode 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把 singleton 改成 prototype 这样请求 bean 相当于 new bean,就可以保证线程安全

  • 有状态就是有数据存储功能
  • 无状态就是不会保存数据

那 Spring 如何处理线程并发问题的?

在一般情况下,只有无状态的 bean 才可以在多线程环境下共享,在 Spring 中,绝大部分 bean 都可以声明为 singleton 作用域,因为 Spring 对一些 bean 中非线程安全状态采用 ThreadLocal 进行处理,解决线程安全问题

ThreadLocal 和线程同步机制都是为了解决多线程中相同变量的访问冲突问题,同步机制采用了时间换空间的方法,仅提供一份变量,不同的线程在访问前需要先获得锁,没获得锁的线程则需要排队

而 ThreadLocal 采用了空间换时间的方式,ThreadLocal 会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突,因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了,ThreadLocal 提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进 ThreadLocal

常用注解

  • @Component:将 Java 类标记为 bean,是任何 Spring 管理组件的通用型,Spring 的组件扫描机制扫描并用 IoC 容器进行管理
  • @Controller:将类标记为 Spring Web MVC 控制器,会自动导入到 IoC 容器中
  • @Service:基本和 @Component 一样,只是用在 service 层的话,会更好地见名知意
  • @Repository:除了具有 @Component 的作用外,还具有额外的作用,可以将未经检查的异常转换为 Spring DataAccessException

@Autowired 和 @Resource 的区别

  • 官方标准
    • @Resource:JSR250,按名称或类型注入,不支持 @Primary
    • @Inject:JSR330,默认按类型注入,配合 @Qualifier 可实现按名称注入,配合@Primary 可选择优先注入
    • @Autowired:Spring 自己的实现,默认按类型注入,配合 @Qualifier 可实现按名称注入,配合 @Primary 可选择优先注入

事务隔离级别

除了 MySQL 四种事务隔离级别外,多了个默认,就是数据库是什么就是什么

  • isolation_default
  • isolation_read_uncommitted
  • isolation_read_committed
  • isolation_repeatable_read
  • isolation_serializable

IoC 容器

IoC 就是控制反转,它把传统的有程序代码直接 new 的对象的调用权交给 Spring 容器,通过容器来实现对象组件的装配和管理,所谓的控制反转就是对组件对象控制权的转移,从程序代码本身转移到了外部容器

Spring IoC 负责创建对象,管理对象,装配对象,配置对象,并且管理这些对象的整个生命周期

IoC 有什么作用?

  • 管理对象的创建和依赖关系的维护,对象的创建并不是一件简单的事,在对象管理比较复杂时,如果依赖关系需要程序代码来维护,比较痛苦
  • 解耦,由容器去维护具体的对象
  • 托管了类的产生过程,比如我们需要在类的产生过程中做一些处理,最直接的例子就是代理,如果有容器程序可以把这部分处理交给容器,应用程序则无需去关心类是如何完成代理的

循环依赖

A 有 B,B 有 A
Bean 的创建过程:

  • 获取 bean 定义
  • 通过反射创建原始对象
  • 填充属性(这一步就是依赖注入)
  • 完成对象的创建放到一级缓存

A 创建的时候,通过 bean 的定义创建了一个原始对象,然后将 lambda 表达式放到三级缓存中,接着进行属性的填充,填充的时候发现 B 还没有,于是按照同样的方式来创建 B,等到 B 进行属性注入的时候,分别从一级,二级,三级缓存里面找 A,最后在三级缓存里面找到,于是将 A 放到二级缓存中,并从三级缓存中删除 A,此时的 A 是一个对象并未完成初始化好的对象,但是已经创建出来了,那么 B 属性注入成功,就放到一级缓存中,接着回到 A 的属性注入,注入完成 B 后,A 也完成了创建,就放到一级缓存中,并从二级缓存中删掉 A

为什么需要三级缓存来解决循环依赖?

如果是普通的对象,用一级缓存就可以,但是 Spring 的对象很多都是动态代理增强的,三级缓存里存的是一个 lambda 表达式,根据对象是否有动态代理的需求,进行包装返回,这时的对象是未完成初始化的,如果不放到二级缓存直接放到一级缓存,一级缓存里会出现既有初始化完成的 bean,又有未初始化完成的,管理起来就可能会出现不可预知的错误,也不符合单一职责原则。在缓存 bean 的过程中,三个级别的缓存都是互斥的,最终都会只保留一份完成初始化的 bean,放到一级缓存里

SpringMVC 工作流程

  • 用户发送请求到前端控制器 DispatcherServlet
  • DispatcherServlet 收到请求后,调用 HandlerMapping 处理器映射器请求获取 Handler
  • HandlerMapping 根据请求 url 找到具体的处理器,生成处理器对象及处理器拦截器,返回给 DispatcherServlet
  • DispatcherServlet 调用 HandlerAdapter 处理器适配器
  • HandlerAdapter 经过适配调用具体处理器 Handler,也叫后端控制器
  • Handler 执行完成,返回 ModelAndView
  • HandlerAdapter 将 Handler 执行结构 ModelAndView 返回给 DispatcherServlet
  • DispatcherServlet 将 ModelAndView 传给 ViewResolver 视图解析器进行解析
  • ViewResolver 解析后返回具体 View
  • DispatcherServlet 对 View 进行渲染视图,即将模型数据填充至视图中
  • DispatcherServlet 响应用户

解决跨域

跨域可以在前端通过 JSONP 来解决,但是 JSNOP 只可以发送 GET 请求,在 RESTful 风格的应用中,就显得非常鸡肋,因此推荐在后端通过 CORS Cross-Origin Resource Sharing 来解决跨域问题,Spring Boot 中可以通过实现 WebMvcConfigurer 接口,然后重写 addCordMappings 方法来解决跨域问题