Spring
一、Spring两大核心
1、IOC
- IOC其实它是一种思想,“控制反转”(官方解释),其实这种思想就是 将原本在我们自己写的程序里面去创建对象的权利,交给Spring框架来管理。
- IOC容器是Spring 用来实现Ioc的载体,Ioc容器实际上就是Map的结构,里面存放的是各种对象
- IOC容器就像一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件 / 注解,完全不用考虑对象是怎么配置出来的。(典型的工厂模式)
2、AOP
- AOP:面向切面编程(说实话,直接这么说是一脸懵逼的),就是将那些与业务无关的,但是又是这个业务模块所共同调用的逻辑对吧,我们就把他们抽取出来,自己封装起来,统一调用。这样极大程度的减少了重复代码的书写,并且降低了系统的耦合度,利于扩展和维护。(典型的应用就是一些日志管理呀,比如说我想要将调用的每一个controller知道他的调用时间,并且入库,对吧,这时候我们写个切面是很好的处理)
- 核心是动态代理,若果代理的对象实现了某个接口,那么spring AOP就会使用JDK动态代理,去创建对象。对于没有实现接口的对象,spring aop会使用Cglib动态代理生成一个被代理的子类作为代理对象
- AspectJ AOP:编译时增强,当切面太多的话,最好选择 AspectJ ,它比Spring AOP 快很多。
- AOP切面是一个实现举例
二、Spring bean
1、Bean的作用域
- singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。
- prototype : 每次请求都会创建一个新的 bean 实例。
- request : 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。
- session : 每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效。
2、线程安全问题
- 多个线程操作同一个对象的时候,对这个对象的成员变量的写操作会存在线程安全问题。
- 一般情况下,我们常用的
Controller
、Service
、Dao
这些 Bean 是无状态的。无状态的 Bean 不能保存数据,因此是线程安全的。 - 解决办法:
- 在类中定义一个
ThreadLocal
成员变量,将需要的可变成员变量保存在ThreadLocal
中(推荐的一种方式)。 - 改变 Bean 的作用域为 “prototype”:每次请求都会创建一个新的 bean 实例,自然不会存在线程安全问题。
- 在类中定义一个
3、Bean的生命周期
- Bean 容器(IOC容器)首先找到配置文件的中的Spring Bean的定义
- Bean 容器利用反射来创建Bean的一个实例
- 如果涉及到一些属性,那么就利用
set()
方法来设置一些属性值 - 如果 Bean 实现了
BeanNameAware
接口,调用setBeanName()
方法,传入Bean的名字。 - 如果 Bean 实现了
BeanClassLoaderAware
接口,调用setBeanClassLoader()
方法,传入ClassLoader
对象的实例。 - 如果实现了其他
*.Aware
接口,就调用相应的方法。 - 如果有和加载这个 Bean 的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessBeforeInitialization()
方法 - 如果Bean实现了
InitializingBean
接口,执行afterPropertiesSet()
方法。 - 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。
- 如果有和加载这个 Bean的 Spring 容器相关的
BeanPostProcessor
对象,执行postProcessAfterInitialization()
方法 - 当要销毁 Bean 的时候,如果 Bean 实现了
DisposableBean
接口,执行destroy()
方法。 - 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。
4、循环依赖问题⭐
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。
如果是我们自己写的,就会无限创建对象,(类似于死循环),导致内存占满报错,那么Spring是如何解决这种情况呢?
Java中的循环依赖分两种,一种是构造器的循环依赖,另一种是属性的循环依赖。
Spring解决的循环依赖就是指属性的循环依赖
首先我们先介绍一下Spring的三级缓存:
- singletonObjects 一级缓存,用于保存实例化、注入、初始化完成的bean实例
- earlySingletonObjects 二级缓存,用于保存实例化完成的bean实例
- singletonFactories 三级缓存,用于保存bean创建工厂,以便于后面扩展有机会创建代理对象。
ps:盗用一张图(发生了什么??):
三、Spring 中的事务问题
其实,在我的理解里面Spring的事务是跟数据库保持一致的,如果我们选择的数据库它不支持事务(比如myisam),那么我们Spring里面的事务就不会起到作用。
1、四种事务特征(ACID)
- 原子性:强调事务的不可分割
- 一致性:事务的执行前后,数据的完整性保持一致
- 隔离性:一个事务执行过程中,不受其他事务的干扰
- 持久性:事务一旦执行结束,数据就持久到数据库,不会因为数据库宕机而发生数据修改失败等
2、五种隔离级别(比数据库多了一个默认级别)
TransactionDefinition.ISOLATION_DEFAULT
:使用数据库默认的事务隔离级别TransactionDefinition.ISOLATION_READ_UNCOMMITTED
:最低的隔离级别,使用这个隔离级别很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读TransactionDefinition.ISOLATION_READ_COMMITTED
: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生TransactionDefinition.ISOLATION_REPEATABLE_READ
: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。TransactionDefinition.ISOLATION_SERIALIZABLE
: 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
3、七种传播行为
- 支持当前事务
PROPAGATION_REQUIRED
:默认的事务传播行为,如果当前存在事务,则加入该事务,如果当时没有事务,则创建一个新事物。PROPAGATION_SUPPORTS
:如果当前事务存在就加入该事务,如果不存在,则以非事务的方式运行PROPAGATION_MANDATORY
:如果当前事务存在就加入,不存在就抛出异常
- 不支持当前事务
PROPAGATION_REQUIRES_NEW
:创建一个新的事务,如果存在当前的事务就挂起这个事务PROPAGATION_NOT_SUPPORTED
:以非事务的方式运行,如果存在当前事务就挂起PROPAGATION_NEVER
:以非事务的方式运行,如果存在当前事务就抛出异常
- 其他
PROPAGATION_NESTED
:嵌套事务,如果当前存在事务则在嵌套事务中执行,如果没有就以PROPAGATION_REQUIRED
方式运行