目录
一、简介
1.1、OCP原则
1.2、AOP介绍及使用场景
二、AOP在项目中的使用
2.1、集成使用
2.2、定义全局异常通知
2.3、AOP注解的含义
2.4、多个切面的执行顺序
三、通知的执行顺序(基于spring-aop5版本)
四、常用的四种切入点表达式
4.1、bean表达式(常用)
4.2、annotation表达式(常用)
4.3、Execution表达式
4.4、within表达式
五、spring中的事务处理
5.1、概述
5.1、事务处理的位置
5.2、事务处理的两种方式
5.3、基于@Transactional 注解进行声明式事务管理的实现
5.4、代码中使用事务
5.5、@Transactional 注解解析
六、事务的传播特性
6.1、三种常用的事务传播属性
一、简介
1.1、OCP原则
OCP开闭原则,对扩展开放,对修改关闭,在不改变原有系统核心业务代码的基础上动态添加一些扩展功能并可以控制对象的执行。
1.2、AOP介绍及使用场景
AOP,面向切面编程,是一种设计思想,基于OCP(开闭原则),通过预编译方式和运行期动态代理方式,实现在不修改源码的情况下,添加额外的功能的一种技术。
AOP 通常应用于日志处理,事务处理,权限处理,缓存处理等。
二、AOP在项目中的使用
2.1、集成使用
- 添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
- 通过注解实现
@Slf4j @Component //交给spring管理 @Aspect //切面类型 public class UserAspect { //该方法不需要写任何内容,该注解一般描述方法,在该方法上定义切入点 @Pointcut("bean(userServiceImpl)") public void doPointCut(){} @Before("doPointCut()") public void doBefore(){log.info("doBefore");} @After("doPointCut()") public void doAfter(){log.info("doAfter");} @AfterReturning("doPointCut()") public void doAfterReturning(){log.info("doAfterReturning");} @AfterThrowing("doPointCut()") public void doAfterThrowing(){log.info("doAfterThrowing");} //定义环绕通知,优先级最高 @Around("doPointCut()") public Object doAround(ProceedingJoinPoint point)throws Throwable{ try { long t1=System.currentTimeMillis(); Object obj=point.proceed();//调用本切面中的其他通知或者下一个切面的通知或者目标方法 long t2=System.currentTimeMillis(); log.info("方法执行时间:"+(t2-t1)); return obj; }catch (Exception e){ throw e; } } }
执行结果:
2.2、定义全局异常通知
@Component
@Slf4j
@Aspect
public class ExceptionAspect {
@AfterThrowing(value = "bean(userServiceImpl)",throwing = "ex")
public void doError(Exception ex){
log.error(ex.getMessage());
}
}
2.3、AOP注解的含义
@Aspect | 定义一个切面 | |
@PointCut | 定义切入点 | |
通知类型 | @Around | 定义环绕通知 |
@Before | 定义前置通知 | |
@After | 定义后置通知 | |
@AfterReturning | 定义返回通知 | |
@AfterThrowing | 定义异常通知 |
2.4、多个切面的执行顺序
可以在切面类上添加@Order注解,同时可以添加具体的值,值越小,越优先,如下:BookUtil1切面先执行。
@Aspect
@Component
@Order(1)
public class BookUtil1 {
}
@Aspect
@Component
@Order(2)
public class BookUtil2 {
}
三、通知的执行顺序(基于spring-aop5版本)
- 不同的版本执行顺序不一样
针对上面代码的代码,运行结果如下:
分析图如下:
- around环绕通知是入口,它的优先级最高,然后回调用环绕通知中的Proceed方法,判断有没有前置通知;
- 如果定义了前置通知,此时进入before前置通知方法,执行完之后,此时会判断还有没有其他切面有环绕通知等,如果有继续执行,如果没有,则执行目标方法;
- 目标方法执行结束之后,判断有无异常,无异常执行return,有异常执行throw;
- 无论哪种情况,之后调用after后置通知方法;
- after后置通知方法执行完之后,调用环绕通知Proceed方法后面的代码;
四、常用的四种切入点表达式
4.1、bean表达式(常用)
- 该切入点只对userServiceImpl类中所有方法生效
@Pointcut("bean(bookServiceImpl)")
- 指定所有后缀为ServiceImpl的类中的所有方法
@Pointcut("bean(*ServiceImpl)")
4.2、annotation表达式(常用)
通过使用注解来修饰方法
1、首先定义一个注解
@Retention(RetentionPolicy.RUNTIME) //运行时有效 @Target(ElementType.METHOD) //该注解只能描述于方法 public @interface BookRequired { }
2、 定义切面
@Slf4j @Component //交给spring管理 @Aspect public class BookAspect { @Pointcut("@annotation(com.example.springboot.annotation.BookRequired)") public void doPointCut(){} @Around("doPointCut()") public Object around(ProceedingJoinPoint joinPoint)throws Throwable{ log.info("---开始Around通知---"); try { Object obj=joinPoint.proceed(); log.info("---结束Around通知---"); return obj; }catch (Throwable e){ log.error(e.getMessage()); throw e; } } }
3、使用
@BookRequired @Override public JsonResult getBookDetail(Integer bookId) { return new JsonResult().success(); }
4.3、Execution表达式
- 通用方法
解析: * 表示返回值;@Pointcut("execution(* com.example.springboot.service..*.*(..))")
** 表示service包以及它下面所有子包
* 表示所有类中的所有方法
(**)表示任意参数
该表达式意思是,指定某目录下的所有的类中的所有方法
4.4、within表达式
- 指定对bookServiceImpl类
@Pointcut("within(com.example.springboot.service.impl.BookServiceImpl)")
- 指定目录下的所有的类
@Pointcut("within(com.example.springboot.service.impl.*)")
- 指定当前目录及所有子目录下的类
@Pointcut("within(com.example.springboot.service..*)")
五、spring中的事务处理
5.1、概述
- spring框架中提供了一种声明式事务的处理方式,此方式基于AOP代理,可以将具体业务逻辑与事务处理进行解耦。也就是让我们的业务代码逻辑不受污染或少量污染,就可以实现事务控制。
- 在springBoot项目中,其内部提供了事务的自动配置,当我们在项目中添加了指定依赖spring-boot-starter-jdbc时,框架会自动为我们的项目注入事务管理器对象,最常用的为DataSourceTransactionManager对象。
5.1、事务处理的位置
事务处理放在Service层,如果在dao层,回滚的时候只能回滚到当前方法,我们的service层的某个方法可能调用了dao层的好几个方法。如果在dao层,那dao层的所有被调用的方法都要commit,就会很繁琐。
5.2、事务处理的两种方式
- Spring提供了两种事务管理方式, 编程式事务和声明式事务。编程式事务指的是通过编码方式实现事务;声明式事务基于 AOP,将具体业务逻辑与事务处理解耦。声明式事务管理使业务代码逻辑不受污染, 因此在实际使用中声明式事务用的比较多。
- Spring中声明式事务处理有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于@Transactional 注解的方式
5.3、基于@Transactional 注解进行声明式事务管理的实现
- 在配置类上添加注解@EnableTransactionManagement(有些spring版本不用加)
- 添加@Transactional注解到相关的类或方法上
5.4、代码中使用事务
- 在类上添加该注解,表示类中所有的方法都使用事务,成功则提交,失败则回滚;
- 在方法上添加该注解,表示该方法使用事务,成功则提交,失败则回滚
@Service
@Transactional(timeout = 30,readOnly = true,isolation = Isolation.READ_COMMITTED,rollbackFor = Throwable.class,propagation = Propagation.REQUIRED)
public class BookServiceImpl extends ServiceImpl<BookListMapper, BookList> implements BookService {
}
5.5、@Transactional 注解解析
timeout | 设置超时时间,超时之后回滚,如果不设置,底层会一直尝试提交,浪费资源 |
readOnly | 不写默认为false,表示该事务不是一个只读事务,类似于更新,修改的操作,我在修改更新的时候别人只能阻塞等待,设置为true,表示该事务是一个只读事务,查询的时候,一般设置为true,大家可以并发去读,锁的力度会比较小, |
isolation | 事务隔离级别,多个事务可以并发执行,可能存在脏读,幻读等现象,一般设置为Isolation.READ_COMMITTED,表示一个事务读取的都是别人已提交过的数据,未提交的数据是看不到的 |
rollbackFor | 异常类型,表示遇到什么样的异常就会回滚 |
propagation | 事务的传播类型,业务是分开独立运行,还是可以一起混合执行,由他决定, |
六、事务的传播特性
是指不同业务对象(不同service之间)中的事务方法之间的相互调用
6.1、三种常用的事务传播属性
- PROPAGATION_REQUIRED:(Spring默认)支持当前事务,如果当前没有事务,就新建一个事务;如果当前有事务,就参与当前事务。
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。新建事务与被挂起事务相互独立,互不影响。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起。