深入探索String AOP想详情

news2024/9/22 4:10:54

一.AOP的概述

AOP是Spring框架的第⼆⼤核⼼(第⼀⼤核⼼是IoC)
Q:什么是AOP?
A:Aspect Oriented Programming(⾯向切⾯编程)
什么是⾯向切⾯编程呢? 切⾯就是指某⼀类特定问题, 所以AOP也可以理解为⾯向特定⽅法编程. 什么是⾯向特定⽅法编程呢? ⽐如上个章节学习的"登录校验", 就是⼀类特定问题. 登录校验拦截器, 就是对"登录校验"这类问题的统⼀处理. 所以, 拦截器也是AOP的⼀种应⽤. AOP是⼀种思想, 拦截器是AOP思想的⼀种实现. Spring框架实现了这种思想, 提供了拦截器技术的相关接⼝.
同样的, 统⼀数据返回格式和统⼀异常处理, 也是AOP思想的⼀种实现.
简单来说: AOP是⼀种思想, 是对某⼀类事情的集中处理.
什么是Spring AOP?
AOP是⼀种思想, 它的实现⽅法有很多, 有Spring AOP,也有AspectJ、CGLIB等.
Spring AOP是其中的⼀种实现⽅式.
学会了统⼀功能之后, 是不是就学会了Spring AOP呢, 当然不是.
拦截器作⽤的维度是URL(⼀次请求和响应), @ControllerAdvice 应⽤场景主要是全局异常处理
(配合⾃定义异常效果更佳), 数据绑定, 数据预处理. AOP作⽤的维度更加细致(可以根据包、类、⽅法名、参数等进⾏拦截), 能够实现更加复杂的业务逻辑.

二.AOP入门程序

现在有⼀些业务的执⾏效率⽐较低, 耗时较⻓, 我们需要对接⼝进⾏优化.
第⼀步就需要定位出执⾏耗时⽐较⻓的业务⽅法, 再针对该业务⽅法来进
如何定位呢? 我们就需要统计当前项⽬中每⼀个业务⽅法的执⾏耗时.
如何统计呢? 可以在业务⽅法运⾏前和运⾏后, 记录下⽅法的开始时间和结束时间, 两者之差就是这个⽅法的耗时.

 

这种⽅法是可以解决问题的, 但⼀个项⽬中会包含很多业务模块, 每个业务模块⼜有很多接⼝, ⼀个接⼝⼜包含很多⽅法, 如果我们要在每个业务⽅法中都记录⽅法的耗时, 对于程序员⽽⾔, 会增加很多的⼯作量。
AOP就可以做到在不改动这些原始⽅法的基础上, 针对特定的⽅法进⾏功能的增强.
接下来我们来看Spring AOP如何来实现。
1.引入依赖

 代码:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.在建包(aspect),创建一个TimeRecordAspect.类

  作用范围是controller包下所有类的方法。

代码:

package com.example.demo.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Slf4j
@Component//交给Spring管理
@Aspect
public class TimeRecordAspect {
/*记录耗时*/
    @Around("execution(* com.example.demo.controller.*.*(..))")//作用范围
    public Object timeRecord(ProceedingJoinPoint joinPoint) throws Throwable {
        //记录开始时间
        long start = System.currentTimeMillis();
        //执行目标方法
        Object proceed = joinPoint.proceed();
        //记录结束时间
        long end = System.currentTimeMillis();
        log.info("时间耗时:"+(end - start)+"ms");
        return  proceed;

    }

}
  1. @Aspect: 标识这是⼀个切⾯类
  2. @Around: 环绕通知, 在⽬标⽅法的前后都会被执⾏. 后⾯的表达式表⽰对哪些⽅法进⾏增强.
  3. ProceedingJoinPoint.proceed() 让原始方法执行

3.入侵 controller包的三个方法

package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;




@RequestMapping("t1")
    public void sayHi(){
       // userService.doService();
        System.out.println("do controller...");
    }
    public int test1(){
        int count = 0;
        for (int i = 0; i < 10000; i++) {
             count += i;
        }
        return count;
    }
    public int test2(){
        int count = 0;
        for (int i = 0; i < 10000000; i++) {
            count += i;
        }
        return count;
    }

}

4.运行程序,观察日志

我们通过AOP⼊⻔程序完成了业务接⼝执⾏耗时的统计.
通过上⾯的程序, 我们也可以感受到AOP⾯向切⾯编程的⼀些优势:
  1. 模块化:通过将横切关注点从主要业务逻辑中分离出来,实现了更好的模块化。在上面的例子中,服务团队负责处理非烹饪业务,使得厨师们可以专注于烹饪。同样,在软件开发中,AOP使得代码更加模块化,易于理解和维护。

  2. 重用性:AOP允许开发者将横切关注点封装为可重用的模块(切面),并在需要时将其应用于不同的模块中。例如,日志记录功能可以在多个服务或组件中被重用,而无需在每个地方都编写相同的代码。

  3. 解耦合:AOP将横切关注点与主要业务逻辑分离开来,降低了它们之间的耦合度。这使得代码更加灵活,易于修改和扩展。在上面的例子中,如果餐厅需要改变支付方式,服务团队可以独立地进行调整,而不需要厨师们参与。

  4. 提高开发效率:通过减少重复代码和降低耦合度,AOP提高了开发效率。开发者可以更快地编写和维护代码,同时减少了出错的可能性。

简而言之:

  • 代码⽆侵⼊: 不修改原始的业务⽅法, 就可以对原始的业务⽅法进⾏了功能的增强或者是功能的改变
  • 减少了重复代码
  • 提⾼开发效率
  • 维护⽅便


三.Spring AOP 详解

3.1Spring AOP中涉及的核⼼概念

3.1.1切点(Pointcut)

切点(Pointcut), 也称之为"切⼊点
Pointcut 的作⽤就是提供⼀组规则 (使⽤ AspectJ pointcut expression language 来描述), 告诉程序对哪些⽅法来进⾏功能增强.

上⾯的表达式 execution(* com.example.demo.controller.*.*(..)) 就是切点表达式.

3.1.2 连接点(Join Point)

满⾜切点表达式规则的⽅法, 就是连接点. 也就是可以被AOP控制的⽅法
以⼊⻔程序举例, 所有 com.example.demo.controller 路径下的⽅法, 都是连接点.
package com.example.demo.controller;

import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("Test")
public class UserController {
//    private  UserService userService;
//    @Autowired
//    private UserService userService;


//    //通过构造方法注入
//    public UserController( ) {
//
//    }
//    @Autowired
//    public UserController(UserService userService) {
//        this.userService = userService;
//    }

    //通过Setter方法注入
//    @Autowired
//
//    public void setUserService(UserService userService) {
//        this.userService = userService;
//    }






@RequestMapping("t1")
    public void sayHi(){
       // userService.doService();
        System.out.println("do controller...");
    }
    public int test1(){
        int count = 0;
        for (int i = 0; i < 10000; i++) {
             count += i;
        }
        return count;
    }
    public int test2(){
        int count = 0;
        for (int i = 0; i < 10000000; i++) {
            count += i;
        }
        return count;
    }

}
切点和连接点的关系
连接点是满⾜切点表达式的元素. 切点可以看做是保存了众多连接点的⼀个集合

3.1.3 通知(Advice)

通知就是具体要做的⼯作, 指哪些重复的逻辑,也就是共性功能(最终体现为⼀个⽅法)
⽐如上述程序中记录业务⽅法的耗时时间, 就是通知.
在AOP⾯向切⾯编程当中, 我们把这部分重复的代码逻辑抽取出来单独定义, 这部分代码就是通知的内容.

3.1.4 切⾯(Aspect)

切⾯(Aspect) = 切点(Pointcut) + 通知(Advice)
通过切⾯就能够描述当前AOP程序需要针对于哪些⽅法, 在什么时候执⾏什么样的操作.
切⾯既包含了通知逻辑的定义, 也包括了连接点的定义.

切⾯所在的类, 我们⼀般称为切⾯类 .

3.2Spring AOP通知类型

上⾯我们讲了什么是通知, 接下来学习通知的类型. @Around 就是其中⼀种通知类型, 表⽰环绕通知.
Spring中AOP的通知类型有以下⼏种:
  • @Around: 环绕通知, 此注解标注的通知⽅法在⽬标⽅法前, 后都被执⾏
  • @Before: 前置通知, 此注解标注的通知⽅法在⽬标⽅法前被执⾏
  • @After: 后置通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, ⽆论是否有异常都会执⾏
  • @AfterReturning: 返回后通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, 有异常不会执⾏
  • @AfterThrowing: 异常后通知, 此注解标注的通知⽅法发⽣异常后执⾏
接下来我们通过代码来加深对这⼏个通知的理解:
为⽅便学习, 我们可以新建⼀个项⽬
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
@Component
public class AspectDemo {
    //前置通知
    @Before("execution(* com.example.demo.controller.*.*(..))")
    public void doBefore() {
        log.info("执⾏ Before ⽅法");
    }
    //后置通知
    @After("execution(* com.example.demo.controller.*.*(..))")
    public void doAfter() {
        log.info("执⾏ After ⽅法");
    }
    //返回后通知
    @AfterReturning("execution(* com.example.demo.controller.*.*(..))")
    public void doAfterReturning() {
        log.info("执⾏ AfterReturning ⽅法");
    }
    //抛出异常后通知
    @AfterThrowing("execution(* com.example.demo.controller.*.*(..))")
    public void doAfterThrowing() {
        log.info("执⾏ doAfterThrowing ⽅法");
    }
    //添加环绕通知
    @Around("execution(* com.example.demo.controller.*.*(..))")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Around ⽅法开始执⾏");
        Object result = joinPoint.proceed();
        log.info("Around ⽅法结束执⾏");
        return result;
    }
}

 写⼀些测试程序:

package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/test")
@RestController
public class TestController {
    @RequestMapping("/t1")
    public String t1() {
        return "t1";
    }
    @RequestMapping("/t2")
    public boolean t2() {
        int a = 10 / 0;
        return true;
    }
}

运⾏程序, 观察⽇志:

1. 正常运⾏的情况

观察日志: 

程序正常运⾏的情况下, @AfterThrowing 标识的通知⽅法不会执⾏。
从上图也可以看出来, @Around 标识的通知⽅法包含两部分, ⼀个"前置逻辑", ⼀个"后置逻辑".其
中"前置逻辑" 会先于 @Before 标识的通知⽅法执⾏, "后置逻辑" 会晚于 @After 标识的通知⽅法执
2.异常时的情况
观察⽇志:
程序发⽣异常的情况下:
@AfterReturning 标识的通知⽅法不会执⾏, @AfterThrowing 标识的通知⽅法执⾏了
@Around 环绕通知中原始⽅法调⽤时有异常,通知中的环绕后的代码逻辑也不会在执⾏了(因为
原始⽅法调⽤出异常了)
注意事项:
@Around 环绕通知需要调⽤ ProceedingJoinPoint.proceed() 来让原始⽅法执⾏, 其他
通知不需要考虑⽬标⽅法执⾏.
@Around 环绕通知⽅法的返回值, 必须指定为Object, 来接收原始⽅法的返回值, 否则原始⽅法执
⾏完毕, 是获取不到返回值的.
⼀个切⾯类可以有多个切点.

3.3@PointCut

上⾯代码存在⼀个问题, 就是存在⼤量重复的切点表达式 execution(*
com.example.demo.controller.*.*(..)) , Spring提供了 @PointCut 注解, 把公共的切点
表达式提取出来, 需要⽤到时引⽤该切⼊点表达式即可.
上述代码就可以修改为:
@Slf4j
@Aspect
@Component
public class AspectDemo {
    //定义切点(公共的切点表达式)
    @Pointcut("execution(* com.example.demo.controller.*.*(..))")
    private void pt(){}
    //前置通知
    @Before("pt()")
    public void doBefore() {
        //...代码省略
    }
    //后置通知
    @After("pt()")
    public void doAfter() {
        //...代码省略
    }
    //返回后通知
    @AfterReturning("pt()")
    public void doAfterReturning() {
        //...代码省略
    }
    //抛出异常后通知
    @AfterThrowing("pt()")
    public void doAfterThrowing() {
        //...代码省略
    }
    //添加环绕通知
    @Around("pt()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //...代码省略
    }
}
当切点定义使⽤private修饰时, 仅能在当前切⾯类中使⽤, 当其他切⾯类也要使⽤当前切点定义时, 就需要把private改为public. 引⽤⽅式为: 全限定类名.⽅法名()
@Slf4j
@Aspect
@Component
public class AspectDemo2 {
 //前置通知
 @Before("com.example.demo.aspect.AspectDemo.pt()")
 public void doBefore() {
 log.info("执⾏ AspectDemo2 -> Before ⽅法");
 }
}

3.4@Order(切⾯优先级 )

存在多个切⾯类时, 默认按照切⾯类的类名字⺟排序:
@Before 通知:字⺟排名靠前的先执⾏
@After 通知:字⺟排名靠前的后执⾏
但这种⽅式不⽅便管理, 我们的类名更多还是具备⼀定含义的.
Spring 给我们提供了⼀个新的注解, 来控制这些切⾯通知的执⾏顺序: @Order
@Order 注解标识的切⾯类, 执⾏顺序如下:
@Before 通知:数字越⼩先执⾏
@After 通知:数字越⼤先执⾏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1994282.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙HarmonyOS开发:@Observed装饰器和@ObjectLink装饰器:监听嵌套类对象属性变化

文章目录 一、装饰器二、概述三、限制条件四、装饰器说明五、Toggle组件1、子组件2、接口3、ToggleType枚举4、事件 六、示例演示1、代码2、效果 一、装饰器 State装饰器&#xff1a;组件内状态Prop装饰器&#xff1a;父子单向同步Link装饰器&#xff1a;父子双向同步Provide装…

Linux云计算 |【第二阶段】NETWORK-DAY1

主要内容&#xff1a; 计算机网络概述、网络拓扑结构、OSI参考模型、eNSP、交换机及原理 一、计算机网络概述 计算机网络是指将多台计算机或设备通过通信线路连接起来&#xff0c;以便它们能够相互交换信息和共享资源的系统。计算机网络的目的是实现数据通信和资源共享&#…

生成式人工智能安全评估体系构建

文章目录 前言一、人工智能安全治理的现状1.1 国际安全治理现状1.2 国内安全治理现状二、构建人工智能安全评估体系1.1 需要对生成式人工智能技术的安全性、可靠性、可控性、公平性等维度进行全面的考量。1.2 应对生成式人工智能全维度风险。1.3 在体系化应对框架中,应明确法律…

[Meachines] [Easy] Traverxec Nostromo RCE+SSH-私钥RSA解密+journalctl权限提升

信息收集 IP AddressOpening Ports10.10.10.165TCP:22,80 $ nmap -p- 10.10.10.165 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION …

nfc苹果手机怎么开启?3招就教会你

在数字化时代&#xff0c;nfc技术已成为智能手机的一项重要功能&#xff0c;它让我们的设备能够进行无线数据交换&#xff0c;实现便捷的移动支付、文件传输等操作。那么&#xff0c;nfc苹果手机怎么开启呢&#xff1f;如果你想要充分利用这一技术&#xff0c;那么本文将为你提…

git推送错误-->远程分支比本地的分支更新,无法直接推送

每次上传本地修改好的代码的时候,十次有八次都会出现这样的问题!!(暴躁!!!) 现在写个帖子记录一下,这个问题目前我还没有解决,欢迎懂的佬指点一下. 情景: 我在本地仓库做了一些代码的修改,准备上传到远程仓库上,下边是上传步骤: git add . # 将所有的修改都提交到缓冲区git …

免费获取!Windows10 32位纯净版:无捆绑更安全!

今日&#xff0c;系统之家小编给大家分享2024最新的Windows10纯净版32位系统&#xff0c;该版本系统不包含任何捆绑软件&#xff0c;系统资源占用少&#xff0c;运作流畅&#xff0c;适合老旧电脑安装。系统安装起来特别简单&#xff0c;推荐大家使用硬盘安装&#xff0c;即便是…

大黄蜂能飞的起来吗?

Bumblebee argument 虽然早期的空气动力学证明大黄蜂不能飞行——因为体重太重&#xff0c;翅膀太薄&#xff0c;但大黄蜂并不知道&#xff0c;所以照飞不误。 背景 在20世纪初&#xff0c;‌科学家们通过研究发现&#xff0c;‌大黄蜂的身体与翼展的比例失调&#xff0c;‌按照…

OpenAI Embedding效果表现不佳:那如何评估选择Embedding?

OpenAI Embedding效果表现不佳 对文本进行Embedding是大模型应用中必不可少的一步&#xff0c; 虽然大模型OpenAI是最好的&#xff0c; 但OpenAI的Embedding却不是。本文Embedding评估的具体流程&#xff0c; 代码&#xff0c; 以及结论。 如果仅关心代码 & 结论&#xf…

软件项目质量管理体系,软件评审,资质认证,安全建设及项目管理匹配资料(原件参考)

软件项目质量管理体系是指一套系统化的管理方法、流程、工具和文档&#xff0c;旨在确保软件项目从需求分析、设计、开发、测试到部署和维护的整个生命周期中&#xff0c;都能达到预定的质量标准和客户期望。该体系通过明确的角色和责任、标准化的工作流程、有效的质量控制和持…

NC 找到搜索二叉树中两个错误的节点

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 描述 一棵二叉树原…

Windows系统下的MATLAB 虚拟机中Ubuntu系统的ROS通讯

在Windows系统下的MATLAB与虚拟机中Ubuntu系统的ROS进行通讯&#xff0c;主要涉及到网络配置、环境变量设置以及MATLAB与ROS的接口调用。以下是一个详细的步骤说明&#xff1a; 一、网络配置 设置虚拟机网络&#xff1a; 确保虚拟机&#xff08;Ubuntu系统&#xff09;的网络…

7-3 单机多卡并行

一台机器可以安装多个GPU&#xff08;1-16&#xff09;在训练和预测时&#xff0c;我们将一个小批量计算切分到多个GPU上来达到加速的目的 同样一个小批量&#xff0c;让用多个GPU同时运行来一起完成这个计算梯度的过程常用的切分方案有 数据并行 模型并行 通道并行&#xff…

中国中医科学院某医院:监控易7.0开启一体化运维新篇章

在首都北京的医疗领域中&#xff0c;中国中医科学院某医院作为佼佼者&#xff0c;一直致力于为患者提供高质量的医疗服务。然而&#xff0c;随着医院业务的不断扩展和IT系统的日益复杂&#xff0c;如何确保IT基础资源的稳定运行&#xff0c;成为了医院面临的一大挑战。为了应对…

Java继承细节

目录 继承给编程带来的便利 细节1 案例演示 细节2 案例演示 细节3 案例演示 细节4 案例演示 细节5 细节6 细节7 案例演示 细节8 细节9 细节10 继承给编程带来的便利 提高了代码的复用性&#xff0c;代码的扩展性与维护性。 细节1 子类继承了所有的属性和方法…

Java面试题--JVM大厂篇之破解 JVM 性能瓶颈:实战优化策略大全

目录 引言: 正文: 1. 常见的JVM性能问题 频繁的GC导致应用暂停 内存泄漏导致的内存不足 线程争用导致的CPU利用率过高 类加载问题导致的启动时间过长 2. 优化策略大全 2.1 代码层面的优化 2.1.1 避免不必要的对象创建 2.1.2 优化数据结构的选择 2.1.3 使用并发工具…

【文心超级智能体创造营】打造「潮流穿搭助手」智能体 引领潮流新动向

【文心超级智能体创造营】打造潮流穿搭助手 引领潮流新动向 文章目录 【文心超级智能体创造营】打造潮流穿搭助手 引领潮流新动向一、智能体发展背景二、文心智能体平台—AgentBuilder一图概览三、我的智能体—潮流穿搭助手3.1 智能体初步设想3.2 prompt提示词准备3.3 &#x1…

LabVIEW中CANopen 读取程序解读

这段程序用于创建 CANopen 接口&#xff0c;并读取 CANopen CAN 帧消息。以下是详细的解读&#xff1a; 左侧部分 node-ID (U8): 指定节点 ID&#xff0c;用于标识 CANopen 网络中的设备。CANopen interface (U32): 指定 CANopen 接口。baud rate (U32): 设置波特率&#xff0…

企业如何发布新闻稿到全球媒体上?

在经济全球化的今天&#xff0c;中国某个小县生产的商品可能会卖到全国各地&#xff0c;我们国家很多本土品牌面临出海的难题。当然有些企业已经通过国外的代理商把货品铺到了国外的市场&#xff0c;有的通过亚马逊这样的电商平台进行销售&#xff0c;但这些只是铺货&#xff0…

基于融合正余弦和柯西变异的麻雀搜索算法SCSSA优化CNN-BiLSTM的多变量时间序列预测

matlab R2024a以上 一、数据集 二、融合正余弦和柯西变异的麻雀搜索算法 麻雀搜索算法&#xff08;Sparrow Search Algorithm, SSA&#xff09;是一种新型的群体智能优化算法&#xff0c;其灵感来源于麻雀觅食行为。为了提高算法的性能&#xff0c;可以融合正余弦函数和柯西变…