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标签配置

    <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

相关推荐
Rover.x2 小时前
head table is mandatory
java·apache
yanghuashuiyue2 小时前
Java过滤器-拦截器-AOP-Controller
java·开发语言
shoubepatien2 小时前
JAVA —— 03
java·jvm
a努力。2 小时前
【基础数据篇】数据等价裁判:Comparer模式
java·后端
小冷coding2 小时前
【Java】高并发架构设计:1000 QPS服务器配置与压测实战
java·服务器·开发语言
哈哈哈笑什么2 小时前
SpringBoot 企业级接口加密【通用、可配置、解耦的组件】「开闭原则+模板方法+拦截器/中间件模式」
java·后端·安全
期待のcode2 小时前
springboot依赖管理机制
java·spring boot·后端
WX-bisheyuange2 小时前
基于Spring Boot的智慧校园管理系统设计与实现
java·大数据·数据库·毕业设计
深紫色的三北六号3 小时前
大疆不同任务类型执行逻辑,上云API源码分析
java·无人机·springboot·大疆·上云api