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. HCNP Routing&Switching之RSTP

    前文我们了解了vlan优化,vlan聚合技术super vlan相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/16208997.html:今天我们来聊 ...

  2. Oracle19c单实例数据库配置OGG单用户数据同步测试

    目录 19c单实例配置GoldenGate 并进行用户数据同步测试 一.数据库操作 1.开启数据库附加日志 2.开启数据库归档模式 3.开启goldengate同步 4.创建goldengate管理用 ...

  3. 150. Evaluate Reverse Polish Notation - LeetCode

    Question 150. Evaluate Reverse Polish Notation Solution 2 1 + 3 * 是((2+1)*3)的后缀(postfix)或逆波兰(reverse ...

  4. 好客租房8-React基础阶段总结

    React总结 1react是构建用户组件的javascript库 2使用react是,推荐使用脚手架方式 3初始化项目命令:npx create-react-app my-app 4启动项目命令:y ...

  5. 基础篇:java GC 总结,建议收藏

    垃圾标记算法 垃圾回收算法 major gc.mini gc.full gc.mixed gc 又是什么,怎么触发的 垃圾回收器的介绍 Safe Point 和 Safe Region 什么是 TLA ...

  6. vuex+Es6语法补充-Promise

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,采用 集中式存储管理 单页面的状态管理/多页面状态管理 使用步骤: // 1.导入 import Vuex from 'vuex' // ...

  7. 聚类--DBSCN

    1.什么是DBSCN DBSCAN也是一个非常有用的聚类算法. 它的主要优点:它不需要用户先验地设置簇的个数,可以划分具有复杂形状的簇,还可以找出不属于任何簇的点. DBSCAN比凝聚聚类和k均值稍慢 ...

  8. 【抬杠.NET】如何进行IL代码的开发(续)

    背景 之前写了一篇文 [抬杠.NET]如何进行IL代码的开发 介绍了几种IL代码的开发方式. 创建IL项目 C#项目混合编译IL 使用InlineIL.Fody 使用DynamicMethod(ILG ...

  9. 2021.03.06【NOIP提高B组】模拟 总结

    T1 看起来十分复杂,打表后发现答案是 \(n*m\mod p\) 具体的证明... 原式的物理意义,就是从坐标原点(0,0),用每一种合法的斜率, 穿过坐标[1 ~ n , 1 ~ m]的方阵中的整 ...

  10. Arraylist集合、对象数组

    Arraylist集合 ArrayList是List接口的一个实现类,它是程序中最常见的一种集合. 他的特点:在增加或删除指定位置的元素时,会创建新的数组,效率比较低,因此不适合做大量的增删操作,Ar ...