Spring AOP高级应用与源码剖析

Spring AOP 高级应用

AOP的本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码,日志代码,事务控制代码,性能监测代码。

AOP相关术语

名词

解释

JoinPoint(连接点)

可以用于把增强代码加入到业务主线中的点

Pointcut(切入点)

已经把增强代码加入到业务主线进来之后的连接点

Adice(通知/增强)

切面类中用于提供增强功能的方法

Target(目标对象)

代理的目标对象。即被代理对象

Proxy(代理)

一个类被AOP植入增强后,产生的代理类

Weaving(织入)

把增强应用到目标对象来创建新的代理对象的过程

Aspect(切面)

指定增强的代码所关注的方面,把这些相关的增强代码定义到一个类中,这个类就是切面类。

Spring中AOP的代理选择

AOP思想使用的是动态代理。默认情况下Spring会根据代理对象是否实现接口来选择使用JDK还是CGLIB。当代理对象没有实现任何接口时,Spring会选择CGLIB。

Spring中AOP实现XML形式

maven坐标

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.1.12.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

Aop核心配置

<!--
 Spring基于XML的AOP配置前期准备:
 在spring的配置⽂件中加⼊aop的约束
 xmlns:aop="http://www.springframework.org/schema/aop"
 http://www.springframework.org/schema/aop 
https://www.springframework.org/schema/aop/spring-aop.xsd 
 
 Spring基于XML的AOP配置步骤、
 第⼀步:把通知Bean交给Spring管理
 第⼆步:使⽤aop:config开始aop的配置
 第三步:使⽤aop:aspect配置切⾯
 第四步:使⽤对应的标签配置通知的类型
 ⼊⻔案例采⽤前置通知,标签为aop:before
-->
<!--把通知bean交给spring来管理-->
<bean id="logUtil" class="com.lagou.utils.LogUtil"></bean>
<!--开始aop的配置-->
<aop:config>
<!--配置切⾯-->
 <aop:aspect id="logAdvice" ref="logUtil">
 <!--配置前置通知-->
 <aop:before method="printLog" pointcut="execution(public *
com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou
.pojo.Account))"></aop:before>
 </aop:aspect>
</aop:config>

切入表达式,也称之为Aspectj切入点表达式,指的是遵循特定语法结构的字符串,其作用是用于对符合语法格式的连接点进行增强。

改变代理方式的配置:

我们知道只要不是final修饰的类都可以是cglib提供的方式来创建代理对象,spring提供了两种配置方式:

  • 使用aop:config标签配置
<aop:config proxy-target-class="true">
  • 使用aop:aspectj-autoproxy标签配置
<!--此标签是基于XML和注解组合配置AOP时的必备标签,表示Spring开启注解配置AOP的⽀持-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectjautoproxy>

5中AOP通知类型:

1. 前置通知

  • 配置方式:<aop:before method="方法名" pointcut-ref="切入点引用"/>
  • 出现位置:仅在<aop:aspect>标签内
  • 执行时机:切入方法(业务核心方法)执行之前(必定执行)
  • 细节:可获取切入方法的参数并增强

2. 正常执行时通知(后置返回通知)

  • 配置方式:<aop:after-returning method="方法名" pointcut-ref="切入点引用"/>
  • 出现位置:仅在<aop:aspect>标签内
  • 执行时机:切入方法正常执行完成后(若方法抛异常则不执行)

3. 异常通知

  • 配置方式:<aop:after-throwing method="方法名" pointcut-ref="切入点引用"/>
  • 出现位置:仅在<aop:aspect>标签内
  • 执行时机:切入方法产生异常之后(无异常则不执行)
  • 细节:可获取切入方法的参数 + 异常信息

4. 最终通知

  • 配置方式:<aop:after method="方法名" pointcut-ref="切入点引用"/>
  • 出现位置:仅在<aop:aspect>标签内
  • 执行时机:切入方法执行完成后、返回之前(无论是否抛异常,必定执行)
  • 细节:可获取参数,常用于清理操作

5. 环绕通知

  • 配置方式:<aop:around method="方法名" pointcut-ref="切入点引用"/>
  • 出现位置:仅在<aop:aspect>标签内
  • 特殊说明:是 Spring 的 “特殊通知”,可通过编程控制通知执行时机(区别于前四种 “指定时机” 的通知);依赖ProceedingJoinPoint接口,实现手动触发切入方法的调用

Spring中AOP实现XML+注解形式

XML配置

注解配置

<aop:before method="方法名" pointcut-ref="切入点引用"/>

@Before

<aop:after-returning method="方法名" pointcut-ref="切入点引用"/>

@AfterReturning

<aop:after-throwing method="方法名" pointcut-ref="切入点引用"/>

@AfterThrowing

<aop:after method="方法名" pointcut-ref="切入点引用"/>

@After

<aop:around method="方法名" pointcut-ref="切入点引用"/>

@Around

Spring中AOP实现注解形式

在我们使用注解开发时候要用注解替换掉配置文件

<!--开启spring对注解aop的⽀持-->
<aop:aspectj-autoproxy/>

需要在启动类上面添加

@EnableAspectJAutoProxy //开启spring对注解AOP的⽀持

Spring声明式事务的支持

编程式事务:在业务代码中添加事务控制代码,这样的事务控制机制叫做编程式事务。

声明式事务:通过xml或者注解配置的方式达到事务控制的目的,叫做声明式事务。

事务的四大特性:

  1. 原子性:原子性式指事务是一个不可分割的工作单位,事务中的操作要么全部发生,要么全部不发生。
  2. 一致性:事务必须使数据库从一个一致性状态变换成另一个一致性状态。
  3. 隔离性:事务的隔离性式多个用户并发访问数据库时,数据库未每一个用户开启的事务,每个事务不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
  4. 持久性:持久性时指一个事务一旦被提交,它对数据中数据的改变就是永久性的,接下来即使数据库发送故障也不应该对其有任何影响。

不考虑事务隔离级别会出现的问题:

  1. 脏读:一个线程中的事务读到另一个线程中未提交的数据。
  2. 不可重复读:一个线程的事务读到另一个线程中已经提交的update的数据(前后内容不一样)。
  3. 幻读:一个线程中的事务读取到了另一个线程中已经提交的insert或者delete的数据(前后条数不一样)。

数据库中定义的四种隔离级别(从高到低):

  1. Serializable(串行化):避免脏读,不可重复读,虚读情况的发生。
  2. Repeatable read(可重复读):可避免脏读,不可重复读情况发生。
  3. Read committed(读已提交):可避免脏读情况发生。
  4. Read uncommitted(读未提交):以上情况都没有保证。

Mysql默认的事务隔离级别是:读已提交。
查询当前使用的隔离级别:SELECT @@session.transaction_isolation;

设置Mysql事务的隔离级别:(仅仅是当前会话的隔离级别)SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

传播行为常量

中文名称

核心规则

适用场景

REQUIRED

(默认)

必需的

如果当前有事务,加入该事务;如果无事务,新建事务。

绝大多数业务(如订单 + 库存)

SUPPORTS

支持的

如果当前有事务,加入;无事务,则以非事务方式执行。

查询类方法(可选事务)

MANDATORY

强制的

必须在已有事务中执行;无事务则抛异常(IllegalTransactionStateException)。

核心子操作(必须依赖外层事务)

REQUIRES_NEW

新建的

无论当前是否有事务,都新建独立事务;原有事务暂停,新事务执行完后恢复。

日志记录、消息发送(独立事务)

NOT_SUPPORTED

不支持的

以非事务方式执行;若当前有事务,暂停原有事务。

纯查询(无需事务,提升性能)

NEVER

从不

必须以非事务方式执行;若当前有事务,抛异常。

绝对禁止事务的操作

NESTED

嵌套的

基于当前事务创建嵌套子事务(Savepoint 实现);子事务回滚不影响外层,外层回滚则全滚。

批量操作(部分失败可回滚)

纯xml方式

<!-- 配置事务管理器(基于JDBC数据源) -->
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/> <!-- 引用已配置的数据源 -->
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- 全局默认配置:所有方法默认非只读,传播行为REQUIRED -->
        <tx:method name="*" read-only="false" 
                   propagation="REQUIRED" isolation="DEFAULT" timeout="-1"/>
        
        <!-- 针对查询方法的单独配置:只读,传播行为SUPPORTS -->
        <tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <!-- 切入点:匹配TransferServiceImpl类的所有方法 -->
    <aop:advisor advice-ref="txAdvice" 
                 pointcut="execution(* com.lagou.edu.service.impl.TransferServiceImpl.*(..))"/>
</aop:config>

xml+注解方式

<!-- 开启Spring注解事务的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)

纯注解方式

需要在启动类上加入@EnableTransactionManagement

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值