AOP
1、什么是AOP?
AOP概念:
- 面向切面编程,主要将一些与业务代码不相关,但却对多个对象产生影响的公共行为和逻辑,抽取到一个独立的模块中,让业务逻辑更加清爽。
AOP好处:
- AOP 可以将遍布应用各处的功能分离出来形成可重用的组件。
- 在编译期间、装载期间或运行期间实现在不修改源代码的情况下给程序动态添加功能。从而实现对业务逻辑的隔离,提高代码的模块化能力。
2、AOP实现原理?
实现 AOP 的技术,主要分为两大类:
- 静态代理 - 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;
- 动态代理 - 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。目前Spring中使用了两种代理库:
- JDK 动态代理;
- CGLIB动态代理。
那么Spring什么时候使用JDK动态代理,什么时候使用CGLIB呢?
- Spring AOP部分使用JDK动态代理或则CGLIb来为目标对象创建代理(建议尽量使用JDK的动态代理);
- 如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都被该代理;
- 若该目标对象没有实现任何接口,则创建一个CGLIB代理。
SpringAOP中的动态代理主要两种方式:
- JDK 动态代理:通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口 。JDK 动态代理的核心是 InvocationHandler 接口和 Proxy 类 。
- CGLIB动态代理: 如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类 。CGLIB ( Code Generation Library ),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意, CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final ,那么它是无法使用 CGLIB 做动态代理的。
动态代理好处: 一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。
事务
1、Spring事务?
- Spring 事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,Spring 是无法提供事务功能的。
- Spring 只提供统一事务管理接口,具体实现都是由各数据库自己实现,数据库事务的提交和回滚是通过数据库自己的事务机制实现。
- 事务是逻辑上的一组操作,要么都执行,要么都不执行。如果任意一个操作失败,那么整组操作即为失败,会回到操作前状态或者是上一个节点。
2、Spring 事务的种类?
Spring 支持编程式事务管理和声明式事务管理两种方式:
- 编程式事务
- 编程式事务管理使用 TransactionTemplate,需要显式执行事务。
- 声明式事务
- 声明式事务管理建立在 AOP 之上的。其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
- 声明式事务管理建立在 AOP 之上的。其本质是通过 AOP 功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前启动一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
- 优点是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过 @Transactional 注解的方式,便可以将事务规则应用到业务逻辑中,减少业务代码的污染。
- 唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
3、Spring的事务隔离级别?
Spring的接口TransactionDefinition中定义了表示隔离级别的常量,当然其实主要还是对应数据库的事务隔离级别:
- ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,MySQL 默认可重复读,Oracle 默认读已提交。
- ISOLATION_READ_UNCOMMITTED:读未提交
- ISOLATION_READ_COMMITTED:读已提交
- ISOLATION_REPEATABLE_READ:可重复读
- ISOLATION_SERIALIZABLE:串行化
4、Spring事务的传播机制?
事务传播机制概念:
- Spring 事务的传播机制说的是,当多个事务同时存在的时候——一般指的是多个事务方法相互调用时,Spring 如何处理这些事务的行为。
- 事务传播机制是使用简单的 ThreadLocal 实现的,所以,如果调用的方法是在新线程调用的,事务传播实际上是会失效的。
7种事务传播机制:
事务传播机制 |
描述 |
REQUIRED |
支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择 |
SUPPORTS |
支持当前事务,如果当前没有事务,就以非事务方式执 |
MANDATORY |
支持当前事务,如果当前没有事务,就抛出异常 |
REQUIRES_NEW |
新建事务,如果当前存在事务,把当前事务挂起 |
NOT_SUPPORTED |
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起 |
NEVER |
以非事务方式执行,如果当前存在事务,则抛出异常 |
NESTED |
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作 |
5、Spring事务的实现原理?
实现原理:
- 事务的操作本来应该由数据库进行控制,但是为了方便用户进行业务逻辑的控制,spring对事务功能进行了扩展实现。一般我们很少使用编程式事务,更多的是使用@Transactional注解实现。当使用了@Transactional注解后事务的自动功能就会关闭,由spring帮助实现事务的控制。
- Spring的事务管理是通过AOP代理实现的,对被代理对象的每个方法进行拦截,在方法执行前启动事务,在方法执行完成后根据是否有异常及异常的类型进行提交或回滚。
- 当在某个类或者方法上使用@Transactional注解后,Spring会基于该类生成一个代理对象,并将这个代理对象作为bean。当调用这个代理对象的方法时,如果有事务处理,则会先关闭事务的自动功能,然后执行方法的具体业务逻辑,如果业务逻辑没有异常,那么代理逻辑就会直接提交,如果出现任何异常,那么直接进行回滚操作。当然我们也可以控制对哪些异常进行回滚操作。
6、Spring事务的失效场景?
Spring事务的常见失效场景:
- 数据库不支持
- Spring事务是业务层的事务,其底层还是依赖于数据库本身的事务支持。比如 MySQL数据库,MyISAM引擎不支持事务而 InnoDB 引擎支持事务。
- 类不受 Spring管理
- 事务需要的 Advisor等资源是在 Spring创建代理类时去创建的,如果类就不受 Spring容器管理,那么事务需要的 Advisor资源也就无法生成,事务自然就失效了。
- 事务方法不是 public
- @Transactional注解只能用于 public的方法上,否则事务不会生效。
- 异常被业务代码 catch
- 捕获异常后,却未抛出异常,在事务方法中使用try-catch,导致异常无法抛出,Spring 自然就 catch不到异常,因此回滚的逻辑也就不会执行,事务自然就失效了。
- rollbackFor属性配置错误
- 通过 Transactional注解的源码,我们可以发现 rollbackFor属性指定的异常必须是 Throwable及其子类时事务才会回滚,并且在默认情况下,Spring 对 RuntimeException 和 Error 两种异常会自动捕获并回滚事务,也就是说,如果业务抛出来的异常是 RuntimeException 和 Error 类型,可以不需要通过 rollbackFor属性指定,Spring默认会识别处理。
- 如果业务代码在 @Transactional注解里指定了 rollbackFor = Error.class,但是代码中抛出的异常却是 Exception(throw new Exception()),而 Exception 和 Error 没有任何关系,也就是说,代码告诉 Spring,当捕获到 Error 时需要回滚事务,可是偏偏抛出一个和 Error 不相关的 Exception异常,因此事务自然无效,不能回滚。
- 方法内部调用事务方法
- 如果我们在一个方法的内部通过this.的方式去调用一个加了 @Transactional注解的方法,事务也会失效,因为事务是通过 Spring AOP 代理来实现的, this对象调用属于AOP代理。