Spring AOP超详细解析

article/2025/9/14 9:34:07

AOP - 面向切面编程(Aspect Oriented Programming)

Spring早期版本的核心功能:管理对象生命周期与对象分配。

  • 即Bean本身的管理创建,以及它整个生命周期里跟其他对象相互之间引用装配

为了更好的实现管理和装配,一个自然而然的想法就是,加一个中间层代理(字节码增强)来实现所有对象的托管。

  • 为了更好的实现Bean的管理装配,特别是需要对他做一些增强。所以在可以不改变原来这些Bean本身的定义功能的前提下,进一步把需要增强的的部分、需要额外处理的部分。通过这个中间层来解决。做增强的这个中间层这个面就是所谓的切面

补充:

  • 计算机领域有一句至理名言:计算机领域的任何问题都可以通过增加一个中间层来解决。

IoC - 控制反转(Inversion Of Control)

也成为DI(Dependency Injection)依赖注入。对象装配思路的改进。

从对象A直接引用和操作对象B,变成对象A里只需要依赖一个接口IB,系统启动和装配阶段,把IB接口的实例对象注入到对象A,这样A就不需要依赖一个IB接口的具体实现,也就是类B。

从而可以实现在不修改代码的情况,修改配置文件,即可以运行时替换成注入IB接口另一实现类C的一个对象实例。

补充:

  • 为什么叫DI:
    • 正常情况下写一个接口,在一个包或者某一个类里需要用到它具体的一个实现类,写在另外一个包里。
    • 在常规编程中,我们需要在定义这个接口使用的地方,直接new出来这个接口的实现类,这样的话,上层就需要依赖下层的某个东西。
    • 实际上我们更期望这个灵活的编程模型能够做到:在上层使用的时候定义一个接口,我们现在不管他实现类是什么,在运行期只要有一个实现类塞进来,就可以装配进去,放在这里使用。
    • 相当于装修房子:先在墙上留出来一个位置,准备放一幅画。只要把这个位置大小留好了,那幅画最终入住的时候放进来,是油画、水彩、素描都可以。只要我们留出来这个位置是合适的。那么在前期装修房子的时候,先就不用特别关注于这一小块,到底以后用什么类型的画。
    • 这么做还有一个好处:就是当装修完房子以后,发现我现在放的这幅油画我不喜欢了,我就随时可以使用一个水彩画替换掉这个油画。
    • 也就是说当我们实现依赖注入(控制反转)的方式装配对象,那么在运行期可以不去改我们的代码,改一下装配、配置这块的一些配置(配置文件、注解等),就可以把我们在运行期实际上这一块这个位置上使用的东西直接给替换掉。

循环依赖

  • 场景一:比如说一个对象(A),里面需要用某个接口,这个接口它实际的实现类需要用到(B)的实例,(B)里面某个属性需要用一个接口,那个接口他的具体实现类是(A)。

解决:

Spring Bean:类A和类B,他们俩都各自先独立被创建出来的。再在后面的过程中,对它内部的这些属性进行装配。这个时候这两个对象的引用本身是有了,所以就可以彼此的给对方赋值了。也别是显式的拿到一个Bean,再把他通过代码显式的方式注入到其他的一些使用的对象里面去的时候,这个问题就更不是问题了。

AOP:假如说在里面做了AOP,这时候拿到的是一个代理类,或者增强的一个运行期生成的子类,中间做一层代理,那么更可以解决所谓的循环依赖的问题。

什么类型的循环依赖Spring无法处理?除了Spring,循环依赖还有哪些类似场景?

  • 场景二:A对象在构造函数里需要传进来:一个B对象才能初始化(或者一个接口,接口实例是B,才能初始化),反过来B也需要再new出来它的实例,构造函数里面,参数要传一个A才能初始化。这个时候Spring就没办法解决了。常规的编写Java代码也解决不了。
    • 这两个东西相当于死锁了,每个对象自己创建一个实例的时候,都需要另外的那个对象,另一个对象也没有,有需要这个对象。这样来回调就会导致堆栈溢出了。
  • 所以常规说,属性之间的这种循环依赖,Spring是可以解决的。构造器里产生的循环依赖Spring是解决不了的。
  • 循环依赖本质原因是会导致死锁(频繁调用或来来回回的赋值都会导致死锁),所以要尽量避免。

AOP代理方式

在Spring里可以把一个类型注册成Spring里的一个Bean,这时候Spring就会帮我们把这个Bean初始化,变成一个可用的对象。加入我们需要在上面做一些增强,就是我们所谓的AOP。这时候我们就需要在中间加一层代理类或者增强类。

  • JdkProxy:假如说这个对象所在的类上面有接口(基于接口来做的),Spring会默认使用JdkProxyJDK的动态代理),来生成一个代理,在代理里进一步的把所有对这个类做的增强操作,放到代理执行的代码里面。然后先做了增强的操作,再去调用原本的类的他的方法
  • proxyTargetClass:如果要代理的类有接口但想强制不用默认JDK的动态代理,也是用字节码增强的技术,就可以开启proxyTargetClass选项。同CGlib。

  • CGlib:假如说要增强或代理的这个类没有接口,只有一个类的定义,Spring会默认使用CGlib,对他做字节码增强。相当于硬生生的给他生成一个子类。在这个子类里,当我们调用原先这个类的某个方法时,先做增强操作,再去调原本类的方法,最后再把结果返回回来

总之,Spring AOP面向切面的增强功能,都是作用在方法上的!

小插曲:两种常用的注入方法:

  1. @Autowired(required = true):默认是按类型注入。required=true表示启动的时候就找该对象(就是把他先配置好)还是当我们去调用该对象(调用某个属性或者某个方法)时,再看他要不要装配(懒加载)。
  2. @Resource(name = "student"):默认按名字注入

配置AOP(常用两种方式):

  • 代码+XML:around、before、after

  • 全注解:

同时配置文件开启自动代理:

AOP在工作中的作用:

  • 在调用service具体一些业务方法的时候,想在前面打一些日志。
  • 通过前后两次取时间戳来减一下,来统计所有业务方法执行的时间。
  • 在调用某一类业务方法时,判断用户有没有权限。
  • 在一系列业务方法前后加上事务的控制。
    • 比如startTransaction、commitTransaction(模拟事务控制)。

等等当我们需要对已有的这些业务,写好的一些方法进行一些额外的处理、控制的时候,我们就需要AOP

AOP可以再不改变原有代码的基础上,给我们做这些基于方法的额外的增强

类似CGlib做字节码增强的工具有哪些?

怎么理解字节码增强操作?

  • 在面向对象里,先定义一个对象,然后显式调用对象的方法,或者基于接口调用具体某个对象的方法,这时所有操作都是显式的。写代码的时候就必须知道这个方法有,而且能够调用这个方法,调的到他。
  • 反射:很多时候我们要操作一批对象,这批对象给过来的时候,我们都不知道他具体的类型(比如传参都是Object),但是我们约定好了,不管是什么类型,他都有一个ding()方法,这个方法参数是空的,这个时候就可以通过反射的方式,用这些对象.getClass().getMethod("ding")获取到具体的ding方法。然后再通过反射:method.invoke()来执行ding方法,这样一来反射相当于在某种程度上破坏了单纯的面向对象这个良好的封装。

// 创建对象

Object instance=clazz.getDeclaredConstructor().newInstance();

// 执行方法

Method method=clazz.getMethod(methodName);

method.invoke(instance);

  • 面向对象在我们看来,就想医生给病人做检查的时候,通过外部来看一下(起色,体温等)。反射相当于拍CT、拍X光片,胃镜等深入到内部去吧内部的一些信息通过反射的东西窥探出来。
  • 不管是面向对象本身的显式调用还是反射这种隐式调用的方式,都是在对象现有的结构上做的,但是字节码操作不同。他是在运行期,通过在内存里拼一个新的类型,这种类型需要的字节码就相当于动态的创建出来了一个新的类
  • 还是拿看病举例:字节码技术相当于创造了新的基因,创造了新的物种。凭空创造出了一个新的个体出来了。

字节码增强新工具:ByteBuddy

提供了更友好的API,比较常用,做好了很方便的函数,方便使用。(机器语言->汇编语言->高级语言),比上面那些机器语言更高的汇编语言,写起来更方便。

Instrumentation

  • 既然字节码技术能够创建出来一些新的类型,继承原有老的类型。即在运行期动态创建一些子类型。那有没有一种技术可以直接修改jar包里的一些字节码?--->
  • Jdk的新技术Instrumentation。作用在JVM加载一个jar包,把jar包完整的加载到内存之前的一个预处理的步骤上,通过Java Agent技术,就可以再中间拦一层,跟AOP类似,但他是AOP在整个JVM上。就可以把Jar包在往JVM加载的时候中间在agent层做一次预处理,就可以把原先Jar包里的一些字节码直接给替换掉
  • 这个技术就相当于在字节码技术创建新物种的程度上更深入了一层,可以直接替换现有的一些生物个体他们内部的基因片段。
  • 常见的应用场景:
    • APM
    • 应用性能监控、全链路监控

http://chatgpt.dhexx.cn/article/NFtx63wS.shtml

相关文章

Spring学习:AOP概述

一、AOP概念 AOP是指面向切面编程,利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 通俗描述:不通过修改源代码方式,在主干…

SpringAOP学习--SpringAOP简介及原理

前文对AOP做了介绍,实际项目中,一般不会直接上手手动实现aop,而是使用一些高级封装的aop实现,如SpringAOP。 Spring是一个广泛应用的框架,SpringAOP则是Spring提供的一个标准易用的aop框架,依托Spring的IOC…

图文详解Spring AOP,你学会了吗?

如果说 IOC 是 Spring 的核心,那么面向切面编程AOP就是 Spring 另外一个最为重要的核心,需要重点掌握mikechen 本篇主要会详解以下六点: 1.AOP的定义 2.AOP的作用 3.AOP的应用场景 4.Spring AOP的术语 AOP核心概念Spring AOP 通知分类S…

Spring AOP全面详解(超级详细)

如果说IOC 是 Spring 的核心,那么面向切面编程AOP就是 Spring 另外一个最为重要的核心mikechen AOP的定义 AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP&…

mysql执行SQL脚本

方法一 【Mysql的bin目录】\mysql –u用户名 –p密码 –D数据库<【sql脚本文件路径全名】 示例&#xff1a; 如果mysql配了全局变量&#xff0c;就不需要到Mysql的bin目录下执行&#xff0c;可以在任何地方使用用户名、密码、指定数据库等参数值与参数名不需要隔空格 不…

SpringBoot 实现SQL脚本自动执行

SpringBoot 实现配置SQL脚本自动执行 一. 背景 我们可能遇到过这种情况: 在公网开发时, 新增数据表非常容易, 直接登录到对应服务器的mysql / 使用Navicat访问mysql服务器. 然后去执行sql语句或脚本即可在内网开发时, 由于都在一个网段, 所以操作也比较方便但是在公网开发, 部…

flink-sql-client提交sql脚本文件

标题: flink-sql-client提交sql脚本文件 日期: 2021-10-22 22:11:34 标签: [flink,sql-client] 分类: flink 我们知道&#xff0c;sql-client.sh可以提供给我们一个sql交互界面&#xff0c;让我们没执行一个sql&#xff0c;就可以看到执行结果&#xff0c;也可以交互式查询表的…

如何在mysql中执行sql脚本文件

一、sql脚本文件 简介 xxxx.sql这种文件被称为sql脚本文件。sql脚本文件中编写了大量的sql语句。我们执行sql脚本文件的时候&#xff0c;该文件中所有的sql语句会全部执行&#xff01;批量的执行SQL语句&#xff0c;可以使用sql脚本文件。 上面这个vip.sql就是sql脚本文件&am…

使用sql脚本创建数据库表

准备脚本语句&#xff1a; CREATE TABLE test (title varchar(100) DEFAULT NULL,author varchar(10) DEFAULT NULL,digest varchar(255) DEFAULT NULL,content text,content_source_url varchar(500) DEFAULT NULL,thumb_media_id varchar(255) DEFAULT NULL,need_open_comme…

PowerDesigner生成Sql脚本

点击工具栏上的“Database”&#xff0c;选择“Change Current DBMS”进行修改导出脚本类型&#xff0c;可以选择mysql、sql server/ oracle 、db2等主流的数据库。 在DBMS中点击下拉菜单&#xff0c;选择要导出的数据库脚本&#xff0c;对名字进行自定义&#xff0c;点击确定即…

PowerDesigner导入sql脚本

一个好的数据库建模,不但可以让人直观的理解模型,充分的利用数据库技术,优化数据库的设计,而且还可以让新员工快速的熟悉数据库表结构与业务之间的关系.无奈的是随着开发过程中,数据库表结构字段的增删以及关联关系的变动给数据库模型带来维护上的巨大工作量.现为了维护上的简单…

dbeaver导入sql脚本

新建数据库 执行脚本 选择脚本文件 选择mysql 然后按确定就行了

springboot + mybatis启动时执行sql脚本

目录 1. 创建数据版本表&#xff0c;结构如下&#xff1a; 2. 创建HdVersion对象 3. 创建执行sql的dao 4. 创建dao对应的xml 5.创建sql执行器&#xff0c;实现ApplicationRunner 6. 结语 背景&#xff1a;项目开发或发布阶段修改表结构&#xff0c;项目更新时需要手动执行脚…

SpringBoot启动自动执行sql脚本

在开发当中我们每次发布服务都需要手动执行脚本&#xff0c;然后重启服务&#xff0c;而SpringBoot有服务启动自动执行sql脚本的功能的&#xff0c;可以为我们省去手动执行脚本的这一步&#xff0c;只需要部署新的服务即可。 这个功能是SpringBoot自带的不需要引入额外的依赖&a…

Excel数据转化为sql脚本

在实际项目开发中&#xff0c;有时会遇到客户让我们把大量Excel数据导入数据库的情况。这时我们就可以通过将Excel数据转化为sql脚本来批量导入数据库。 1 在数据前插入一列单元格&#xff0c;用来拼写sql语句。 具体写法&#xff1a;"insert into t_student (id,name,ag…

MySQL导出sql脚本文件

⭐️前言⭐️ sql脚本文件在我们做项目时&#xff0c;特别是学习别人的开源项目时经常需要进行导入导出操作&#xff0c;才能在自己的系统上跑起来&#xff0c;这篇文章主要介绍如何导出sql脚本文件&#xff0c;具体操作如下&#xff0c;附带截图详解。 &#x1f349;博客主页…

dataGrip导出sql脚本

1.打开dataGrip。 2.选择要导出的数据库表。 3.点击右键->选择"Dump Data to File(s)", 同时选择&#xff0c;Skip Computed Columns(sql),Add Table Definition(sql),Overwrite Exsting Files和Single File。 4.点击sql Inserts 5.选择文件保存位置 6.生成sql脚…

linux下plsql怎么执行sql脚本,plsql怎么执行sql脚本

首先,我们需要登录需要执行sql文件的用户,在我们确保sql文件无误的情况下,进入plsqldeveloper: 1,找到tools---》import tables ---》选择sql insert,不要选中sqlplus,选择最下面的那个导入sql文件,选中好sql文件后,点击import就会执行sql语句,生成日志。 2,如果执行…

DBeaver执行SQL脚本文件

1、右键库名&#xff0c;点击工具-->执行脚本 2、在弹出窗口中选择输入文件&#xff0c;并修改Extra command args:--default-character-setutf8&#xff0c;防止中文乱码&#xff0c;点击开始按钮。 3、执行完成。

kettle执行SQL脚本

参考一下kettle官方文档 kettle什么时候需要创建临时表呢 SELECT * WHERE cid IN(xxx) 的数据太多&#xff0c;占了很大内存。 目标表有没有必要做逻辑删除&#xff0c;如果做逻辑删除&#xff0c;后期数据量增长过快。 目标表增量更新&#xff1a;1、sql直接插入&#xff1…