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分享客栈】SpringBoot线程池参数搜一堆资料还是不会配,我花一天测试换你此生明白。

    一.前言   首先说一句,如果比较忙顺路点进来的,可以先收藏,有时间或用到了再看也行:   我相信很多人会有一个困惑,这个困惑和我之前一样,就是线程池这个玩意儿,感觉很高大上,用起来很fashion, ...

  2. Shell脚本实战:日志关键字监控+自动告警

    一个执着于技术的公众号 该程序使用场景说明:主要用于Linux服务器监控程序日志,如出现关键字异常则触发相应的动作或告警操作,通知到邮件联系人. 一.安装邮件服务 1.解压 tar -jxf mail ...

  3. mysql5.6 innodb_large_prefix引起的一个异常

    phenomenon: Specified key was too long; max key length is 3072 bytes 在修改一个数据库字段时,字段容量被限制为了表前缀的大小而不是本 ...

  4. Django学习——ajax发送其他请求、上传文件(ajax和form两种方式)、ajax上传json格式、 Django内置序列化(了解)、分页器的使用

    1 ajax发送其他请求 1 写在form表单 submit和button会触发提交 <form action=""> </form> 注释 2 使用inp ...

  5. python之贪婪算法

    贪婪算法 贪婪算法也称为最优算法,这种算法并不是最准确的答案,但确认最接近答案的近似算法. 这时候有人会问,不是最准确的答案我要她干嘛?但是在日常中,我们有时候会遇到一些我们无法处理的问题,甚至是要花 ...

  6. brew常用命令

    Homebrew 常用命令 brew -help # 查看帮助命令 brew config # 查看配置信息 brew list # 查看已安装软件包列表 brew cleanup # 清理所有包的旧 ...

  7. 『忘了再学』Shell基础 — 20、Shell中的运算符

    目录 1.Shell常用运算符 2.Shell中数值运算的方法 (1)方式一 (2)方式二 (3)方式三(推荐) 1.Shell常用运算符 Shell中常用运算符如下表: 优先级数值越大优先级越高,具 ...

  8. MySQL中读页缓冲区buffer pool

    Buffer pool 我们都知道我们读取页面是需要将其从磁盘中读到内存中,然后等待CPU对数据进行处理.我们直到从磁盘中读取数据到内存的过程是十分慢的,所以我们读取的页面需要将其缓存起来,所以MyS ...

  9. 编程式导航路由跳转到当前路由(参数不变), 多次执行会抛出NavigationDuplicated的警告错误?

    注意:编程式导航(push|replace)才会有这种情况的异常,声明式导航是没有这种问题,因为声明式导航内部已经解决这种问题. 这种异常,对于程序没有任何影响的. 为什么会出现这种现象: 由于vue ...

  10. SRE,了解一下?35+岁程序员新选择

    摘要:随着云业务的发展,今后会有越来越多的工程师深入到SRE领域. 本文分享自华为云社区<浅谈SRE角色认知>,作者: SRE确定性运维. 一.什么是SRE? SRE(Site Relia ...