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. [AcWing 28] 在O(1)时间删除链表结点

    点击查看代码 /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * L ...

  2. 《Java编程思想》读书笔记(二)

    三年之前就买了<Java编程思想>这本书,但是到现在为止都还没有好好看过这本书,这次希望能够坚持通读完整本书并整理好自己的读书笔记,上一篇文章是记录的第一章到第十章的内容,这一次记录的是第 ...

  3. C Primer Plus 学习笔记 -- 前六章

    记录自己学习C Primer Plus的学习笔记 第一章 C语言高效在于C语言通常是汇编语言才具有的微调控能力设计的一系列内部指令 C不是面向对象编程 编译器把源代码转化成中间代码,链接器把中间代码和 ...

  4. 一、全新安装搭建redis主从集群

    前言· 这里分为三篇文章来写我是如何重新搭建redis主从集群和哨兵集群的及原本服务器上有单redis如何通过升级脚本来实现redis集群.(redis结构:主-从(备)-从(备)) 至于为什么要搭建 ...

  5. 虚拟机:ESX

    VMware ESXi 与ESX 产品之比较   VMware vSphere 5.0 以后版本,所有底层虚拟化产品都改为ESXi产品,本文主要比较了ESXi与ESX的各自特点,以便对大家是否要把现有 ...

  6. Spring Authorization Server 0.3.0 发布,官方文档正式上线

    基于OAuth2.1的授权服务器Spring Authorization Server 0.3.0今天正式发布,在本次更新中有几大亮点. 文档正式上线 Spring Authorization Ser ...

  7. Unicode和中午互转

    import java.io.UnsupportedEncodingException; public class TestUnicode{ /* * 中文转unicode编码 */ public s ...

  8. C#/VB.NET 在Word转PDF时生成目录书签

    当我们在转换Word文档到PDF格式时,想保留Word文档的标题作为PDF书签,那么应该如何操作呢?那么本文将以C#及VB.NET代码为例,介绍如何在Word转PDF时生成目录书签.下面是具体方法和步 ...

  9. JavaScript中的??和?.和??=操作符

    JS中两种不常使用但挺实用的操作符:??和?. 一起来了解并学会使用它们吧: 空值合并操作符:?? 只有当操作符左侧为null或undefined时才会返回操作符右侧的值,否则返回左侧的值. eg: ...

  10. Eureka属性配置

    一:Eureka Instance实例信息配置   里面的配置以"-"隔开 其实也支持驼峰命名代替"-" 首先是入门时的配置: server: port: 80 ...