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 | 切面编程的更多相关文章

  1. Spring-AOP切面编程(3)

    https://www.jianshu.com/p/be69b874a2a9 目录 1. Web MVC发展史历程2.Spring概要3.Spring-依赖注入概要(IOC)4.属性注入的三种实现方式 ...

  2. SpringAOP/切面编程示例

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11833954.html Spring AOP/切面编程实例和一些注意事项, 主要是利用注解来实 ...

  3. JavaWeb_(Spring框架)SpringAOP面向切面编程

    SpringAOP:面向切面编程(面向fifter编程) 通俗易懂术语:所有纵向重复的代码,我们提取成横向的代码 以下文章内容参考知乎:从0带你学习SpringAOP,彻底的理解AOP思想 传送门 1 ...

  4. SpringAOP 面向切面编程

    AOP的相关概念 AOP:全称是 Aspect Oriented Programming 即:面向切面编程. 简单的说它就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理的技术,在不修改 ...

  5. Spring-AOP面向切面编程

    AOP是面向切面编程,区别于oop,面向对象,一个是横向的,一个是纵向. 主要解决代码分散和混乱的问题. 1.概念: 切面:实现AOP共有的类 通知:切面类中实现切面功能的方法 连接点:程序被通知的特 ...

  6. SpringAOP面向切面编程

    Spring中三大核心思想之一AOP(面向切面编程): 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的 ...

  7. 了解并使用springAOP(面向切面编程)

    Aop是干嘛的为什么要使用它 在业务系统中,总有一些散落,渗透到系统的各处且不得不处理的事情,这些穿插在既定业务中的操作就是所谓的“横切逻辑”,也称切面, 我们怎样才不受这些附加要求的干扰,专心于真正 ...

  8. 面向切面编程AOP

    本文的主要内容(AOP): 1.AOP面向切面编程的相关概念(思想.原理.相关术语) 2.AOP编程底层实现机制(动态代理机制:JDK代理.Cglib代理) 3.Spring的传统AOP编程的案例(计 ...

  9. spring框架应用系列三:切面编程(带参数)

    本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7786715.html 解决问题 1.分离业务监控与业务处理.简单点 ...

随机推荐

  1. JAVA 基础(1)开发环境的搭建以及开发工具的选择

    ​  我们现在还是在学习阶段因此我们不用配置那么多的jdk,配置一个jdk8就够应付日常的学习了.前面的文章我尽量写详细一些照顾刚入坑的朋友.后文还有教大家怎么使用企业版的idea. 一.开发环境的搭 ...

  2. 测试覆盖率 之 Cobertura的使用

    什么是代码覆盖率? 代码覆盖率是对整个测试过程中被执行的代码的衡量,它能测量源代码中的哪些语句在测试中被执行,哪些语句尚未被执行. 为什么要测量代码覆盖率? 众所周知,测试可以提高软件版本的质量和可预 ...

  3. 【Python爬虫案例】用Python爬取李子柒B站视频数据

    一.视频数据结果 今天是2021.12.7号,前几天用python爬取了李子柒的油管评论并做了数据分析,可移步至: https://www.cnblogs.com/mashukui/p/1622025 ...

  4. linux下的mysql数据库以及mysql主从复制

    参考博客 1.mysql数据库的安装 centos7如何安装mysql 1.yum安装 1.1首先配置yum源,然后再用yum进行安装 2. 源代码编译安装 3.rpm包安装 yum安装的前提条件,是 ...

  5. Docker容器的数据卷

    一.数据卷概念 1.数据卷是宿主机中的一个目录或文件 2.当容器目录和数据卷目录绑定后,对方的修改会立即同步 3.一个数据卷可以被多个容器同时挂载 4.一个容器也可以挂载多个数据卷 简单理解:有点类似 ...

  6. go thrift 开发

    thrift 从 0.9.1版本开始,可以完美支持 go 语言,可以完美的实现跨语言的 rpc 调用了.下面以 go 和 java 语言相互调用为例. 编辑协议文件,go 语言示例 /** examp ...

  7. axios源码解析 - 请求方法的别名实现

    axios中的创建请求方式很多,比如axios(url),axios.get(url),axios.post(url),axios.delete(url),方便快捷的api设计让axios火得一塌糊涂 ...

  8. grafana展示zabbix统计

    1.安装grafana   参照官网文档:https://grafana.com/grafana/download 我这边是centos系统,执行这两个命令 wget https://dl.grafa ...

  9. Netty源码研究笔记(4)——EventLoop系列

    1. Netty源码研究笔记(4)--EventLoop系列 EventLoop,即事件驱动,它是Netty的I/O模型的抽象,负责处理I/O事件.任务. 不同的EventLoop代表着不同的I/O模 ...

  10. 关于我学git这档子事(2)

    将本地main分支push到远程dev分支(不同名分支间的push) 远程dev分支还未创建 (在push同时创建远程dev分支,并将本地main分支内容上传) git push -u --set-u ...