目录

一、什么是AOP

二、AOP使用场景

三、使用AOP的好处

四、先举个例子理解AOP面向切面编程

五、Spring5.X的AOP切入点表达式有这些种写法

六、实战基于Spring的AOP快速实现通用日志打印

七、实战基于Spring的AOP快速实现统计接口耗时


本篇重点介绍了AOP面向切面编程,同时在实际项目中使用自定义注解后置通知和环绕通知方式分别用来实现日志统一收集和接口耗时统计功能。

一、什么是AOP

Aspect Oriented Program 面向切面编程,再通俗点说就是在不改变原有逻辑上增加额外的功能,比如解决系统层面的问题,或者增加新的功能

二、AOP使用场景

权限控制、缓存、日志处理、事务控制、接口统计耗时
AOP思想把功能分两个部分,分离系统中的各种关注点
    核心关注点
        业务的主要功能。就比如订单模块中的下单操作,属于业务的主要功能
    横切关注点
        非核心、额外增加的功能。就比如订单模块中下单操作之前的权限校验以及事务控制或者防重提交校验等

三、使用AOP的好处

减少代码侵入,解耦
    可以统一处理横切逻辑
    方便添加和删除横切逻辑

四、先举个例子理解AOP面向切面编程

用户下单逻辑中
    核心关注点:创建订单
    横切关注点:记录日志、控制事务、权限校验

VideoOrderService{//新增订单addOrder(){ }//查询订单findOrderById(){}//删除订单delOrder(){}//更新订单updateOrder(){}
}
JointPoint连接点:addOrder/findOrderById/delOrder/updateOrder
Pointcut切入点:过滤出那些JointPoint哪些目标函数进行切入,比如说记录日志,通常增删改
才需要记录日志,查询的日志可以忽略
Advice通知:在切入点中的函数上执行的动作,比如记录日志,权限校验的话,就在对应的方法上加上注解。
Aspect切面:由切入点和通知组合而成,定义通知应用到哪些切入点
Weaving织入:把切面的代码,应用到目标函数的过程

核心概念:

通知 Advice
    在特定的切入点上执行的增强处理,有5种通知
    @Before前置通知
        在执行目标方法之前运行
    @After后置通知
        在目标方法运行结束之后
    @AfterReturning返回通知
        在目标方法正常返回值后运行
    @AfterThrowing异常通知
        在目标方法出现异常后运行
    @Around环绕通知
        在目标方法完成前、后做增强处理 ,环绕通知是最重要的通知类型 ,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint,需要手动执行 joinPoint.procced()
    做啥? 比如你需要记录日志,控制事务 ,提前编写好通用的模块,需要的地方直接调用
连接点 JointPoint
    要用通知的地方,业务流程在运行过程中需要插入切面的具体位置,
    一般是方法的调用前后,全部方法都可以是连接点
    只是概念,没啥特殊
切入点 Pointcut
    不能全部方法都是连接点,通过特定的规则来筛选连接点, 就是Pointcut,选中那几个你想要的方法
    在程序中主要体现为书写切入点表达式(通过通配、正则表达式)过滤出特定的一组 JointPoint连接点
    过滤出相应的 Advice 将要发生的joinpoint地方
切面 Aspect
    通常是一个类,里面定义 切入点+通知 , 定义在什么地方; 什么时间点、做什么事情
    通知 advice指明了时间和做的事情(前置、后置等)
    切入点 pointcut 指定在什么地方干这个事情
    web接口设计中,web层->网关层->服务层->数据层,每一层之间也是一个切面,对象和对象,方法和方法之间都是一个个切面
目标 target
    目标类,真正的业务逻辑,可以在目标类不知情的条件下,增加新的功能到目标类的链路上
织入 Weaving
    把切面(某个类)应用到目标函数的过程称为织入
AOP代理
    AOP框架创建的对象,代理就是目标对象的加强
    Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理

下单权限校验/日志业务流程伪代码例子:

//目标类 VideoOrderService; 里面每个方法都是连接点,;切入点是CUD类型的方法,R读取的不作为切入点
//CRDU全称:增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete)VideoOrderService{//新增订单addOrder(){ }//查询订单findOrderById(){}//删除订单delOrder(){}//更新订单updateOrder(){}
}//权限切面类 = 切入点+通知
PermissionAspects{//切入点  定义了什么地方@Pointcut("execution(public int net.wnn.permission.service.VideoOrderService.*(..))")public void pointCut(){}//before 通知 表示在目标方法执行前切入, 并指定在哪个方法前切入//什么时候,做什么事情@Before("pointCut()")public void permissionCheck(){System.out.println("在 xxx 之前执行权限校验");}....
}//日志切面类 = 切入点+通知
LogAspect{//切入点  定义了什么地方@Pointcut("execution(public int net.wnn.permission.service.VideoOrderService.*(..))")public void pointCut(){}//after 通知 表示在目标方法执行后切入, 并指定在哪个方法前切入//什么时候,做什么事情@After("pointCut()")public void logStart(){System.out.println("在 xxx 之后记录日志");}....
}

五、Spring5.X的AOP切入点表达式有这些种写法

切入点表示式,除了返回类型、方法名和参数外,其它项都是可选的 (修饰符基本都是省略不写)
                 访问修饰符           返回值类型(必填)      包和类                     方法(必填)
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)  throws-pattern?) 
比如上面的:
@Pointcut("execution(public int net.wnn.permission.service.VideoOrderService.*(..))")
其中:*:匹配任何数量字符 单个VideoOrderService.*表示VideoOrderService下任意方法
..:    () 匹配一个不接受任何参数的方法
    (..) 匹配一个接受任意数量参数的方法
    (*) 匹配了一个接受一个任何类型的参数的方法
    (*,Integer) 匹配了一个接受两个参数的方法,其中第一个参数是任意类型,第二个参数必须是Integer类型

常见的例子:
任意公共方法
execution(public * *(..))

任何一个名字以“save”开始的方法
execution(* save*(..))

VideoService接口定义的任意方法(识别)
execution(* net.wnn.service.VideoService.*(..))

在service包中定义的任意方法(识别)
execution(* net.wnn.service.*.*(..))

匹配 service 包,子孙包下所有类的所有方法(识别)
execution(* net.wnn.service..*.*(..))

六、实战基于Spring的AOP快速实现通用日志打印

第一步开启SpringAOP注解配置


@Configuration
@ComponentScan("net.wnn")
@EnableAspectJAutoProxy  //开启了spring对aspect的支持
public class AnnotationAppConfig {}

第二步

配置切入点和通知。自定义注解类型配置

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OpLog {/**** 操作模块*/String opModule();/**** 操作类型*/String operType();/**** 操作描述*/String operDesc();
}
import lombok.extern.slf4j.Slf4j;
import net.wnn.model.OpLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;//让spring进行扫描 一定要加
@Component
//告诉spring,这个一个切面类,里面可以定义切入点和通知
@Aspect
@Slf4j
public class LogAdvice {/*** 首先定义一个切点*/@Pointcut("@annotation(net.wnn.model.OpLog)")public void printLog() {}@After("printLog()")public void after(JoinPoint joinPoint){try {//获取方法名称String methodName = joinPoint.getSignature().getName();//获取类名称String className = joinPoint.getSignature().getDeclaringTypeName();System.out.println("类名:"+className+" 方法名:"+methodName);// 从切面织入点处通过反射机制获取织入点处的方法MethodSignature signature =(MethodSignature) joinPoint.getSignature();// 获取切入点所在的方法Method method =signature.getMethod();// 获取操作OpLog opLog =method.getAnnotation(OpLog.class);log.info("模块名称:[{}],类型:[{}],描述信息:[{}]",opLog.opModule(),opLog.operType(),opLog.operDesc());//执行连接点的方法try {((ProceedingJoinPoint)joinPoint).proceed();} catch (Throwable throwable) {throwable.printStackTrace();}} catch (Throwable throwable) {throwable.printStackTrace();}}}

第三步在请求方法上增加注解

  /*** 用户登录* @param request* @return*/@PostMapping("login")@OpLog(opModule = "用户模块",operType = "登录操作",operDesc = "此方法用户用户登录")public JsonData login(@RequestBody AccountLoginRequest request){JsonData jsonData = accountService.login(request);return jsonData;}

postman请求接口验证:

日常开发工作中,可以根据实际日志存储需求,将统一收集到的需求进行入库等操作。

七、实战基于Spring的AOP快速实现统计接口耗时

第一步开启SpringAOP注解配置


@Configuration
@ComponentScan("net.wnn")
@EnableAspectJAutoProxy  //开启了spring对aspect的支持
public class AnnotationAppConfig {}

第二步配置切入点

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;//让spring进行扫描 一定要加
@Component
//告诉spring,这个一个切面类,里面可以定义切入点和通知
@Aspect
@Slf4j
public class LogAdvice {//切入点表达式,也可以直接在通知上编写切入点表达式@Pointcut("execution(* net.wnn.service.impl.AccountServiceImpl.*(..))")public void aspect(){}@Around("aspect()")public void around(JoinPoint joinPoint){Object target = joinPoint.getTarget().getClass().getName();//通过joinPoint获取参数Object [] args = joinPoint.getArgs();log.info("调用者:{} 调用方法:{} 调用参数:{}",target,joinPoint.getSignature(),args[0]);long start = System.currentTimeMillis();log.info("===========环绕通知 环绕前========");//执行连接点的方法try {((ProceedingJoinPoint)joinPoint).proceed();} catch (Throwable throwable) {throwable.printStackTrace();}long end = System.currentTimeMillis();log.info("===========环绕通知 环绕后========");log.info("调用方法总耗时 time = " + (end - start) +" ms");}}

控制台输出:

实际开发工作中,可根据接收到的方法 以及耗时进行入库统计展示。

AOP面向切面编程之全局日志打印/统计接口耗时相关推荐

  1. 面向切面编程:操作日志

    在项目中,操作日志至关重要,操作日志记录的是:谁干了什么事: 我们可以利用面向切面编程实现操作日志的打印与记录: 1.首先,添加依赖: <!--spring aop 依赖--><de ...

  2. spring中AOP(面向切面编程)

    spring中AOP(面向切面编程) 面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是spring框架中的一个重要内容 ...

  3. SpringBoot之AOP面向切面编程实例

    目录 1.引入pom依赖 2.切入点表达式 --组成 --逻辑运算符 --通配符 --范例 3. 启动类配置 4.通知类型 4.1 @Before : 标注当前方法作为前置通知 4.1.1 创建自定义 ...

  4. 【AOP 面向切面编程】AOP 简介 ( AspectJ 简介 | AspectJ 下载 )

    文章目录 一.AOP 简介 二.AspectJ 简介 三.AspectJ 下载 一.AOP 简介 AOP 是 Aspect Oriented Programming 的缩写 , 面向切面编程 ; 利用 ...

  5. 大数据WEB阶段Spring框架 AOP面向切面编程(一)

    Spring - AOP面向切面编程(一) 一.代理模式概述 代理的特点:(目标对象即被代理者) 实现和目标对象相同的接口 具备和目标对象的方法 代理者不仅要做目标对象的方法 , 还要做一些额外的操作 ...

  6. AOP(面向切面编程)大概了解一下

    前言 上一篇在聊MemoryCache的时候,用到了Autofac提供的拦截器进行面向切面编程,很明显能体会到其优势,既然涉及到了,那就趁热打铁,一起来探探面向切面编程. 正文 1. 概述 在软件业, ...

  7. python aop编程_学习笔记: AOP面向切面编程和C#多种实现

    AOP:面向切面编程   编程思想 OOP:一切皆对象,对象交互组成功能,功能叠加组成模块,模块叠加组成系统 类--砖头     系统--房子 类--细胞     系统--人 面向对象是非常适合做大型 ...

  8. AOP—面向切面编程

    前言 上一篇在聊MemoryCache的时候,用到了Autofac提供的拦截器进行面向切面编程,很明显能体会到其优势,既然涉及到了,那就趁热打铁,一起来探探面向切面编程. 正文 概述 在软件业,AOP ...

  9. Springboot 一文搞懂AOP面向切面编程

    Springboot AOP面向切面编程 AOP简介 AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构. 作用:在不惊动原始设计的 ...

最新文章

  1. 人工智能产业趋势和机遇!
  2. 【视频】vue指令之v-else-if
  3. linux驱动开发音频设备驱动,linux驱动开发—基于Device tree机制的驱动编写
  4. 朴素贝叶斯分类器python_朴素贝叶斯分类器及Python实现
  5. iOS 手记 - 计算文字高度/宽度:- (CGSize)sizeWithAttributes:(NSDictionaryNSString *,id *)attrs...
  6. 【代码笔记】iOS-切换条
  7. mysql数据库编程第六章试题_2016计算机二级MySQL数据库试题及答案
  8. PCWorld盘点2011科技界15大败笔 索尼居首(图)
  9. 如何快速出机械工程图
  10. 出行即服务(MAAS)框架
  11. <Linux>计算机体系结构和操作系统
  12. iOS开发——frame和bounds详解
  13. VALUE DATE
  14. 三种开窗函数详细用法,图文详解
  15. LED阵列PCB灯板绘制
  16. 制作淘宝详情页时要注意哪些细节?
  17. 旺季选品逻辑:用数据思维找到潜力爆款
  18. m1芯片 php,苹果M1 版 MacBook软件兼容实测:VS Code不能用 PHPStorm可运行
  19. 【听】告別玻璃心的十三件事,走出舒适圈
  20. 恒润达生筹备科创板上市:李国顺合计控股49%,深创投等为投资方

热门文章

  1. linux 调用rest接口,REST调用
  2. Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
  3. vue 项目中使用v-loading实现加载效果
  4. 关于$(#div).load()的加载内容
  5. Linux删掉一级目录,linux命令删除文件夹
  6. java计算机毕业设计校园酒店管理系统源程序+mysql+系统+lw文档+远程调试
  7. 黑龙江工业机器人夹爪_一种工业机器人专用夹爪的制作方法
  8. SqlServer数据库远程链接失败问题
  9. windows与linux文件互传,实现Linux与windows文件互传
  10. Namespace基本知识