1、事务的传播机制
propagation_requierd:Spring默认的事务传播机制,如果当前没有事务,就自己新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作
2、事务失效的场景
Spring事务的本质其实就是数据库对事务的支持,spring事务的原理是AOP,进行了切面增强,那么失效的根本原因是这个AOP不起作用!常见情况如下几种:
1、发生是调用,类里面使用this调用本类发方法(this通常省略),此时这个this对象不是代理类,而是UserService对象本身!
解决办法很简单,让那个this变成UserService的代理类即可!
2方法不是public修饰的
@Transactional 只能用于public 的方法否者事务不会生效,如果要用在非public 方法上,可以开启
AspectJ 代理模式
3、数据库不支持事务
InnoDB支持事务,MyISAM不支持,这一点是非常之重要。事务是一种高级的处理方式,如在一些列增删改中只要哪个出错还可以回滚还原,而MyISAM就不可以了。
4、没有被spring管理、
// @Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
// update order;
}
}
如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。
。
5、异常被catch掉,事务不会回滚(或者抛出的异常没有被定义,默认为RuntimeExcelption)
3、事务的隔离机制
隔离机制
我们说的事务隔离级别是指并发事务,操作相同的数据,引发的问题,数据库针对这种业务场景,满足不同的业务诉求,提供四种隔离级别:
事务的隔离级别就是一种控制并发执行的事务对数据操作的规则 ISOLATION
1、读未提交(Read Uncommitted):
一个事务读到另一个事务还没有提交的数据。如果另一个事务失败了,回滚了,那么第一个事务读到的数据就是无效的数据。这就是脏读。
场景:屌丝A操作存款业务,一次性将工资50000都存到LP指定的账户上,LP这时刚好查看到了账户信息,心中充满喜悦;屌丝A在事务提交之前,突然发现忘记存私房钱了,于是赶紧撤销,改成存3000.哈哈。。。这个业务场景中LP读到的数据就是脏数据,俗称脏读。
2,读已提交(Read Committed):
这是大多数数据库系统的默认隔离级别(但不是mysql默认的)
一个事务只能看见已经提交事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。
该隔离级别可能导致的问题是不可重复读。因为两次执行同样的查询,可能会得到不一样的结果。
不可重复读,不可重复读发生在一个事务里两次查询同一个数据,发现查询结果不一样。这是因为在该事务的两次查询之间,有另一个事务更新了该数据。
场景:屌丝A的LP在查看屌丝Ade账户余额时,发现只有50元,这时屌丝A在对账户进行转账操作,存入3000元,屌丝A的LP重新查询时,发现金额变为3050。
3、可重复读(Repeatable Read):
幻读,幻读和不可重复读比较类似。当一个事务t1,第一次查询了一些行,第二次又查询了一些行,发现两次查询出来的行个数不一样,或者多了一些行,或者少了一些行,好像出现了幻觉一样。这是因为在两次查询之间,有另一事务更新了符合第一个事务查询条件的行。
场景:屌丝A在做指定条件的更新操作,更新只有,又有相同条件的数据在插入,当屌丝A在查询时,发现有部分数据没有更新掉,像是产生的幻觉。
可重复读,顾名思义,就是专门针对“不可重复读”这种情况而制定的隔离级别,自然,它就可以有效的避免“不可重复读”。而它也是MySql的默认隔离级别。
在这个级别下,普通的查询同样是使用的“快照读”,但是,和“读提交”不同的是,当事务启动时,就不允许进行“修改操作(Update)”了,而“不可重复读”恰恰是因为两次读取之间进行了数据的修改,因此,“可重复读”能够有效的避免“不可重复读”,但却避免不了“幻读”,因为幻读是由于“插入或者删除操作(Insert or Delete)”而产生的。
4、串行化(Serializable):
这是最高的隔离级别
它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。通俗地讲就是,假如两个事务都操作到同一数据行,那么这个数据行就会被锁定,只允许先读取/操作到数据行的事务优先操作,只有当事务提交了,数据行才会解锁,后一个事务才能成功操作这个数据行,否则只能一直等待,该隔离级别可能导致大量的超时现象和锁竞争。
4、Spring缓存
缓存
Spring中有三个缓存,用于存储单例的Bean实例,这三个缓存是彼此互斥的,不会针对同一个Bean的实例同时存储。
一级缓存:Map singletonObjects
-
第一级缓存的作用?
1、用于存储单例模式下创建的Bean实例(已经创建完毕)。
2、该缓存是对外使用的,指的就是使用Spring框架的程序员。 -
存储什么数据?
1、 K:bean的名称
2、V:bean的实例对象(有代理对象则指的是代理对象,已经创建完毕)
二级缓存:Map earlySingletonObjects
-
第二级缓存的作用?
1、 用于存储单例模式下创建的Bean实例(该Bean被提前暴露的引用,该Bean还在创建中)。
2、该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。
3、为了解决第一个classA引用最终如何替换为代理对象的问题(如果有代理对象)请爬楼参考演示案例 -
存储什么数据?
1、K:bean的名称
2、V:bean的实例对象(有代理对象则指的是代理对象,该Bean还在创建中)
三级缓存 singletonFactories
-
第三级缓存的作用?
通过ObjectFactory对象来存储单例模式下提前暴露的Bean实例的引用(正在创建中)。该缓存是对内使用的,指的就是Spring框架内部逻辑使用该缓存。此缓存是解决循环依赖最大的功臣 -
存储什么数据?
1、K:bean的名称
2、V:ObjectFactory,该对象持有提前暴露的bean的引用
循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。如下图:
Spring通过将实例化后的对象提前暴露给Spring容器中的singletonFactories,解决了循环依赖的问题。
Spring只解决了单例模式下的属性注入的循环依赖
5、Spring IOC
Spring IOC是控制反转,就是我们以前创建对象都是使用new关键字,Spring IOC就是把创建对象的权利交给Spring容器去管理
IOC即控制反转,就是以前我们创建对象都是需要用new关键字去new一个对象,而控制反转就是我们将创建对象的权利交给Spring容器去管理
实现原理:利用了java的反射机制
Java反射机制定义
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java 反射机制的功能
1.在运行时判断任意一个对象所属的类。
2.在运行时构造任意一个类的对象。
3.在运行时判断任意一个类所具有的成员变量和方法。
4.在运行时调用任意一个对象的方法。
5.生成动态代理。
6、Spring Aop
AOP 叫做面向切面编程,他是一个编程范式,目的就是提高代码的模块性。Srping AOP 基于动态代理的方式实现,如果是实现了接口的话就会使用 JDK 动态代理,反之则使用 CGLIB 代理,Spring中 AOP 的应用主要体现在 事务、日志、异常处理等方面,通过在代码的前后做一些增强处理,可以实现对业务逻辑的隔离,提高代码的模块化能力,同时也是解耦。Spring主要提供了 Aspect 切面、JoinPoint 连接点、PointCut 切入点、Advice 增强等实现方式。
7、SpringMVC的访问流程
8、Spring当中用到了那些设计模式
单例、工厂、代理
9、spring 中的 bean 是线程安全的吗?
这个得根据bean的类型来说了
对于原型Bean(prototype:原型),每次创建一个新对象,也就是线程之间并不存在Bean共享,自然是不会有线程安全的问题。
对于单例Bean,所有线程都共享一个单例实例Bean,因此是存在资源的竞争。
如果单例Bean(singleton:单例),是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Spring mvc 的 Controller、Service、Dao等,这些Bean大多是无状态的,只关注于方法本身。
对于有状态的bean,Spring官方提供的bean,一般提供了通过ThreadLocal去解决线程安全的方法,比如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等。
注: Spring容器本身并没有提供线程安全的策略,因此是否线程安全完全取决于Bean本身的特性。
10、Spring 中Bean注入的几种方式
1、Setter方法注入,是容器通过调用无参构造器或无参static工厂 方法实例化bean之后,调用该bean的setter方法。
public class B {
private A a;
public void setA(A a){
this.a == a;
}
}
2、构造方法注入。
同set方法注入一样,首先把需要注入的类写成属性,然后使用构造方法。实现代码如下:
public class B {
private A a;
public B(A a){
this.a == a;
}
}
3、注解注入
11、@RequestMapping 的作用是什么?
RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
value:等价于path,可以配置多个地址,用{},主要用于地址的映射
method:请求的method类型, 例如GET、POST、PUT、DELETE等
params:指定request中必须包含某些参数值是,才让该方法处理
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
12、Spring bean的生命生命周期
总体来说分为四个阶段:
实例化–赋值—初始化—销毁
实例化bean对象(通过构造方法或者工厂方法
设置对象属性(setter等)(依赖注入)
如果Bean实现了BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。
如果Bean实现了BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身
将Bean实例传递给Bean的前置处理器的postProcessBeforeInitialization(Object bean, String beanname)方法
调用Bean的初始化方法
将Bean实例传递给Bean的后置处理器的postProcessAfterInitialization(Object bean, String beanname)方法使用Bean
容器关闭之前,调用Bean的销毁方法