Spring AOP(实现,动态原理)详解版

Spring AOP

  • 1.什么是AOP?
    • 1.1引入AOP依赖
    • 1.2编写AOP程序
  • 2.Spring AOP核⼼概念
    • 2.1 切点(Pointcut)
    • 2.2连接点(Join Point)
    • 2.3通知(Advice)
    • 2.4 切⾯(Aspect)
  • 3.通知类型
    • 3.1顺序
    • 3.2切⾯优先级 @Order
    • 3.3 ⾃定义注解 @MyAspect
  • 4. Spring AOP 原理
  • 5 动态代理怎么实现
    • 5.1 JDK 动态代理类实现步骤
      • 定义JDK动态代理类
      • InvocationHandler
      • Proxy
    • 5.2CGLIB 动态代理类实现步骤
  • 6.总结

1.什么是AOP?

AOP是⾯向切⾯编程切⾯。
什么是⾯向特定⽅法编程呢? ⽐如 登录校验拦截器, 就是对"登录校验"这类问题的统⼀处理. 所以, 拦截器也是AOP的⼀种应⽤. AOP是⼀种思想, 拦截器是AOP思想的⼀种实现. AOP是⼀种思想, 是对某⼀类事情的集中处理

1.1引入AOP依赖

在pom.xml⽂件中添加配置

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

1.2编写AOP程序

记录Controller中每个⽅法的执⾏时间

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
@Aspect
@Component
public class TimeAspect {
/**
* 记录⽅法耗时
*/
@Around("execution(* com.example.demo.controller.*.*(..))")
public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {
//记录⽅法执⾏开始时间
long begin = System.currentTimeMillis();
//执⾏原始⽅法
Object result = pjp.proceed();
//记录⽅法执⾏结束时间
long end = System.currentTimeMillis();
//记录⽅法执⾏耗时
log.info(pjp.getSignature() + "执⾏耗时: {}ms", end - begin);
return result;
}

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

2.Spring AOP核⼼概念

2.1 切点(Pointcut)

切点(Pointcut), 也称之为"切⼊点"
Pointcut 的作⽤就是提供⼀组规则, 告诉程序对哪些⽅法来进⾏功能增强.
在这里插入图片描述

2.2连接点(Join Point)

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

2.3通知(Advice)

通知就是具体要做的⼯作, 指哪些重复的逻辑,也就是共性功能(最终体现为⼀个⽅法)
⽐如上述程序中记录业务⽅法的耗时时间, 就是通知.

2.4 切⾯(Aspect)

切⾯(Aspect) = 切点(Pointcut) + 通知(Advice)
通过切⾯就能够描述当前AOP程序需要针对于哪些⽅法, 在什么时候执⾏什么样的操作.
在这里插入图片描述

3.通知类型

3.1顺序

Spring中AOP的通知类型有以下⼏种:
• @Around: 环绕通知, 此注解标注的通知⽅法在⽬标⽅法前, 后都被执⾏
• @Before: 前置通知, 此注解标注的通知⽅法在⽬标⽅法前被执⾏
• @After: 后置通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, ⽆论是否有异常都会执⾏
• @AfterReturning: 返回后通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, 有异常不会执⾏
• @AfterThrowing: 异常后通知, 此注解标注的通知⽅法发⽣异常后执⾏
在这里插入图片描述

程序正常运⾏的情况下, @AfterThrowing 标识的通知⽅法不会执⾏
@Around 标识的通知⽅法包含两部分, ⼀个"前置逻辑", ⼀个"后置逻辑".其中"前置逻辑" 会先于 @Before 标识的通知⽅法执⾏, “后置逻辑” 会晚于 @After 标识的通知⽅法执⾏
在这里插入图片描述

@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.2切⾯优先级 @Order

在这里插入图片描述
@Before 通知:数字越⼩先执⾏
@After 通知:数字越⼤先执⾏

@Order 控制切⾯的优先级, 先执⾏优先级较⾼的切⾯, 再执⾏优先级较低的切⾯, 最终执⾏⽬标⽅法.

3.3 ⾃定义注解 @MyAspect

在这里插入图片描述

import java.lang.annotation.ElementType;
2 import java.lang.annotation.Retention;
3 import java.lang.annotation.RetentionPolicy;
4 import java.lang.annotation.Target;
5
6 @Target(ElementType.METHOD)
7 @Retention(RetentionPolicy.RUNTIME)
8 public @interface MyAspect {
9 
10 }

1.@Target 标识了 Annotation 所修饰的对象范围, 即该注解可以⽤在什么地⽅.
常⽤取值:
ElementType.TYPE: ⽤于描述类、接⼝(包括注解类型) 或enum声明
ElementType.METHOD: 描述⽅法
ElementType.PARAMETER: 描述参数
ElementType.TYPE_USE: 可以标注任意类型
2. @Retention 指Annotation被保留的时间⻓短, 标明注解的⽣命周期

4. Spring AOP 原理

Spring AOP 是基于动态代理来实现AOP的
⽣活中的代理
• 艺⼈经纪⼈: ⼴告商找艺⼈拍⼴告, 需要经过经纪⼈,由经纪⼈来和艺⼈进⾏沟通.
• 房屋中介: 房屋进⾏租赁时, 卖⽅会把房屋授权给中介, 由中介来代理看房, 房屋咨询等服务.
实现
1.Subject: 业务接⼝类. 可以是抽象类或者接⼝(不⼀定有)
2. RealSubject: 业务实现类. 具体的业务执⾏, 也就是被代理对象.
3. Proxy: 代理类. RealSubject的代理.

5 动态代理怎么实现

1. JDK动态代理
2. CGLIB动态代理

5.1 JDK 动态代理类实现步骤

  1. 定义⼀个接⼝及其实现类(静态代理中的 HouseSubject 和 RealHouseSubject )
  2. ⾃定义 InvocationHandler 并重写 invoke ⽅法,在 invoke ⽅法中我们会调⽤⽬标⽅
    法(被代理类的⽅法)并⾃定义⼀些处理逻辑
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[]
    interfaces,InvocationHandler h) ⽅法创建代理对象

定义JDK动态代理类

实现 InvocationHandler 接⼝

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class JDKInvocationHandler implements InvocationHandler {
//⽬标对象即就是被代理对象
private Object target;
public JDKInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Thro
// 代理增强内容
System.out.println("我是中介, 开始代理");
//通过反射调⽤被代理类的⽅法
Object retVal = method.invoke(target, args);
//代理增强内容
System.out.println("我是中介, 代理结束");
return retVal;
}
}

InvocationHandler

InvocationHandler 接⼝是Java动态代理的关键接⼝之⼀, 它定义了⼀个单⼀⽅法 invoke() , ⽤于处理被代理对象的⽅法调⽤.

public interface InvocationHandler {
/**
* 参数说明
* proxy:代理对象
* method:代理对象需要实现的⽅法,即其中需要重写的⽅法
* args:method所对应⽅法的参数
*/

```bash
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

通过实现 InvocationHandler 接⼝, 可以对被代理对象的⽅法进⾏功能增强.

Proxy

Proxy 类中使⽤频率最⾼的⽅法是: newProxyInstance() , 这个⽅法主要⽤来⽣成⼀个代理对象

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,InvocationHandler h)
throws IllegalArgumentException
{
//...代码省略
}

5.2CGLIB 动态代理类实现步骤

  1. 定义⼀个类(被代理类)
  2. ⾃定义 MethodInterceptor 并重写 intercept ⽅法, intercept ⽤于增强⽬标⽅
    法,和 JDK 动态代理中的 invoke ⽅法类似
  3. 通过 Enhancer 类的 create()创建代理类

6.总结

  1. AOP是⼀种思想, 是对某⼀类事情的集中处理. Spring框架实现了AOP, 称之为SpringAOP
  2. Spring AOP常⻅实现⽅式有两种: 1. 基于注解@Aspect来实现 2. 基于⾃定义注解来实现, 还有⼀些更原始的⽅式,⽐如基于代理, 基于xml配置的⽅式, 但⽬标⽐较少⻅
  3. Spring AOP 是基于动态代理实现的, 有两种⽅式: 1. 基本JDK动态代理实现 2. 基于CGLIB动态代理实现. 运⾏时使⽤哪种⽅式与项⽬配置和代理的对象有关.
  4. 代理类只能用CGLIB,代理接口,CGLIB和JDK都可以。SpringBoot2.x以后默认使用CGLIB代理。用

spring.aop.proxy-target-class=false

来设置JDK代理。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BeagleBone Black入门总结

文章目录 参考连接重要路径系统镜像下载访问 BeagleBone 参考连接 镜像下载启动系统制作&#xff1a;SD卡烧录工具入门书籍推荐&#xff1a;BeagleBone cookbookBeagleBone概况&#xff1f; 重要路径 官方例程及脚本路径&#xff1a;/var/lib/cloud9BeagleBone Cookbook例程…

03-3.5.1~4 特殊矩阵的压缩存储

&#x1f44b; Hi, I’m Beast Cheng&#x1f440; I’m interested in photography, hiking, landscape…&#x1f331; I’m currently learning python, javascript, kotlin…&#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以订…

最新区块链论文速读--CCF A会议 CCS 2023 共25篇 附pdf下载(3/4)

Conference&#xff1a;ACM Conference on Computer and Communications Security (CCS) CCF level&#xff1a;CCF A Categories&#xff1a;network and information security Year&#xff1a;2023 Num&#xff1a;25 第1~7篇区块链文章请点击此处查看 第8~13篇区块链文…

【介绍下什么是Kubernetes编排系统】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

java线程池介绍

在Java中&#xff0c;线程池是用来管理和复用线程的一种机制&#xff0c;它可以显著提升程序性能&#xff0c;特别是在大量短期异步任务的场景下。以下是创建和使用线程池的基本步骤&#xff1a; 1.创建线程池: 使用java.util.concurrent.Executors类的静态工厂方法创建线程池&…

【c语言】自定义类型----结构体

结构体是c语言的一种自定义类型&#xff0c;自定义类型对于开发者及其重要的类型&#xff0c;它可以随意由开发者进行谱写功能&#xff0c;而今天的结构体可以用来表示一种变量的单个或多种具体属性&#xff0c;再编写代码时有着不可替代的作用&#xff01;&#xff01;&#x…

编译原理-词法分析(实验 C语言)

编译原理-词法分析 1. 实验目的 设计、编写并调试一个词法分析程序&#xff0c;加深对词法分析原理的理解 2. 实验要求 2.1 待分析的简单语言的词法 关键字&#xff1a;begin&#xff0c;if&#xff0c;then&#xff0c;while&#xff0c;do&#xff0c;end 所有关键字都是…

Nginx(openresty) 查看连接数和并发送

1 通过浏览器查看 #修改nginx配置文件 location /status {stub_status on;access_log off;allow 192.168.50.0/24;deny all;} #重新加载 sudo /usr/local/openresty/nginx/sbin/nginx -s reloadActive connections //当前 Nginx 当前处理的活动连接数。 server accepts handl…

HPC: perf入门

如果你想查看你的程序在cpu上运行时&#xff0c;耗时时如何分布的&#xff0c;那么perf是一个合理的选择。 准备工作 为了支持使用perf&#xff0c;首先你要安装相关的库 sudo apt install linux-tools-5.15.0-67-generic此外&#xff0c;因为使用perf进行benchmark&#xf…

四种跨域解决方案

文章目录 1.引出跨域1.基本介绍2.具体演示1.启动之前学习过的springboot-furn项目2.浏览器直接访问 [localhost:8081/furns](http://localhost:8081/furns) 可以显示信息3.启动前端项目&#xff0c;取消请求拦截器&#xff0c;这样设置&#xff0c;就会出现跨域4.跨域原因 2.跨…

linux指令--sed

sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。 语法解析 sed [选项] 编辑命令 文件 选项&#xff1a; -n&#xff1a;只显示匹配处理的行-e&#xff1a;执行多个编辑命令时-i&#xff1a;在原文件中进行修改&#xff0c;不输出到屏幕-…

零基础入门学用Arduino 第二部分(一)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…

系统思考—决策

为‮么什‬模型及‮式公‬通常比人‮决类‬策更有效&#xff1f;究‮是竟‬什么让‮些某‬模型和公‮表式‬现出色&#xff1f;事实上&#xff0c;我‮应们‬该探究的‮人是‬类在决策‮程过‬中的不足。关‮在键‬于人类‮策决‬中存在的“噪声”。尽‮模管‬型或公‮不式‬完…

【JavaScript】内置对象 - 字符串对象 ⑤ ( 判断对象中是否有某个属性 | 统计字符串中每个字符出现的次数 )

文章目录 一、判断对象中是否有某个属性1、获取对象属性2、判定对象是否有某个属性 二、统计字符串中每个字符出现的次数1、算法分析2、代码示例 String 字符串对象参考文档 : https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String 一、判…

【NI国产替代】电池模拟器,快速模拟 3C 产品电池的充放电功能

电池模拟器 快速模拟 3C 产品电池的充放电功能输出灵活可调节的电压/电流内置双向 DC-DC 降压变换器为 3C 产品提供漏电检测 电池模拟器系列包含单节双通道&#xff08;1S&#xff09;、双节双通道&#xff08;2S&#xff09;、三节单通道&#xff08;3S&#xff09;三种规格&…

贪心(不相交的开区间、区间选点、带前导的拼接最小数问题)

目录 1.简单贪心 2.区间贪心 不相交的开区间 1.如何删除&#xff1f; 2.如何比较大小 区间选点问题 3.拼接最小数 1.简单贪心 比如&#xff1a;给你一堆数&#xff0c;你来构成最大的几位数 2.区间贪心 不相交的开区间 思路&#xff1a; 首先&#xff0c;如果有两个…

LeetCode刷题之HOT100之颜色分类

下午好呀&#xff0c;大家&#xff01;昨天估计是喝了假酒&#xff0c;现在没有胃口&#xff0c;喝酒真的没有任何好处。以后尽量避免此活动。今天几乎没睡觉&#xff0c;准备做完这题回宿舍&#xff0c;把电脑也带回去。 1、题目描述 2、逻辑分析 对颜色排序&#xff0c;要求…

数字孪生技术体系和核心能力整理

最近对数字孪生技术进行了跟踪调研学习,整理形成了调研成果,供大家参考。通过学习,发现数字孪生技术的构建过程其实就是数字孪生体的构建与应用过程,数字孪生体的构建是一个体系化的系统工程,数字化转型的最终形态应该就是数实融合互动互联的终极状态。数实融合是每个行业…

自定义类型:结构体+结构体内存对齐+结构体实现位段

结构体内存对齐实现位段 一.结构体1.结构体的声明2.结构体变量成员访问操作符3.结构体传参4.匿名结构体5.结构的自引用 二.结构体内存对齐1.对齐规则2.为什么存在内存对齐&#xff1f;3.修改默认对齐数 三.结构体实现位段1.什么是位段2.位段的内存分配3.位段的跨平台问题4.位段…

flink读取hive写入http接口

目录 0、创建hive数据 1、pom.xml 2、flink代码 3、sink 4、提交任务jar 5、flink-conf.yaml 6、数据接收 flink-1.17.2jdk1.8hive-3.1.3hadoop3.3.6passwordhttp0、创建hive数据 /cluster/hive/bin/beeline !connect jdbc:hive2://ip:10000 create database demo; d…