5-19 SpringAop | 切面编程
Aop面向切面编程
什么是Aop
面向切面的程序设计(Aspect Oriented Programming)又译作剖面导向程序设计
和OOP(Object Oriented Programming)一样,也是计算机开发的一种程序设计思想
一句话概括面向切面编程
就是在不修改现有程序代码的前提下,可以设置某个方法运行之前或运行之后新增额外代码的操作
目标是将横切关注点与业务主体进行进一步分离,以提高程序代码的模块化程度。通过在现有代码基础上增加额外的通知(Advice)机制,能够对被声明为“切点(Pointcut)”的代码块进行统一管理与扩展
什么是切面
程序中的切面指的就是程序中方法的相互调用

名词解释
- 切面(aspect):是一个可以加入额外代码运行的特定位置,一般指方法之间的调用,可以在不修改原代码的情况下,添加新的代码,对现有代码进行升级维护和管理
- 织入(weaving):选定一个切面,利用动态代理技术,为原有的方法的持有者生成动态对象,然后将它和切面关联,在运行原有方法时,就会按照织入之后的流程运行了

通知(advice)
通知要织入的代码的运行时机
- 前置通知(before advice)
- 后置通知(after advice)
- 环绕通知(around advice)
- 异常通知(after throwing advice)
Spring实现Aop
之前我们明确了Spring框架的两大功能
- Ioc\DI
- AOP
实际上我们在项目开发的过程中,多处多次用到了AOP思想
它们都可以实现不修改代码就能新增各种功能
例如
- 过滤器
- Spring-Security(底层通过过滤器实现)
- SpringMvc统一异常处理类
- SpringValidation
- .....
实际上Spring只是实现Aop的方式之一
下面我们就使用Spring来实现Aop的功能
不必新建项目,使用任何SpringBoot项目都可以
我们使用csmall-business项目来测试Spring的AOP
<!-- 支持SpringAop注解的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
SpringAop的优势是通用性更强
项目中的任何由Spring保存的对象的方法都可以是Aop的目标
包括不限于控制层\业务层\持久层\其他类
我们首先来确定我们要aop的目标
我们可以在BusinessController类中新增一个方法用于测试aop
@GetMapping("/test")
@ApiOperation("Aop测试方法")
public JsonResult aopTest(){
System.out.println("控制器方法运行");
return JsonResult.ok("运行完成!");
}
要想添加aop的效果
我们可以新建一个包aspect
包中新建类DemoAspect
// 当前DemoAspect类的功能是为指定的方法进行aop实现
// 必须将当前切面设置类也交由Spring管理
@Component
// 表示当前类不是普通类,是做切面功能设计的
@Aspect
public class DemoAspect {
// 1.定义切面
// @Pointcut是指定切面方法的注解
// 注解中通过固定的格式指定或统配添加切面的方法
@Pointcut("execution(public * cn.tedu.csmall.business.controller" +
".BusinessController.aopTest(..))")
// 我们需要在注解下定义一个方法,代表这个切面的定义
// 这个方法不需要任何内容,方法名足矣
public void pointCut(){}
// 2.织入内容
// 向确定好的切面中添加需要运行的额外代码
// 我们需要设计它的运行时机,这里以前置运行为例
// 在注解中配置上面切面的方法名,pointCut()是上面方法名,带()是固定要求
@Before("pointCut()")
public void before(){
// 这个代码就会在aopTest方法运行之前运行
System.out.println("前置advice运行");
}
}
本地Nacos\seata
启动business
20000端口访问aop测试方法
运行后到控制台观察输出内容
各种advice和aop方法参数
正常的aop方法可能需要当前程序运行的一些状态
我们可以在advice方法的参数位置添加JoinPoint参数
在Before的方法参数中添加如下
@Before("pointCut()")
public void before(JoinPoint joinPoint){
// 这个代码就会在aopTest方法运行之前运行
System.out.println("前置advice运行");
// JoinPoint可以声明在任何织入方法的参数中
// JoinPoint会包含当前切面方法的各种信息,主要都是反射获取的
// 最常用的就是当前切面方法的方法信息,例如方法名
String methodName=joinPoint.getSignature().getName();
System.out.println("切面方法为:"+methodName);
}
后置异常和环绕Advice
// 当前DemoAspect类的功能是为指定的方法进行aop实现
// 必须将当前切面设置类也交由Spring管理
@Component
// 表示当前类不是普通类,是做切面功能设计的
@Aspect
public class DemoAspect {
// 1.定义切面
// @Pointcut是指定切面方法的注解
// 注解中通过固定的格式指定或统配添加切面的方法
@Pointcut("execution(public * cn.tedu.csmall.business.controller" +
".BusinessController.aopTest(..))")
// 我们需要在注解下定义一个方法,代表这个切面的定义
// 这个方法不需要任何内容,方法名足矣
public void pointCut(){}
// 2.织入内容
// 向确定好的切面中添加需要运行的额外代码
// 我们需要设计它的运行时机,这里以前置运行为例
// 在注解中配置上面切面的方法名,pointCut()是上面方法名,带()是固定要求
@Before("pointCut()")
public void before(JoinPoint joinPoint){
// 这个代码就会在aopTest方法运行之前运行
System.out.println("前置advice运行");
// JoinPoint可以声明在任何织入方法的参数中
// JoinPoint会包含当前切面方法的各种信息,主要都是反射获取的
// 最常用的就是当前切面方法的方法信息,例如方法名
String methodName=joinPoint.getSignature().getName();
System.out.println("切面方法为:"+methodName);
}
// 后置 Advice
@After("pointCut()")
public void after(){
System.out.println("后置advice运行");
}
// 异常 Advice (只有切面的方法发生异常时才会运行)
@AfterThrowing("pointCut()")
public void throwing(){
System.out.println("方法发生异常!");
}
// 环绕Advice
@Around("pointCut()")
// 环绕Advice要想正常执行,必须设置方法的返回值和参数
// 它能够实现切面方法运行前后都添加代码
// 参数类型必须是ProceedingJoinPoint,它是JoinPoint的子接口
// 它拥有更多方法,其中包含针对环绕Advice调用方法返回值的功能
// 环绕增强,参与到了原有方法代码的调用和返回值的接收工作
// 所以环绕增强需要讲原有方法的返回值返回才能有保持原有的工作流程
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 这个方法运行时,当前切面的目标方法还没有执行
System.out.println("环绕Advice前置执行");
// 环绕增强调用目标方法,并接收返回值(只有环绕增强有这个步骤)
Object obj=joinPoint.proceed();
// 这里目标方法已经执行完毕
System.out.println("环绕Advice后置执行");
// 千万别忘了要返回obj
return obj;
}
}

切面语法定义规则
上面课程中使用的切面定义语法为:
@Pointcut("execution(public * cn.tedu.csmall.business.controller" +
".BusinessController.aopTest(..))")
含义为public 修饰的,任何返回值的cn.tedu.csmall.business.controller包BusinessController类的aopTest方法
可以是任意参数
实际上这个定义切面的语法还有很多变化或通配,以满足各种切面定义需求
下面是详细语法规则的模板
execution(
modifier-pattern?
ret-type-pattern
declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?)
带?的是可选属性,不带?是必须写的
- modifier-pattern:访问修饰符(可选)
- ret-type-pattern:返回值类型(必写)
- declaring-type-pattern:全路径类名(可选)
- name-pattern:方法名(必写)
- param-pattern:参数列表(必写)
- throws-pattern:抛出的异常类型(可选)
分析下面的表达式设置切面的方法
execution(* *(..)):
匹配spring框架中(spring容器中)所有类得所有方法都定为切面
execution(public * com.test.TestController.*(..)):
匹配com.test.TestController类中的所有被public修饰方法定义为切面
execution(* cn.tedu.csmall.cart.mapper.*.*(..)):
匹配cn.tedu.csmall.cart.mapper包中所有接口\类的所有方法定义为切面
Aop实现业务逻辑层性能记录
我们想了解酷鲨商城的业务运行用时
我们可以在需要测试用时的模块中添加aop环绕Advice
在运行前和运行后分别记录时间,将它们相减,获得的时间差就是用时
我们以mall-order-webapi模块为例
pom文件添加依赖
<!-- 支持SpringAop注解的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
在创建aspect包
包中创建TimeAspect类
代码如下
@Component
@Aspect
public class TimeAspect {
// 定义切面,目标是当项目所有业务逻辑层方法
@Pointcut("execution(public * cn.tedu.mall.order.service.*.*(..))")
public void timer(){}
// 使用环绕Advice计时比较合理
@Around("timer()")
public Object timeRecord(ProceedingJoinPoint joinPoint) throws Throwable {
// 记录开始时间
long start=System.currentTimeMillis();
// 调用切面方法
Object obj=joinPoint.proceed();
// 记录结束时间
long end=System.currentTimeMillis();
// 计算时间差
long time= end-start;
// 获得方法名
String methodName=joinPoint.getSignature().getName();
// 输出方法用时
System.out.println(methodName+"方法用时"+time+"ms");
// 别忘了返回!
return obj;
}
}
使用虚拟机即可
没有虚拟机使用nacos\seata\redis
启动项目leaf\product\passport\order
5-19 SpringAop | 切面编程的更多相关文章
- Spring-AOP切面编程(3)
https://www.jianshu.com/p/be69b874a2a9 目录 1. Web MVC发展史历程2.Spring概要3.Spring-依赖注入概要(IOC)4.属性注入的三种实现方式 ...
- SpringAOP/切面编程示例
原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11833954.html Spring AOP/切面编程实例和一些注意事项, 主要是利用注解来实 ...
- JavaWeb_(Spring框架)SpringAOP面向切面编程
SpringAOP:面向切面编程(面向fifter编程) 通俗易懂术语:所有纵向重复的代码,我们提取成横向的代码 以下文章内容参考知乎:从0带你学习SpringAOP,彻底的理解AOP思想 传送门 1 ...
- SpringAOP 面向切面编程
AOP的相关概念 AOP:全称是 Aspect Oriented Programming 即:面向切面编程. 简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改 ...
- Spring-AOP面向切面编程
AOP是面向切面编程,区别于oop,面向对象,一个是横向的,一个是纵向. 主要解决代码分散和混乱的问题. 1.概念: 切面:实现AOP共有的类 通知:切面类中实现切面功能的方法 连接点:程序被通知的特 ...
- SpringAOP面向切面编程
Spring中三大核心思想之一AOP(面向切面编程): 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的 ...
- 了解并使用springAOP(面向切面编程)
Aop是干嘛的为什么要使用它 在业务系统中,总有一些散落,渗透到系统的各处且不得不处理的事情,这些穿插在既定业务中的操作就是所谓的“横切逻辑”,也称切面, 我们怎样才不受这些附加要求的干扰,专心于真正 ...
- 面向切面编程AOP
本文的主要内容(AOP): 1.AOP面向切面编程的相关概念(思想.原理.相关术语) 2.AOP编程底层实现机制(动态代理机制:JDK代理.Cglib代理) 3.Spring的传统AOP编程的案例(计 ...
- spring框架应用系列三:切面编程(带参数)
本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7786715.html 解决问题 1.分离业务监控与业务处理.简单点 ...
随机推荐
- 【导包】使用Sklearn构建Logistic回归分类器
官方英文文档地址:http://scikit-learn.org/dev/modules/generated/sklearn.linear_model.LogisticRegression.html# ...
- 虚拟 DOM 与 DOM Diff
虚拟 DOM 与 DOM Diff 本文写于 2020 年 9 月 12 日 虚拟 DOM 在今天已经是前端离不开的东西了,因为他的好处实在是太多了. 在<高性能 JavaScript>一 ...
- 基于SqlSugar的开发框架循序渐进介绍(2)-- 基于中间表的查询处理
在前面介绍的SqlSugar的相关查询处理操作中,我们主要以单表的方式生成相关的实体类,并在查询的时候,对单表的字段进行条件的对比处理,从而返回对应的数据记录.本篇随笔介绍在一些外键或者中间表的处理中 ...
- Vagrant之CentOS
Vagrant之CentOS Vagrant官网 https://www.vagrantup.com https://app.vagrantup.com/boxes/search https://ap ...
- 一些GIT操作的技巧
一.git stash 我们有时会遇到这样的情况,正在分支a上开发一半,然后分支b上发现Bug,需要马上处理.这时候分支a上的修改怎么办呢,git add 是不行的,有的git客户端版本会提示还有ad ...
- 好客租房28-state和this.setState(this.setState修改状态)
状态是改变的 语法:this.setstate 千万不要直接改变this.setState setState 修改state 更新ui 数据驱动视图 //导入react import Reac ...
- awd平台搭建及遇到的问题
1.安装docker环境 a.使用的是ubuntu系统,通过sudo apt install docker.io进行docker得安装,此方式会自动启动docker服务. b.通过curl -s ht ...
- 【Java面试】如何中断一个正在运行的线程?
一个去京东面试的工作了5年的粉丝来找我说: Mic老师,你说并发编程很重要,果然我今天又挂在一道并发编程的面试题上了. 我问他问题是什么,他说:"如何中断一个正在运行中的线程?". ...
- 声学感知刻度(mel scale、Bark scale、ERB)与声学特征提取(MFCC、BFCC、GFCC)
梅尔刻度 梅尔刻度(Mel scale)是一种由听众判断不同频率 音高(pitch)彼此相等的感知刻度,表示人耳对等距音高(pitch)变化的感知.mel 刻度和正常频率(Hz)之间的参考点是将1 k ...
- 「JOISC 2019 Day4」蛋糕拼接 3
loj 3039 NKOJ Description \(n\)个蛋糕,每个蛋糕有\(w_i,h_i\).选\(m\)个蛋糕满足\(\sum\limits_{j=1}^mw_{k_j}-\sum\lim ...