【Spring】 AOP面向切面编程

news2024/9/22 12:35:26

文章目录

  • AOP是什么?
  • 一、AOP术语名词介绍
  • 二、Spring AOP框架介绍和关系梳理
  • 三、Spring AOP基于注解方式实现和细节
    • 3.1 Spring AOP底层技术组成
    • 3.2 初步实现
    • 3.3 获取通知细节信息
    • 3.4 切点表达式语法
    • 3.5 重用(提取)切点表达式
    • 3.6 环绕通知
    • 3.7 切面优先级设置
    • 3.8 CGLib动态代理生效
    • 3.9 注解实现小结
  • 四、Spring AOP对获取Bean的影响理解
  • 总结
    • 代理介绍:
    • AOP名词理解:
    • AOP思维以及与代理的关系:


AOP是什么?

AOP:Aspect Oriented Programming面向切面编程,解决非核心代码的冗余。

  • AOP利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。
  • 所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
  • 使用AOP,可以在不修改原来代码的基础上添加新功能。

AOP思想主要的应用场景:

  1. 日志记录:在系统中记录日志是非常重要的,可以使用AOP来实现日志记录的功能,可以在方法执行前、执行后异常抛出时记录日志
  2. 事务处理:在数据库操作中使用事务可以保证数据的一致性,可以使用AOP来实现事务处理的功能,可以在方法开始前开启事务,在方法执行完毕后提交或回滚事务
  3. 安全控制:在系统中包含某些需要安全控制的操作,如登录、修改密码、授权等,可以使用AOP来实现安全控制的功能。可以在方法执行前进行权限判断,如果用户没有权限,则抛出异常或转向到错误页面,以防止未经授权的访问。
  4. 性能监控:在系统运行过程中,有时需要对某些方法的性能进行监控,以找到系统的瓶颈并进行优化。可以使用AOP来实现性能监控的功能,可以在方法执行前记录时间戳,在方法执行完毕后计算方法执行时间并输出到日志中。
  5. 异常处理:系统中可能出现各种异常情况,如空指针异常、数据库连接异常等,可以使用AOP来实现异常处理的功能,在方法执行过程中,如果出现异常,则进行异常处理(如记录日志、发送邮件等)。
  6. 缓存控制:在系统中有些数据可以缓存起来以提高访问速度,可以使用AOP来实现缓存控制的功能,可以在方法执行前查询缓存中是否有数据,如果有则返回,否则执行方法并将方法返回值存入缓存中。
  7. 动态代理:AOP的实现方式之一是通过动态代理,可以代理某个类的所有方法,用于实现各种功能。
  • 综上所述,AOP可以应用于各种场景,它的作用是将通用的横切关注点与业务逻辑分离,使得代码更加清晰、简洁、易于维护。

一、AOP术语名词介绍

  • 横切关注点
    • 从每个方法中抽取出来的同一类非核心业务
    • 在同一个项目中,我们可以使用多个横切关注点对相关方法进行多个不同方面的增强。
    • 这个概念不是语法层面天然存在的,而是根据附加功能的逻辑上的需要:有十个附加功能,就有十个横切关注点

·

  • AOP把软件系统分为两个部分:核心关注点横切关注点

    • 业务处理的主要流程是核心关注点,非核心代码是横切关注点。
    • 横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事务、异常等。
    • AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。(分离核心业务代码与非核心代码
  • 通知(增强)

    • 每一个横切关注点上要做的事情都需要写一个方法来实现,这样的方法就叫通知方法。
      • 前置通知:在被代理的目标方法执行
      • 返回通知:在被代理的目标方法成功结束后执行(寿终正寝
      • 异常通知:在被代理的目标方法异常结束后执行(死于非命
      • 后置通知:在被代理的目标方法最终结束后执行(盖棺定论
      • 环绕通知:使用try...catch...finally结构围绕整个被代理的目标方法,包括上面四种通知对应的所有位置

1

  • 切入点 pointcut
    定位连接点的方式,或者可以理解成被选中的连接点!
    是一个表达式,比如execution(* com.spring.service.impl..(…))。
    符合条件的每个方法都是一个具体的连接点。

  • 切面 aspect
    切入点和通知的结合。是一个类。
    1

  • 目标 target
    被代理的目标对象。

  • 代理 proxy
    向目标对象应用通知之后创建的代理对象。

  • 织入 weave
    指把通知应用到目标上,生成代理对象的过程。
    可以在编译期织入,也可以在运行期织入,Spring采用后者。

二、Spring AOP框架介绍和关系梳理

  • AOP一种区别于OOP编程思维,用来完善和解决OOP的非核心代码冗余和不方便统一维护问题!
  • 代理技术(动态代理|静态代理)是实现AOP思维编程的具体技术,但是自己使用动态代理实现代码比较繁琐!
  • Spring AOP框架基于AOP编程思维封装动态代理技术,简化动态代理技术实现的框架!
    • SpringAOP内部帮助我们实现动态代理,
    • 我们只需写少量的配置指定生效范围即可完成面向切面思维编程的实现!

三、Spring AOP基于注解方式实现和细节

3.1 Spring AOP底层技术组成

1

  • 动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口
    • 因为这个技术要求代理对象和目标对象实现同样的接口(兄弟两个拜把子模式)。
  • cglib:通过继承被代理的目标类(认干爹模式)实现代理,所以不需要目标类实现接口。
  • AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ中的AOP注解。

3.2 初步实现

1
1

  1. 导入依赖
<!-- spring-aspects会帮我们传递过来aspectjweaver -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.0.6</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.6</version>
</dependency>
  1. 准备接口
public interface Calculator {
    
    int add(int i, int j);
    
    int sub(int i, int j);
    
    int mul(int i, int j);
    
    int div(int i, int j);
    
}
  1. 纯净实现类
/**
 * @Description: 实现计算接口,单纯添加加减乘除 实现,掺杂其它功能
 *  AOP -》 只针对IOC容器对象 - 创建代理对象 -> 将代理对象存储到IOC容器
 */
@Component
public class CalculatorImpl implements Calculator {
    @Override
    public int add(int i, int j) {

        int result = i + j;

        return result;
    }

    @Override
    public int sub(int i, int j) {

        int result = i - j;

        return result;
    }

    @Override
    public int mul(int i, int j) {

        int result = i * j;

        return result;
    }

    @Override
    public int div(int i, int j) {

        int result = i / j;

        return result;
    }
}
  1. 声明切面类
package com.doug.advice;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

/**
 * @Description: 增强类 实现增强方法
 */
@Component
@Aspect // @Aspect表示这个类是一个切面类
public class LogAspect {
    /*
    * 1. 定义方法存储增强代码
    *       具体定义几个方法,根据插入的位置决定!
    * 2. 使用配置注解 指定插入目标方法的位置
    *   前置 @Before
    *   后置 @AfterReturning
    *   异常 @AfterThrowing
    *   最后 @After
    *   环绕 @Around
    *
    *   try{
    *       前置
    *       目标方法执行
    *       后置
    *   }catch(){
    *       异常
    *   }finally{
    *       最后
    *   }
    *
    * 3.配置切点表达式 [选择要插入的方法  切点
    * 4.注解补全
    *   加入IOC容器 @Component
    *   配置切面 @Aspect
    * 5.开启Aspect注解的支持
    * */

    @Before("execution(* com.doug.aop.impl.*.*(..))")
    public void start(){
        System.out.println("方法开始了");
    }

    @After("execution(* com.doug.aop.impl.*.*(..))")
    public void after(){
        System.out.println("方法结束了");
    }

    @AfterThrowing("execution(* com.doug.aop.impl.*.*(..))")
    public void error(){
        System.out.println("方法报错了");
    }
}
  1. 配置类
@Configuration
@ComponentScan("com.doug")
@EnableAspectJAutoProxy // 开启Aspectj的注解  xml: <aop:aspectj-autoproxy />
public class MyConfiguration {
}
  1. 测试
@SpringJUnitConfig(value = MyConfiguration.class)
public class TestAop {
    @Autowired
    private Calculator calculator;

    @Test
    public void aopTest(){
        int add = calculator.add(1, 0);
        System.out.println(add);
    }
}

1

3.3 获取通知细节信息

  • JointPoint接口
    • JoinPoint 接口通过 getSignature() 方法获取目标方法的签名(方法声明时的完整信息)
  • 方法返回值
    • 在返回通知中,通过 @AfterReturning注解的returning属性获取目标方法的返回值!
  • 异常对象捕捉
    • 在异常通知中,通过**@AfterThrowing**注解的throwing属性获取目标方法抛出的异常对象
package com.doug.advice;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.lang.reflect.Modifier;

/**
 * @Description: 定义四个增强方法,获取目标方法的信息 返回值 异常
 *  1. 定义方法 - 增强代码
 *  2. 使用注解指定对应的位置
 *  3. 配置切点表达式选中方法
 *  4. 切面和IOC的配置
 *  5. 开启aspectj注解的支持
 */
@Component
@Aspect
public class MyAdvice {
    /*
    *   增强方法中获取目标方法信息:
    *
    *  1. 全部增强方法中,获取目标方法的信息(方法名,参数,访问修饰符,所属类的信息...)
    *   (JoinPoint joinPoint) import org.aspectj.lang.JoinPoint;
    *
    *  2. 返回结果 - @AfterReturning
    *     (Object result) result 接受返回的结果
    *       @AfterReturning(value = "execution(* com..impl.*.*(..))",returning = "result")
    *
    * 3.  异常信息 - @AfterThrowing
    *   (Throwing t) t接收异常信息
    *   @AfterThrowing(value = "execution(* com..impl.*.*(..))",throwing = "result")
    * */

    @Before("execution(* com..impl.*.*(..))")
    public void start(JoinPoint joinPoint){
        // 1. 获取方法属于类的信息
        String simpleName = joinPoint.getTarget().getClass().getSimpleName();
        // 2. 获取方法名称
        int modifiers = joinPoint.getSignature().getModifiers();
        String s = Modifier.toString(modifiers);
        String name = joinPoint.getSignature().getName();//获取方法名

        // 3. 获取参数列表
        Object[] args = joinPoint.getArgs();
    }

    @AfterReturning(value = "execution(* com..impl.*.*(..))",returning = "result")
    public void afterReturn(JoinPoint joinPoint,Object result){

    }

    @After("execution(* com..impl.*.*(..))")
    public void after(){

    }

    @AfterThrowing(value = "execution(* com..impl.*.*(..))",throwing = "result")
    public void afterThrowing(Throwable result){

    }
}

3.4 切点表达式语法

切点表达式作用
AOP切点表达式(Pointcut Expression)是一种用于指定切点的语言,它可以通过定义匹配规则,来选择需要被切入的目标对象。
1
切点表达式语法
1
语法细节:
1

  • 实战:
1.查询某包某类下,访问修饰符是公有,返回值是int的全部方法
     execution(public int com.doug.xClass.*(..))
2.查询某包下类中第一个参数是String的方法
      execution(* com.doug.xClass.*(String..))
3.查询全部包下,无参数的方法!
      execution(* *..*.*())
4.查询com包下,以int参数类型结尾的方法
     execution(* com..*.*(..int))
5.查询指定包下,Service开头类的私有返回值int的无参数方法
     execution(private int com.doug.Service*.*())

3.5 重用(提取)切点表达式

1
提取重复的切点表达式
1
切点统一管理:
将切点表达式统一存储到一个类中进行集中管理和维护!

3.6 环绕通知

三合一(四合一)
1

3.7 切面优先级设置

相同目标方法上同时存在多个切面时,切面的优先级控制切面的内外嵌套顺序。

  • 优先级高的切面:外面
  • 优先级低的切面:里面

使用 @Order 注解可以控制切面的优先级:

  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低
    1
    实际意义:
    实际开发时,如果有多个切面嵌套的情况,要慎重考虑。例如:如果事务切面优先级高,那么在缓存中命中数据的情况下,事务切面的操作都浪费了。
    1
    此时应该将缓存切面的优先级提高,在事务操作之前先检查缓存中是否存在目标数据。
    1

3.8 CGLib动态代理生效

在目标类没有实现任何接口的情况下,Spring会自动使用cglib技术实现代理。

总结:
a. 如果目标类有接口,选择使用jdk动态代理

b. 如果目标类没有接口,选择cglib动态代理

c. 如果有接口,接口接值

d. 如果没有接口,类进行接值

3.9 注解实现小结

1

四、Spring AOP对获取Bean的影响理解

对实现了接口的类应用切面
在这里插入图片描述
对没实现接口的类应用切面new
1
如果使用AOP技术,目标类有接口,必须使用接口类型接收IoC容器中代理组件!


总结

代理介绍:

1

AOP名词理解:

1

AOP思维以及与代理的关系:

1

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

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

相关文章

R语言入门笔记2.6

描述统计 分类数据与顺序数据的图表展示 为了下面代码便于看出颜色参数所对应的值&#xff0c;在这里先集中介绍&#xff0c; col1是黑色&#xff0c;2是粉红&#xff0c;3是绿色&#xff0c;4是天蓝&#xff0c;5是浅蓝&#xff0c;6是紫红&#xff0c;7是黄色&#xff0c;…

前沿科技速递——YOLOv9

随着YOLO系列的不断迭代更新&#xff0c;前几天&#xff0c;YOLO系列也迎来了第九个大型号的更新&#xff01;YOLOv9正式推出了&#xff01;附上原论文链接。 arxiv.org/pdf/2402.13616.pdf 同样是使用MS COCO数据集进行对比比较&#xff0c;通过折线图可看出AP曲线在全方面都…

一、系统架构师考试介绍

一、系统架构设计师介绍 系统架构设计师在软考体系中&#xff0c;属于高级资格。(不需要先考中级可以直接报考高级&#xff0c;我之前不知道还考了软件设计师T.T不如当初直接考系统架构师) 考试时间: 每年11月份的第二个周六 报名方式: 网上报名 报名网址 http://wwwruankao.…

C++常见问题

C常见问题 引用模板STLvector原理移动语义与右值引用New delete与malloc freeinlineconststaticexplicit 的作用lambda 表达式友元public、protected、private的区别封装继承多态虚函数重载、重写、隐藏的区别智能指针C 11新特性深拷贝与浅拷贝虚拟内存内存对齐及内存泄漏C内存…

解决ubuntu系统cannot find -lc++abi: No such file or directory

随着CentOS的没落&#xff0c;使用ubuntu的越来越多&#xff0c;而且国外貌似也比较流行使用ubuntu&#xff0c;像LLVM/Clang就有专门针对ubuntu编译二进制发布文件&#xff1a; ubuntu本身也可以直接通过apt install命令来安装编译好的clang编译器。不过目前22.04版本下最高…

【尚硅谷】MybatisPlus 学习笔记(下)

目录 六、插件 6.1、分页插件 6.1.1、添加配置类 6.1.2、测试 6.2、xml自定义分页 6.2.1、UserMapper中定义接口方法 6.2.2、UserMapper.xml中编写SQL 6.2.3、测试 6.3、乐观锁 6.3.1、场景 6.3.2、乐观锁与悲观锁 6.3.3、模拟修改冲突 数据库中增加商品表 添加数…

防御保护---防火墙的病毒防御

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 一.防病毒网关&#xff08;AV&#xff09;概述 防火墙的防病毒网关&#xff08;AV&#xff09;是一种网络安全设备&#xff0c;用于检测和阻止恶意软件&#xff08;如病毒、蠕虫、木马等&#x…

pclpy 半径滤波实现

pclpy 半径滤波实现 一、算法原理背景 二、代码1.pclpy 官方给与RadiusOutlierRemoval2.手写的半径滤波&#xff08;速度太慢了&#xff0c;用官方的吧&#xff09; 三、结果1.左边为原始点云&#xff0c;右边为半径滤波后点云 四、相关数据 一、算法原理 背景 RadiusOutlier…

Javaweb之SpringBootWeb案例之AOP案例的详细解析

4. AOP案例 SpringAOP的相关知识我们就已经全部学习完毕了。最后我们要通过一个案例来对AOP进行一个综合的应用。 4.1 需求 需求&#xff1a;将案例中增、删、改相关接口的操作日志记录到数据库表中 就是当访问部门管理和员工管理当中的增、删、改相关功能接口时&#xff0c…

08 Redis之集群的搭建和复制原理+哨兵机制+CAP定理+Raft算法

5 Redis 集群 2.8版本之前, Redis采用主从集群模式. 实现了数据备份和读写分离 2.8版本之后, Redis采用Sentinel哨兵集群模式 , 实现了集群的高可用 5.1 主从集群搭建 首先, 基本所有系统 , “读” 的压力都大于 “写” 的压力 Redis 的主从集群是一个“一主多从”的读写分…

java+springmvc+springboot众筹救助系统mybatis

儿童众筹救助系统在流畅性&#xff0c;续航能力&#xff0c;等方方面面都有着很大的优势。这就意味着儿童众筹救助系统的设计可以比其他系统更为出色的能力&#xff0c;可以更高效的完成最新的救助基金、救助申请、众筹项目、捐赠信息等功能。 此系统设计主要采用的是JAVA语言来…

zemax凯尔纳目镜

也叫做凯涅尔目镜 凯尔纳目镜 是由单片透镜和双胶合透镜组成的。 一种改进型的冉斯登目镜&#xff0c;二片组成的接目镜及双凸透镜作为场镜。 它能校正倍率色差 &#xff0c;同时也减小了位置色差 、像散和畸变。 视场角大于40&#xff0c;可达50。 目前在一些中低倍望远镜中…

vscode怎么运行C++的cpp文件

非常简单&#xff0c;点击vscode导航栏的Extensions, 搜索C/C Compile Run安装上即可&#xff0c; 写好 C/C 程序之后&#xff0c;直接使用 F6 一键编译运行就能出结果。

TensorRT及CUDA自学笔记003 CUDA编程模型、CUDA线程模型及其管理、CUDA内存模型及其管理

TensorRT及CUDA自学笔记003 CUDA编程模型、CUDA线程模型及其管理、CUDA内存模型及其管理 各位大佬&#xff0c;这是我的自学笔记&#xff0c;如有错误请指正&#xff0c;也欢迎在评论区学习交流&#xff0c;谢谢&#xff01; CUDA编程模型 我们使用CUDA_C语言进行CUDA编程&am…

成功解决No module named ‘skimage‘(ModuleNotFoundError)

成功解决No module named ‘skimage’(ModuleNotFoundError) &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程 &#x1f448; 希望得到您…

Day09-面向对象-多态

文章目录 Day09-面向对象-多态学习目标1对象数组1.1 对象数组的声明和使用1.2 对象数组的内存图分析 2. 多态2.1 前提2.2 多态的意义2.3 多态的特点2.3.1 成员方法的特点2.3.2 成员变量的特点 2.4 引用数据类型转换2.4.1 类型转换的意义2.4.2 转型的异常 3. final关键字的使用3…

PMP认证有什么用?含金量高吗?如何备考?

PMP备考多久能参加PMP考试&#xff0c;培训机构是关键点 依我这几年的持证体验来看&#xff0c;PMP认证的用处还是比较多的&#xff0c;也有一定的含金量&#xff0c;这两个方面基本都是随便一百度就能得到结果的&#xff0c;在考PMP的人群中唯一不同的可能就是备考方面的问题…

谷歌发布开源大模型 Gemma,评测+最佳微调实践来啦!

Gemma 是由 Google 推出的一系列轻量级、先进的开源模型&#xff0c;他们是基于 Google Gemini 模型的研究和技术而构建。它们是一系列text generation&#xff0c;decoder-only的大型语言模型&#xff0c;对英文的支持较好&#xff0c;具有模型权重开源、并提供预训练版本&…

探索 LRU 算法的缺陷与解决方案

LRU算法 Linux 的 Page Cache 和 MySQL 的 Buffer Pool 的大小是有限的&#xff0c;并不能无限的缓存数据&#xff0c;对于一些频繁访问的数据我们希望可以一直留在内存中&#xff0c;而一些很少访问的数据希望可以在某些时机可以淘汰掉&#xff0c;从而保证内存不会因为满了而…

【新手易错点】golang中byte和rune

1 总体区别 在Golang中&#xff0c;byte和rune是两种不同类型的数据。简单来说&#xff0c;byte是一个8位的无符号整数类型&#xff0c;而rune则是一个32位的Unicode字符类型。 Byte: 在Golang中&#xff0c;byte类型实际上是uint8的别名&#xff0c;它用来表示8位的无符号整…