吃透 Spring 系列—AOP部分

目录

◆   AOP 简介

- AOP的概念

- AOP思想的实现方案

- 模拟AOP的基础代码

- AOP相关概念

◆   基于xml配置的AOP

- xml方式AOP快速入门

- xml方式AOP配置详解

- xml方式AOP原理剖析

◆   基于注解配置的AOP

- 注解方式AOP基本使用

- 注解方式AOP配置详解

- 注解方式AOP原理剖析

◆   基于AOP的声明式事务控制

- Spring事务编程概述

- 搭建测试环境

- 基于xml声明式事务控制

- 基于注解声明式事务控制


◆   AOP 简介

- AOP的概念

AOP,Aspect Oriented Programming,面向切面编程,是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象,一个对象包括静态的属性信息,包括动态的方法信息等。而AOP是横向的对不同事物的抽象,属性与属性、方法与方法、对象与对象都可以组成一个切面,而用这种思维去设计编程的方式叫做面向切面编程

- AOP思想的实现方案

动态代理技术,在运行期间,对目标对象的方法进行增强,代理对象同名方法内可以执行原有逻辑的同时嵌入执行其他增强逻辑或其他对象的方法

- 模拟AOP的基础代码

其实在之前学习BeanPostProcessor时,在BeanPostProcessor的after方法中使用动态代理对Bean进行了增强,实际存储到单例池singleObjects中的不是当前目标对象本身,而是当前目标对象的代理对象Proxy,这样在调用目标对象方法时,实际调用的是代理对象Proxy的同名方法,起到了目标方法前后都进行增强的功能,对该方式进行一下优化,将增强的方法提取出去到一个增强类中,且只对com.itheima.service.impl包下的任何类的任何方法进行增强

- AOP相关概念

◆   基于xml配置的AOP

- xml方式AOP快速入门

前面自己编写的AOP基础代码还是存在一些问题的,主要如下:

● 被增强的包名在代码写死了

● 通知对象的方法在代码中写死了

 通过配置文件的方式去解决上述问题

● 配置哪些包、哪些类、哪些方法需要被增强

● 配置目标方法要被哪些通知方法所增强,在目标方法执行之前还是之后执行增强

配置方式的设计、配置文件(注解)的解析工作,Spring已经帮封装好了 

xml方式配置AOP步骤

1、导入AOP相关坐标;

Spring-context坐标下已经包含spring-aop的包了,所以就不用额外导入了

2、准备目标类、准备增强类,并配置给Spring管理;

3、配置切点表达式(哪些方法被增强);

4、配置织入(切点被哪些通知方法增强,是前置增强还是后置增强)

- xml方式AOP配置详解

xml配置AOP的方式还是比较简单的,下面看一下AOP详细配置的细节:

● 切点表达式的配置方式

● 切点表达式的配置语法

● 通知的类型

● AOP的配置的两种方式

切点表达式的配置方式有两种,直接将切点表达式配置在通知上,也可以将切点表达式抽取到外面,在通知上进行引用

切点表达式是配置要对哪些连接点(哪些类的哪些方法)进行通知的增强,语法如下:

其中, 

● 访问修饰符可以省略不写;

● 返回值类型、某一级包名、类名、方法名 可以使用 * 表示任意;

● 包名与类名之间使用单点 . 表示该包下的类,使用双点 .. 表示该包及其子包下的类;

● 参数列表可以使用两个点 .. 表示任意参数。

切点表达式举几个例子方便理解

AspectJ的通知由以下五种类型 

通知名称

配置方式

执行时机

前置通知

< aop:before >

目标方法执行之前执行

后置通知

< aop:after-returning >

目标方法执行之后执行,目标方法异常时,不在执行

环绕通知

< aop:around >

目标方法执行前后执行,目标方法异常时,环绕后方法不在执行

异常通知

< aop:after-throwing >

目标方法抛出异常时执行

最终通知

< aop:after >

不管目标方法是否有异常,最终都会执行

环绕通知

异常通知,当目标方法抛出异常时,异常通知方法执行,且后置通知和环绕后通知不在执行


最终通知,类似异常捕获中的finally,不管目标方法有没有异常,最终都会执行的通知

通知方法在被调用时,Spring可以为其传递一些必要的参数 

参数类型

作用

JoinPoint

连接点对象,任何通知都可使用,可以获得当前目标对象、目标方法参数等信息

ProceedingJoinPoint

JoinPoint子类对象,主要是在环绕通知中执行proceed(),进而执行目标方法

Throwable

异常对象,使用在异常通知中,需要在配置文件中指出异常对象名称

JoinPoint 对象 

ProceedingJoinPoint对象

Throwable对象

AOP的另一种配置方式,该方式需要通知类实现Advice的子功能接口

Advice的子功能接口

例如:通知类实现了前置通知和后置通知接口

切面使用advisor标签配置

又例如:通知类实现了方法拦截器接口

 切面使用advisor标签配置

使用aspect和advisor配置区别如下:

1)配置语法不同:

2)通知类的定义要求不同,advisor 需要的通知类需要实现Advice的子功能接口:

aspect 不需要通知类实现任何接口,在配置的时候指定哪些方法属于哪种通知类型即可,更加灵活方便:

3)可配置的切面数量不同:

● 一个advisor只能配置一个固定通知和一个切点表达式;

● 一个aspect可以配置多个通知和多个切点表达式任意组合,粒度更细。 

4)使用场景不同:

● 如果通知类型多、允许随意搭配情况下可以使用aspect进行配置;

● 如果通知类型单一、且通知类中通知方法一次性都会使用到的情况下可以使用advisor进行配置;

● 在通知类型已经固定,不用人为指定通知类型时,可以使用advisor进行配置,例如后面要学习的Spring事务控制的配置;

由于实际开发中,自定义aop功能的配置大多使用aspect的配置方式,所以主要讲解aspect的配置,advisor是为了后面Spring声明式事务控制做铺垫,此处了解即可。

- xml方式AOP原理剖析

通过xml方式配置AOP时,引入了AOP的命名空间,根据讲解的,要去找spring-aop包下的META-INF,在去找spring.handlers文件

最终加载的是 AopNamespaceHandler,该Handler的init方法中注册了config标签对应的解析器

ConfigBeanDefinitionParser作为入口进行源码剖析,最终会注册一个AspectJAwareAdvisorAutoProxyCreator进入到Spring容器中,那该类作用是什么呢?看一下集成体系图

AspectJAwareAdvisorAutoProxyCreator 的上上级父类AbstractAutoProxyCreator中的postProcessAfterInitialization方法

通过断点方式观察,当bean是匹配切点表达式时,this.wrapIfNecessary(bean, beanName, cacheKey)返回的是一个JDKDynamicAopProxy

可以在深入一点,对wrapIfNecessary在剖析一下,看看是不是熟知的通过JDK的
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 的方式创建的代理对象呢?经过如下一系列源码跟踪

动态代理的实现的选择,在调用getProxy() 方法时,我们可选用的 AopProxy接口有两个实现类,如上图,这两种都是动态生成代理对象的方式,一种就是基于JDK的,一种是基于Cglib

代理技术

使用条件

配置方式

JDK 动态代理技术

目标类有接口,是基于接口动态生成实现类的代理对象

标类有接口的情况下,默认方式

Cglib 动态代理技术

目标类无接口且不能使用final修饰,是基于被代理对象动态生成子对象为代理对象

目标类无接口时,默认使用该方式;目标
类有接口时,手动配置<aop:config proxy-target-class=“true”>强制使用Cglib方式

下面看一下Cglib基于超类的动态代理

◆   基于注解配置的AOP

- 注解方式AOP基本使用

Spring的AOP也提供了注解方式配置,使用相应的注解替代之前的xml配置,xml配置AOP时,主要配置了三部分:目标类被Spring容器管理、通知类被Spring管理、通知与切点的织入(切面),如下:

目标类被Spring容器管理、通知类被Spring管理

配置aop,其实配置aop主要就是配置通知类中的哪个方法(通知类型)对应的切点表达式是什么

注解@Aspect、@Around需要被Spring解析,所以在Spring核心配置文件中需要配置aspectj的自动代理

如果核心配置使用的是配置类的话,需要配置注解方式的aop自动代理

- 注解方式AOP配置详解

各种注解方式通知类型

切点表达式的抽取,使用一个空方法,将切点表达式标注在空方法上,其他通知方法引用即可

- 注解方式AOP原理剖析

之前在使用xml配置AOP时,是借助的Spring的外部命名空间的加载方式完成的,使用注解配置后,就抛弃了<aop:config>标签,而该标签最终加载了名AspectJAwareAdvisorAutoProxyCreator的BeanPostProcessor ,最终,在该BeanPostProcessor中完成了代理对象的生成。

同样,从aspectj-autoproxy标签的解析器入手 

 

而AspectJAutoProxyBeanDefinitionParser代码内部,最终也是执行了和xml方式AOP一样的代码

如果使用的是核心配置类的话

查看@EnableAspectJAutoProxy源码,使用的也是@Import导入相关解析类

使用@Import导入的AspectJAutoProxyRegistrar源码,一路追踪下去,最终还是注册了AnnotationAwareAspectJAutoProxyCreator 这个类

◆   基于AOP的声明式事务控制

- Spring事务编程概述

事务是开发中必不可少的东西,使用JDBC开发时,我们使用connnection对事务进行控制,使用MyBatis时,我们使用SqlSession对事务进行控制,缺点显而易见,当我们切换数据库访问技术时,事务控制的方式总会变化,Spring 就将这些技术基础上,提供了统一的控制事务的接口。Spring的事务分为:编程式事务控制声明式事务控制

事务控制方式

解释

编程式事务控制

Spring提供了事务控制的类和方法,使用编码的方式对业务代码进行事务控制,事务控制代码和业务操作代码耦合到了一起,开发中不使用

声明式事务控制

Spring将事务控制的代码封装,对外提供了Xml和注解配置方式,通过配置的方式完成事务的控制,可以达到事务控制与业务操作代码解耦合,开发中推荐使用

Spring事务编程相关的类主要有如下三个

务控制相关类

解释

平台事务管理器 PlatformTransactionManager

是一个接口标准,实现类都具备事务提交、回滚和获得事务对象的功能,不同持久层框架可能会有不同实现方案

事务定义 TransactionDefinition

封装事务的隔离级别、传播行为、过期时间等属性信息

事务状态 TransactionStatus

存储当前事务的状态信息,如果事务是否提交、是否回滚、是否有回滚点等

- 搭建测试环境

搭建一个转账的环境,dao层一个转出钱的方法,一个转入钱的方法,service层一个转账业务方法,内部分别调用dao层转出钱和转入钱的方法,准备工作如下:

● 数据库准备一个账户表tb_account;

● dao层准备一个AccountMapper,包括incrMoney和decrMoney两个方法;

● service层准备一个transferMoney方法,分别调用incrMoney和decrMoney方法;

● 在applicationContext文件中进行Bean的管理配置;

● 测试正常转账与异常转账。

- 基于xml声明式事务控制

结合上面学习的AOP的技术,就可以想到,可以使用AOP对Service的方法进行事务的增强。

● 目标类:自定义的AccountServiceImpl,内部的方法是切点

● 切点:service业务类中的所有业务方法

● 通知类:Spring提供的,通知方法已经定义好,只需要配置即可

分析:

● 通知类是Spring提供的,需要导入Spring事务的相关的坐标;

● 配置目标类AccountServiceImpl;

● 使用advisor标签配置切面。

配置目标类AccountServiceImpl

使用advisor标签配置切面

疑问:Spring提供的通知类是谁?是谁?是spring-tx包下的advice标签配置提供的

对上述配置进行详解一下

首先,平台事务管理器PlatformTransactionManager是Spring提供的封装事务具体操作的规范接口,封装了事务的提交和回滚方法

不同的持久层框架事务操作的方式有可能不同,所以不同的持久层框架有可能会有不同的平台事务管理器实现,例如,MyBatis作为持久层框架时,使用的平台事务管理器实现是DataSourceTransactionManager。Hibernate作为持久层框架时,使用的平台事务管理器是HibernateTransactionManager。

其次,事务定义信息配置,每个事务有很多特性,例如:隔离级别、只读状态、超时时间等,这些信息在开发时可以通过connection进行指定,而此处要通过配置文件进行配置

其中,name属性名称指定哪个方法要进行哪些事务的属性配置,此处需要区分的是切点表达式指定的方法与此处指定的方法的区别?切点表达式,是过滤哪些方法可以进行事务增强;事务属性信息的name,是指定哪个方法要进行哪些事务属性的配置

方法名在配置时,也可以使用 * 进行模糊匹配,例如:

isolation属性:指定事务的隔离级别,事务并发存在三大问题:脏读、不可重复读、幻读/虚读。可以通过设置事务的隔离级别来保证并发问题的出现,常用的是READ_COMMITTED 和 REPEATABLE_READ 

isolation属性

解释

DEFAULT

默认隔离级别,取决于当前数据库隔离级别,例如MySQL默认隔离级别是REPEATABLE_READ

READ_UNCOMMITTED

A事务可以读取到B事务尚未提交的事务记录,不能解决任何并发问题,安全性最低,性能最高

READ_COMMITTED

A事务只能读取到其他事务已经提交的记录,不能读取到未提交的记录。可以解决脏读问题,但是不
能解决不可重复读和幻读

REPEATABLE_READ

A事务多次从数据库读取某条记录结果一致,可以解决不可重复读,不可以解决幻读

SERIALIZABLE

串行化,可以解决任何并发问题,安全性最高,但是性能最低

read-only属性:设置当前的只读状态,如果是查询则设置为true,可以提高查询性能,如果是更新(增删改)操作则设置为false

timeout属性:设置事务执行的超时时间,单位是秒,如果超过该时间限制但事务还没有完成,则自动回滚事务,不在继续执行。默认值是-1,即没有超时时间限制

 

propagation属性:设置事务的传播行为,主要解决是A方法调用B方法时,事务的传播方式问题的,例如:使用单方的事务,还是A和B都使用自己的事务等。事务的传播行为有如下七种属性值可配置 

事务传播行为

解释

REQUIRED默认值)

A调用BB需要事务,如果A有事务B就加入A的事务中,如果A没有事务,B就自己创建一个事务

REQUIRED_NEW

A调用BB需要新事务,如果A有事务就挂起,B自己创建一个新的事务

SUPPORTS

A调用BB有无事务无所谓,A有事务就加入到A事务中,A无事务B就以非事务方式执行

NOT_SUPPORTS

A调用BB以无事务方式执行,A如有事务则挂起

NEVER

A调用BB以无事务方式执行,A如有事务则抛出异常

MANDATORY

A调用BB要加入A的事务中,如果A无事务就抛出异常

NESTED

A调用BB创建一个新事务,A有事务就作为嵌套事务存在,A没事务就以创建的新事务执行

xml方式声明式事务控制的原理浅析一下

<tx:advice>标签使用的命名空间处理器是TxNamespaceHandler,内部注册的是解析器是TxAdviceBeanDefinitionParser

TxAdviceBeanDefinitionParser中指定了要注册的BeanDefinition

TxAdviceBeanDefinitionParser二级父类AbstractBeanDefinitionParser的parse方法将TransactionInterceptor以配置的名称注册到了Spring容器中

 

TransactionInterceptor中的invoke方法会被执行,跟踪invoke方法,最终会看到事务的开启和提交 

● 在AbstractPlatformTransactionManager的132行中开启的事务;

● 在TransactionAspectSupport的242行提交了事务。 

- 基于注解声明式事务控制

注解就是对xml的替代

同样,使用的事务的注解,平台事务管理器仍然需要配置,还需要进行事务注解开关的开启 

如果使用全注解的话,使用如下配置类的形式代替配置文件 

来源:黑马程序员新版Spring零基础入门到精通,一套搞定spring全套视频教程(含实战源码) 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/136289.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法练习Day46】判断子序列不同的子序列

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 判断子序列不同的子序列总结…

人机交互复习专题

第一章概述 1.1人机交互的概念与理解 人机交互的概念与理解 人机交互是人与机器进行交互的操作方式&#xff0c;即用户与机器互相传递信息的媒介。好的人机交互界面美观且通俗易懂、操作简单有引导功能&#xff0c;使用户感受到愉快、有兴趣&#xff0c;从而提升使用效率。 美…

mysql8安装和驱动jar包下载

方式一&#xff1a;基于docker安装 下拉镜像 docker pull mysql:8.0.21 启动镜像 docker run -p 3307:3306 --name mysql -e MYSQL_ROOT_PASSWORDhadoop -d mysql:8.0.21 启动成功后&#xff0c;进入容器内部拷贝配置文件&#xff0c;到宿主主机 docker cp mysql:/etc/mysql…

vue3项目常用功能分享

Vue3常用功能分享 本文主要分享一下在使用vue3开发项目时的一些常用功能 一、自动注册全局组件 自动注册components目录下所有vue组件并以组件的文件名为组件的名称 // components/index.tsimport { type App, defineAsyncComponent } from vue const components Object.e…

Sprint Boot 学习路线 5

Spring MVC Spring MVC是Spring框架的一部分&#xff0c;是一个Web应用程序框架。它旨在使用Model-View-Controller&#xff08;MVC&#xff09;设计模式轻松构建Web应用程序。 在Spring MVC中&#xff0c;应用程序被分为三个主要组件&#xff1a;Model、View和Controller。M…

2023年09月 Python(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 用枚举算法求解“100以内既能被3整除又能被4整除的元素”时,在下列数值范围内,算法执行效率最高的是?( ) A:1~101 B:4~100 C:12~100 D:12~96 答案:D 题目要求找出在 100…

什么是α测试β测试和灰度测试?

前言 看这个标题 α测试 β测试 &#xff0c;应该读做&#xff1a;阿尔法测试&#xff0c;贝特测试。 这样大家应该就都听过了吧&#xff0c;听过的同学举个手哈&#xff0c;听过的人大体上知道俩个测试是干嘛的&#xff0c;但是具体标准的大流程估计没人研究过&#xff0c;所…

VUE组件的生命周期

每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤&#xff0c;比如设置好数据侦听&#xff0c;编译模板&#xff0c;挂载实例到 DOM&#xff0c;以及在数据改变时更新 DOM。在此过程中&#xff0c;它也会运行被称为生命周期钩子的函数&#xff0c;让开发者有机会在特定阶…

数据结构 | 图

最小生成树算法 Prime算法 算法思路&#xff1a;从已选顶点所关联的未选边中找出权重最小的边&#xff0c;并且生成树不存在环。 其中&#xff0c;已选顶点是构成最小生成树的结点&#xff0c;未选边是不属于生成树中的边。 例子&#xff1a; 第一步&#xff1a; 假设我们从顶…

docker搭建mysql主从复制

1. 基础环境 环境 名称描述CentOS 7.6Linux操作系统版本docker 20.10.5docker版本mysql 8.0.29mysql镜像版本 节点 节点名称读写/主从地址端口master读节点/主节点192.168.1.6:3306slave1写节点/从节点192.168.1.6:3307slave2写节点/从节点192.168.1.6:3308 2. 主节点 使…

【Redis】Redis-Key的使用

上一篇&#xff1a; redis-server和redis-cli https://blog.csdn.net/m0_67930426/article/details/134361885?spm1001.2014.3001.5501 官网 命令 |雷迪斯 (redis.io) 设置key set name xxxxx 查看key keys * 再设置一个key并且查看 这里查看了两个key&#xff08;name a…

什么是代理模式,用 Python 如何实现 Proxy(代理 或 Surrogate)对象结构型模式?

什么是代理模式&#xff1f; 代理&#xff08;Proxy&#xff09;是一种结构型设计模式&#xff0c;其目的是通过引入一个代理对象来控制对另一个对象的访问。代理对象充当目标对象的接口&#xff0c;这样客户端就可以通过代理对象间接地访问目标对象&#xff0c;从而在访问过程…

【C++笔记】二叉搜索树的模拟实现

【C笔记】二叉搜索树的模拟实现 一、二叉搜索树的概念二、二叉搜索树的模拟实现2.0、定义二叉树节点2.1、非递归接口实现2.1.1、插入2.1.2、查找2.1.3、删除 2.2、递归接口实现2.2.1、插入2.2.2、查找2.2.3、删除 三、升级为K-V模型 一、二叉搜索树的概念 二叉搜索树的概念&am…

systemd-timesyncd

介绍 systemd-timesyncd 是一个用于跨网络同步系统时钟的守护服务。它实现了一个 SNTP 客户端。与NTP的复杂实现相比&#xff0c;这个服务简单的多&#xff0c;它只专注于从远程服务器查询然后同步到本地时钟。除非你打算为客户端提供 NTP 服务器或者连接本地硬件时钟&#xff…

【C++】C++的介绍及其发展史

初识C 一、什么是C&#xff0c;为什么会出现C二、C的发展史三、C的重要性3.1 语言的使用广泛度3.2 在工作领域 四、C的学习路径 及 书籍推荐 一、什么是C&#xff0c;为什么会出现C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。 对于复杂的问题&#xff…

【python】sys-psth和模块搜索路径

我们在导入一个模块的时候&#xff0c;比如说&#xff1a; import math它必然是有搜索路径的&#xff0c;那到底是在哪个目录下面找呢&#xff1f;Python解释器去哪里找这个文件呢&#xff1f;只有找到这个文件才能读取、装载运行该模块文件。 它一般按照如下路径寻找模块文件…

混合A Star算法

混合A Star算法就是将A*与Lattice Graph相关联。 使用栅格地图&#xff0c;保持在栅格地图的这个节点方格里都只记录一个机器人的State&#xff0c;例如图上的弯弯的线&#xff0c;进行剪枝&#xff0c;看一个节点的Cost&#xff08;时间、消耗的能量、路径的长度、终端状态惩…

ElasticSearch学习和使用 (使用head软件可视化es数据)

使用步骤 直接使用 Elasticsearch的安装和使用 下载Elasticsearch6.2.2的zip包&#xff0c;并解压到指定目录&#xff0c;下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2运行bin目录下的elasticsearch.bat启动Elasticsearch安…

五个做原型的好处和意义

绘制原型不仅是产品开发的重要基础&#xff0c;也是UI设计师在设计过程中应该采用的主要路径标识。绘制原型的重要性不亚于建筑师手中的设计图纸&#xff01;虽然原型设计可能会给产品开发带来一些误解&#xff0c;但毫不夸张地说&#xff0c;任何开发人员都可以将优秀的开发产…

SMART PLC MODBUSTCP速度测试

SMART PLC MODBUSTCP通信详细介绍请参看下面文章链接: S7-200SMART PLC ModbusTCP通信(多服务器多从站轮询)_matlab sumilink 多个modbustcp读写_RXXW_Dor的博客-CSDN博客文章浏览阅读6.4k次,点赞5次,收藏10次。MBUS_CLIENT作为MODBUS TCP客户端通过S7-200 SMART CPU上的…