AOP 概述
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过 预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。 AOP 是 OOP 的延续,是软件开发中的一个热点,也是 Spring 框架中的一个重 要内容。利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各 部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 AOP、OOP 在字面上虽然非常类似,但却是面向不同领域的两种设计思想。OOP (面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获 得更加清晰高效的逻辑单元划分。
而 AOP 则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的 某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设 计思想在目标上有着本质的差异。
面向切面编程的好处就是: 减少重复,专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
核心原理:
使用动态代理的方式在执行方法前后或者出现异常的时候做加入相关的逻辑.
使用案例:
事务处理:开启事务,关闭事务,出现异常后回滚事务
权限判断:在执行方法前,判断是否具有权限
日志:在执行前进行日志处理
AOP 的基本概念
连接点(Joinpoint): 类中可以被增强的方法,这个方法就被称为连接点
切入点(pointcut): 类中有很多方法可以被增强,但实际中只有 add 和 update被增了,那么 add 和 update 方法就被称为切入点(实际实现的连接点)
通知(Advice): 通知是指一个切面在特定的连接点要做的事情(增强的功能)。通 知分为方法执行前通知,方法执行后通知,环绕通知等.
切面(Aspect): 把通知添加到切入点的过程叫切面.
目标(Target): 代理的目标对象(要增强的类)
代理(Proxy): 向目标对象应用通知之后创建的代理对象
springAOP 实现
对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可 以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,AspectJ 是一个基于 Java 语言的 AOP 框架,它提供了强大的 AOP 功能,且其实现方式更为简捷,使用更为方便, 而 且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引 入到了自己的框 架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。 AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切 面实现。
AspectJ 中常用的通知有五种类型:前置通知,后置通知,环绕通知,异常通知,最终通知.
导入其jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
基于 aspectj 的 xml 配置方式实现
<bean id="aopdemo" class="com.ff.spring.aop.AopDemo"></bean>
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* com.ff.spring.service.UserService.adduser(..))" id="adduser"/>
<aop:pointcut expression="execution(* com.ff.spring.service.UserService.*(..))" id="allmethod"/>
<!-- 配置通知和切入点 -->
<aop:aspect ref="aopdemo">
<aop:before method="savelog" pointcut-ref="adduser"/>
<aop:after method="savelog" pointcut-ref="adduser"/>
<aop:around method="aroundAdvice" pointcut-ref="adduser"/>
<aop:after-throwing method="exceptionAdvice" pointcut-ref="allmethod" throwing="e" />
</aop:aspect>
</aop:config>
aop配置文件的内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--吧装有增强功能的类交给spring管理-->
<bean id="aopdemo" class="com.ffyc.spring.aop.AopDemo_back"></bean>
<!--编织: 将连接点与通知编织到一起-->
<aop:config>
<!--id(save)就是想要增强的方法名-->
<!--*是返回值类型 save(..)是参数类型 ..是任意类型-->
<aop:pointcut id="save" expression="execution(* com.ffyc.spring.dao.UserDao.save(..))"/>
<aop:aspect ref="aopdemo">
<!--id(savelog)就是增强代码的内容 aop后的就是前通知、后置通知、异常通知、环绕通知-->
<aop:after method="savelog" pointcut-ref="save"></aop:after><!--后置通知-->
<aop:before method="savelog" pointcut-ref="save"></aop:before> <!--前置通知-->
<aop:after-throwing throwing="e" method="exceptionAdvice" pointcut-ref="save"></aop:after-throwing><!--异常通知-->
<aop:around method="aroundAdvice" pointcut-ref="save"></aop:around>
</aop:aspect>
</aop:config>
</beans>
然后再aop包的AOPDemo里面使用注解的方式在切面增加通知
package com.ffyc.spring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component //让spring创建对象
@Aspect //声名这是一个切面
public class AopDemo {
@Before("execution(* com.ffyc.spring.dao.UserDao.save(..))")
public void savelog(){ //before 在save之前
System.out.println("执行了before编织");
}
@AfterThrowing(value = "execution(* com.ffyc.spring.dao.UserDao.save(..))",throwing = "e")
public void exceptionAdvice(Throwable e){
System.out.println(e.getMessage());
System.out.println("执行了AfterThrowing编织");
}
@Around("execution(* com.ffyc.spring.dao.UserDao.save())")
public void aroundAdvice(ProceedingJoinPoint point){
System.out.println("环绕前通知");
try {
point.proceed();//调用save
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("环绕后通知");
}
}
AopDemo_back类里面
import org.aspectj.lang.ProceedingJoinPoint;
public class AopDemo_back {
/**
* 通知:在连接点行要做的事
* 通知又分为 前置通知 后置通知 异常通知 环绕通知
*/
public void savelog(){ //before 在save之前
System.out.println("保存日志");
}
public void exceptionAdvice(Throwable e){
System.out.println(e.getMessage());
}
public void aroundAdvice(ProceedingJoinPoint point){
System.out.println("前置通知");
try {
point.proceed();//调用save
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println("后置通知");
}
}
Spring 事物管理
事物可以看做是由对数据库若干操作组成的一个单元。
我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作 的结合。由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常, 异常会导致后续操作无法完成,此时由于业务逻辑并未正确的完成,之前成功操 作数据的并不可靠,需要在这种情况下进行回退。
事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都 必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态,这些操作 要么都完成,要么都取消,从而保证数据满足一致性的要求
Spring 中的事务管理分为两种形式,一种是编程式事务,一种是声明式事务.
- 编 程 式 事 务 在 项 目 中 很 少 使 用 , 这 种 方 式 需 要 注 入 一 个 事 务 管 理 对 象 TransactionTemplate ,然后在我们代码中需要提交事务或回滚事务时自己写代码实现.
- 声明式事务管理建立在 AOP 基础上,本质是对方法前后进行拦截,所以声明式 事务是方法级别的。
Spring 声明式事物管理方式有两种:
- 基于 xml 配置
- 注解实现
Spring 事物管理 API,PlatformTransactionManager 事物管理器接口
Spring 针对不同的 dao 框架,提供了不同的实现类,Jdbc,mybatis 事物管理实 现类是 DataSourceTransactionManager
配置事物管理器
<!-- 配置 spring 事务管理类, 并注入数据源 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
spring.xml 配置方式
<tx:advice id="txadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut expression="execution(* com.ffyc.spring.dao.UserDao.save())" id="allmethod"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="allmethod"/>
</aop:config>
注解方式:
<!-- 开启注解事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
在 service 中控制事务
@Service(value = "userService")
@Transactional(propagation=Propagation.REQUIRED)
Spring 事务传播行为
什么叫事务传播行为?
即然是传播,那么至少有两个东西,才可以发生传播。单体不存在传播这个行为。 事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事 务方法调用时,这个事务方法应该如何进行。事务传播行为是 Spring 框架独有 的事务增强特性,他不属于的事务实际提供方数据库行为.
例如:methodA 事务方法调用 methodB 事务方法时,methodB 是继续在调 用者 methodA 的事务中运行呢,还是为自己开启一个新事务运行,这就是由 methodB 的事务传播行为决定的。
Spring 定义了七种传播行为:
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NE W 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPOR TED 以非事务方式执行操作,如果当前存在事务,就把当前事务 挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事 务,则执行与 PROPAGATION_REQUIRED 类似的操作。
-
PROPAGATION_REQUIRED 指定的方法必须在事务内执行,若当前存在事务,加入到当前事务中,若当前没 有事务,则创建一个新事务,这种传播行为是最常见的,也是 spring 默认的传播行为
-
PROPAGATION_SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
-
PROPAGATION_REQUIRES_NEW 总是新建一个事务,如果当前存在事务,把当前事务挂起,直到新建的事务结束。
声明式事务不生效的场景@Transactional 应用在非 public 修饰的方法上 @Transactional 注解属性 propagation 设置错误 同一个类中方法调用,导致@Transactional 失效 异常被 catch 捕获导致@Transactional 失效 数据库引擎不支持事务