Spring-Aop

作用

  • 作用:在不惊动原始设计的基础上为其进行功能增强,前面咱们有技术就可以实现这样的功能即代理模式。
    在这里插入图片描述

1.环境准备

添加依赖

<dependency>
       <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>5.3.18</version>
 </dependency>

--------------------------------------------------------
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
  • 因为spring-context中已经导入了spring-aop,所以不需要再单独导入spring-aop
  • 导入AspectJ的jar包,AspectJ是AOP思想的一个具体实现,Spring有自己的AOP实现,但是相比于AspectJ来说比较麻烦,所以我们直接采用Spring整合ApsectJ的方式进行AOP开发。

2.快速实现

  • 1.添加依赖
  • 2.定义接口与实现类
  • 3.定义通知类和通知
public class MyAdvice {
    public void method(){
        System.out.println(System.currentTimeMillis());
    }
}
  • 4.:定义切入点
    在这里插入图片描述
  • 5.制作切面在这里插入图片描述
    6.将通知类配给容器并标识其为切面类
    在这里插入图片描述
  • 7.开启注解格式AOP功能
    在这里插入图片描述

知识点1:@EnableAspectJAutoProxy
在这里插入图片描述
知识点2:@Aspect
在这里插入图片描述
知识点3:@Pointcut
在这里插入图片描述

3.AOP工作流程

空白 需要重新学习 ---------------黑马视频 P31

4.AOP配置管理

在这里插入图片描述

   1.AOP切入点表达式

      1.语法格式

execution(public User com.itheima.service.UserService.findById(int))

  • execution:动作关键字,描述切入点的行为动作,例如execution表示执行到指定切入点
  • public:访问修饰符,还可以是public,private等,可以省略
  • User:返回值,写返回值类型
  • com.itheima.service:包名,多级包使用点连接
  • UserService:类/接口名称
  • findById:方法名
  • int:参数,直接写参数的类型,多个类型用逗号隔开
  • 异常名:方法定义中抛出指定异常,可以省略

      2.通配符

  • 1.*:单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现

execution(public * com.itheima..UserService.find(*))

匹配com.itheima包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法


  • 2...:多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写
    表示任意

execution(public User com…UserService.findById(…))

匹配com包下的任意包中的UserService类或接口中所有名称为findById的方法


  • 3.+:专用于匹配子类类型
    execution(* *…Service+.(…))

execution(void com.itheima.dao.BookDao.update())
匹配接口,能匹配到
execution(void com.itheima.dao.impl.BookDaoImpl.update())
匹配实现类,能匹配到
execution(* com.itheima.dao.impl.BookDaoImpl.update())
返回值任意,能匹配到
execution(* com.itheima.dao.impl.BookDaoImpl.update(*))
返回值任意,但是update方法必须要有一个参数,无法匹配,要想匹配需要在update接口和实现类添加参数
execution(void com.*.*.*.*.update())
返回值为void,com包下的任意包三层包下的任意类的update方法,匹配到的是实现类,能匹配
execution(void com.*.*.*.update())
返回值为void,com包下的任意两层包下的任意类的update方法,匹配到的是接口,能匹配
execution(void *..update())
返回值为void,方法名是update的任意包下的任意类,能匹配
execution(* *..*(..))
匹配项目中任意类的任意方法,能匹配,但是不建议使用这种方式,影响范围广
execution(* *..u*(..))
匹配项目中任意包任意类下只要以u开头的方法,update方法能满足,能匹配
execution(* *..*e(..))
匹配项目中任意包任意类下只要以e结尾的方法,update和save方法能满足,能匹配
execution(void com..*())
返回值为void,com包下的任意包任意类任意方法,能匹配,*代表的是方法
execution(* com.itheima.*.*Service.find*(..))
将项目中所有业务层方法的以find开头的方法匹配
execution(* com.itheima.*.*Service.save*(..))
将项目中所有业务层方法的以save开头的方法匹配

      3.书写技巧

对于切入点表达式的编写其实是很灵活的,那么在编写的时候,有没有什么好的技巧让我们用用:

  • 所有代码按照标准规范开发,否则以下技巧全部失效
  • 描述切入点通**常描述接口**,而不描述实现类,如果描述到实现类,就出现紧耦合了
  • 访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述
  • 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述
  • 包名书写尽量不使用…匹配,效率过低,常用*做单个包描述匹配,或精准匹配
  • 接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名
  • 方法名书写以动词进行精准匹配,名词采用匹配,例如getById书写成getBy,selectAll书写成selectAll
  • 参数规则较为复杂,根据业务方法灵活调整
  • 通常**不使用异常作为匹配**规则

   2.AOP通知类型

在这里插入图片描述

      1.类型介绍

  • 前置通知
  • 后置通知
  • 环绕通知(重点)
  • 返回后通知(了解)
  • 抛出异常后通知(了解)

      2.通知类型的使用

  • 1.前置通知
    在这里插入图片描述

  • 2.后置通知
    在这里插入图片描述

  • 3.环绕通知------重点
    基本使用
    在这里插入图片描述
    注意事项
    在这里插入图片描述

  • 4.返回后通知
    在这里插入图片描述
    注意:返回后通知是需要在原始方法select正常执行后才会被执行,如果select()方法执行的过程中出现了异常,那么返回后通知是不会被执行。后置通知是不管原始方法有没有抛出异常都会被执行。这个案例大家下去可以自己练习验证下。

  • 5.异常后通知
    在这里插入图片描述
    注意:异常后通知是需要原始方法抛出异常,可以在select()方法中添加一行代码int i = 1/0即可。如果没有抛异常,异常后通知将不会被执行。


在这里插入图片描述
环绕通知注意事项

  1. 环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知
  2. 通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行
  3. 对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,最好设定为Object类型
  4. 原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object
  5. 由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须要处理Throwable异常

   3.AOP通知获取数据

      1. 获取参数

获取所要执行函数所传入的参数

  • 1.在方法上添加JoinPoint,通过JoinPoint来获取参数--------非环绕
    在这里插入图片描述
  • 2.在方法上添加ProceedingJoinPoint,通过JoinPoint来获取参数-----环绕
    在这里插入图片描述

      2. 获取返回值

对于返回值,只有返回后AfterReturing和环绕Around这两个通知类型可以获取

  • 1.环绕通知获取返回值
    在这里插入图片描述我们是可以直接获取,不但可以获取,如果需要还可以进行修改。

  • 2.返回后通知获取返回值
    在这里插入图片描述

注意:
(1)参数名的问题
在这里插入图片描述
(2)afterReturning方法参数类型的问题
参数类型可以写成String,但是为了能匹配更多的参数类型,建议写成Object类型
(3)afterReturning方法参数的顺序问题
在这里插入图片描述


      3. 获取异常

对于获取抛出的异常,只有抛出异常后AfterThrowing和环绕Around这两个通知类型可以获取

  • 1.环绕通知获取异常
    在这里插入图片描述
  • 2.抛出异常后通知获取异常
    在这里插入图片描述
    注意:
    在这里插入图片描述

5.AOP事务管理

  • 事务作用:在数据层保障一系列的数据库操作同成功同失败
  • Spring事务作用:在数据层或**业务层**保障一系列的数据库操作同成功同失败

   1.快速使用

Spring为了管理事务,提供了一个平台事务管理器`PlatformTransactionManager

  • 1.添加配置文件
    在这里插入图片描述
  • 2.开启事务在这里插入图片描述
  • 3.使用事务
    在这里插入图片描述

   2.事务管理

知识点1:@EnableTransactionManagement
在这里插入图片描述
知识点2:@Transactional
在这里插入图片描述

   3.Spring事务角色

事务管理员事务协调员

  • 1.未开启Spring事务之前:
    在这里插入图片描述
  • AccountDao的outMoney因为是修改操作,会开启一个事务T1
  • AccountDao的inMoney因为是修改操作,会开启一个事务T2
  • AccountService的transfer没有事务,
    • 运行过程中如果没有抛出异常,则T1和T2都正常提交,数据正确
    • 如果在两个方法中间抛出异常,T1因为执行成功提交事务,T2因为抛异常不会被执行
    • 就会导致数据出现错误

  • 2.开启Spring的事务管理后
    在这里插入图片描述
  • transfer上添加了@Transactional注解,在该方法上就会有一个事务T
  • AccountDao的outMoney方法的事务T1加入到transfer的事务T中
  • AccountDao的inMoney方法的事务T2加入到transfer的事务T中
  • 这样就保证他们在同一个事务中,当业务层中出现异常,整个事务就会回滚,保证数据的准确性。

注意:

目前的事务管理是基于DataSourceTransactionManagerSqlSessionFactoryBean使用的是同一个数据源。
(不太懂)
在这里插入图片描述

   4.Spring事务属性

      1. 事务配置

在这里插入图片描述
上面这些属性都可以在@Transactional注解的参数上进行设置。

  • readOnly:true只读事务,false读写事务,增删改要设为false,查询设为true。
  • timeout:设置超时时间单位秒,在多长时间之内事务没有提交成功就自动回滚,-1表示不设置超时时间。
  • rollbackFor:当出现指定异常进行事务回滚
  • noRollbackFor:当出现指定异常不进行事务回滚

并不是所有的异常都会回滚事务,比如下面的代码就不会回滚

public interface AccountService {
    /**
     * 转账操作
     * @param out 传出方
     * @param in 转入方
     * @param money 金额
     */
    //配置当前接口方法具有事务
    public void transfer(String out,String in ,Double money) throws IOException;
}

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;
	@Transactional
    public void transfer(String out,String in ,Double money) throws IOException{
        accountDao.outMoney(out,money);
        //int i = 1/0; //这个异常事务会回滚
        if(true){
            throw new IOException(); //这个异常事务就不会回滚
        }
        accountDao.inMoney(in,money);
    }

}

出现这个问题的原因是,Spring的事务只会对Error异常RuntimeException异常及其子类进行事务回顾,其他的异常类型是不会回滚的,对应IOException不符合上述条件所以不回滚

此时就可以使用rollbackFor属性来设置出现IOException异常不回滚

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;
	 @Transactional(rollbackFor = {IOException.class})              ========》核心
    public void transfer(String out,String in ,Double money) throws IOException{
        accountDao.outMoney(out,money);
        //int i = 1/0; //这个异常事务会回滚
        if(true){
            throw new IOException(); //这个异常事务就不会回滚
        }
        accountDao.inMoney(in,money);
    }

}
  • rollbackForClassName等同于rollbackFor,只不过属性为异常的类全名字符串
  • noRollbackForClassName等同于noRollbackFor,只不过属性为异常的类全名字符串
  • isolation设置事务的隔离级别
    • DEFAULT :默认隔离级别, 会采用数据库的隔离级别
    • READ_UNCOMMITTED : 读未提交
    • READ_COMMITTED : 读已提交
    • REPEATABLE_READ : 重复读取
    • SERIALIZABLE: 串行化

      2. 事务传播行为

在这里插入图片描述
propagation设置事务属性:传播行为设置为当前操作需要新事务

@Service
public class LogServiceImpl implements LogService {

    @Autowired
    private LogDao logDao;
	//propagation设置事务属性:传播行为设置为当前操作需要新事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void log(String out,String in,Double money ) {
        logDao.log("转账操作由"+out+"到"+in+",金额:"+money);
    }
}

事务传播行为的可选值
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值