当前位置: 首页 > news >正文

设计模式之适配模式是什么?以及在Spring AOP中的拦截器链的使用源码解析。

前言

  本文涉及到适配模式的基本用法,以及在Spring AOP中如何使用,首先需要了解适配模式的工作原理,然后结合Spring AOP的具体实现来详细详细解析源码。

  首先,适配模式,也就是Adapter Pattern,属于结构型设计模式,主要用于让不兼容的接口能够一起工作。要了解它的定义、结构、应用场景以及优缺点。然后,可能需要一个具体的例子来说明,比如电压适配器,这样用户更容易理解。

  接下来是Spring AOP中的拦截器链。如果对AOP的概念有一定了解,但想深入知道拦截器链是如何通过适配模式实现的。这里需要分析Spring AOP的源码,特别是MethodInterceptor接口和AdvisorAdapter的作用。需要提到具体的类,比如MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter和ThrowsAdviceAdapter,以及它们如何将不同类型的Advice适配成统一的MethodInterceptor。

  实际开发中遇到需要整合不同接口的情况,或者在使用Spring AOP时想自定义拦截器,了解底层实现有助于调试和扩展。

  总结来说,回答需要分为两部分:适配模式的讲解和Spring AOP拦截器链的源码分析。结合理论、示例和源码。


一、适配模式(Adapter Pattern)解析

适配模式是一种 结构型设计模式,用于将不兼容的接口转换为客户端期望的接口,使原本无法协同工作的类能够一起工作。其核心思想是 通过一个中间层(适配器)解决接口不匹配问题

1. 适配模式的结构
角色描述
目标接口客户端期望的接口(如 Target)。
被适配者需要被适配的现有类(如 Adaptee)。
适配器实现目标接口,并持有被适配者的引用,转换调用逻辑(如 Adapter)。
2. 适配模式示例

场景:将电压 220V 转换为 5V(USB 充电器即为适配器)。

// 目标接口:期望的5V电压
interface Voltage5V {int output5V();
}// 被适配者:现有的220V电源
class Voltage220V {public int output220V() {return 220;}
}// 适配器:将220V转换为5V
class VoltageAdapter implements Voltage5V {private Voltage220V voltage220V;public VoltageAdapter(Voltage220V voltage220V) {this.voltage220V = voltage220V;}@Overridepublic int output5V() {int src = voltage220V.output220V();return src / 44; // 转换为5V}
}// 客户端使用
public class Client {public static void main(String[] args) {Voltage5V adapter = new VoltageAdapter(new Voltage220V());System.out.println(adapter.output5V()); // 输出5}
}
3. 适配模式的类型
  • 类适配器:通过继承被适配者实现(需支持多继承,Java中不适用)。
  • 对象适配器:通过组合被适配者实现(推荐方式)。
4. 适配模式的优缺点
  • 优点
    • 解耦客户端与被适配者。
    • 符合开闭原则,无需修改现有代码。
  • 缺点
    • 过度使用可能导致系统复杂化。
5. 应用场景
  • 整合遗留代码或第三方库。
  • 统一多个类的接口(如日志框架适配)。

二、Spring AOP 拦截器链的源码解析

Spring AOP 的拦截器链(Interceptor Chain)基于 责任链模式,但其底层实现中大量使用了 适配模式,将不同类型的通知(Advice)适配为统一的 MethodInterceptor

1. Spring AOP 核心接口
接口作用
MethodInterceptor方法拦截器,在方法调用前后执行逻辑(AOP Alliance 标准接口)。
Advice通知的标记接口(如 BeforeAdviceAfterReturningAdvice)。
Advisor组合 Advice 和切点(Pointcut),确定在何处应用通知。
2. 适配模式在拦截器链中的应用

Spring 通过 适配器(AdvisorAdapter) 将不同类型的 Advice 转换为 MethodInterceptor,以便统一执行。

源码示例:AdvisorAdapter 实现类

Spring 内置三种适配器,对应不同类型的通知:

  1. MethodBeforeAdviceAdapter:将 MethodBeforeAdvice 适配为 MethodInterceptor
  2. AfterReturningAdviceAdapter:将 AfterReturningAdvice 适配为 MethodInterceptor
  3. ThrowsAdviceAdapter:将 ThrowsAdvice 适配为 MethodInterceptor

MethodBeforeAdviceAdapter 为例

class MethodBeforeAdviceAdapter implements AdvisorAdapter {@Overridepublic boolean supportsAdvice(Advice advice) {return (advice instanceof MethodBeforeAdvice);}@Overridepublic MethodInterceptor getInterceptor(Advisor advisor) {MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();return new MethodBeforeAdviceInterceptor(advice);}
}
拦截器链的构建流程
  1. 创建代理对象:通过 ProxyFactory 创建目标对象的代理。
  2. 获取所有 Advisor:根据切面配置,筛选匹配的 Advisor。
  3. 适配为拦截器:调用 AdvisorAdapterRegistry 将 Advisor 中的 Advice 转换为 MethodInterceptor
  4. 排序拦截器:按优先级排序拦截器,形成调用链。

源码入口DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice()

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(AdvisorChainFactory config, Method method, Class<?> targetClass) {List<Object> interceptorList = new ArrayList<>();for (Advisor advisor : config.getAdvisors()) {if (advisor instanceof PointcutAdvisor) {// 检查切点是否匹配方法if (((PointcutAdvisor) advisor).getPointcut().getMethodMatcher().matches(method, targetClass)) {// 通过适配器转换为 MethodInterceptorMethodInterceptor[] interceptors = config.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}}return interceptorList;
}
3. 拦截器链的执行流程

拦截器链的执行通过 ReflectiveMethodInvocation 实现,采用 递归调用模式

源码示例ReflectiveMethodInvocation.proceed()

public class ReflectiveMethodInvocation implements ProxyMethodInvocation {private final List<?> interceptorsAndDynamicMethodMatchers;private int currentInterceptorIndex = -1;public Object proceed() throws Throwable {// 所有拦截器执行完毕,调用原始方法if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}// 获取下一个拦截器并执行Object interceptor = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptor instanceof MethodInterceptor) {MethodInterceptor mi = (MethodInterceptor) interceptor;return mi.invoke(this);}// ...}
}
4. 示例:拦截器链的执行顺序

假设存在以下拦截器:

  1. Before Advice:方法执行前打印日志。
  2. Around Advice:计算方法执行时间。
  3. After Advice:方法执行后清理资源。

调用流程

┌→ BeforeAdviceInterceptor
│  ┌→ AroundAdviceInterceptor.proceed()
│  │  ┌→ 目标方法执行
│  │  └→ AroundAdviceInterceptor 计算耗时
│  └→ AfterAdviceInterceptor
└─ 返回结果

三、总结

设计模式在 Spring AOP 中的应用
适配模式将不同类型的 Advice 转换为统一的 MethodInterceptor
责任链模式拦截器链按顺序执行,每个拦截器决定是否传递调用。

源码核心类

  • AdvisorAdapter:适配 AdviceMethodInterceptor
  • ReflectiveMethodInvocation:递归执行拦截器链。
  • ProxyFactory:创建代理对象并管理拦截器。

通过适配模式,Spring AOP 实现了对多种通知类型的统一处理,而拦截器链机制则确保了切面逻辑的灵活组合与执行。这一设计充分体现了 高内聚、低耦合 的架构思想。

相关文章:

设计模式之适配模式是什么?以及在Spring AOP中的拦截器链的使用源码解析。

前言 本文涉及到适配模式的基本用法&#xff0c;以及在Spring AOP中如何使用&#xff0c;首先需要了解适配模式的工作原理&#xff0c;然后结合Spring AOP的具体实现来详细详细解析源码。 首先&#xff0c;适配模式&#xff0c;也就是Adapter Pattern&#xff0c;属于结构型设计…...

Python 库自制 Cross-correlation 算法

Python 库自制 Cross-correlation 算法 引言正文引言 虽然 Scipy 库中包含了成熟的 Cross-correlation 算法,但是有些时候我们无法使用现成的库进行数据处理。这里介绍如何使用 Python 基础函数自制 Cross-correlation 算法。后续读者可以将该算法转换为其他各类语言。 正文…...

C++(23):为类成员函数增加this参数

C23允许指定类成员函数的第一个参数的this类型&#xff0c;从而更加便于函数重载&#xff1a; #include <iostream> using namespace std;class A{ public:void func(this A&){cout<<"in func1"<<endl;}void func(this const A&){cout<…...

javaSE学习笔记23-线程(thread)-总结

创建线程的三种方式 练习代码 package com.kuang.thread;import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask;//回顾总结线程的创建 public class ThreadNew {public static void main(String[…...

【DeepSeek服务器部署全攻略】Linux服务器部署DeepSeek R1模型、实现API调用、搭建Web页面以及专属知识库

DeepSeek R1模型的Linux服务器搭建、API访问及Web页面搭建 1&#xff0c;引言2&#xff0c;安装Ollama工具3&#xff0c;下载DeepSeek R1 模型4&#xff0c;DeepSeek命令行对话5&#xff0c;DeepSeek API接口调用6&#xff0c;DeepSeek结合Web-ui实现图形化界面远程访问6.1&…...

【JAVA工程师从0开始学AI】,第四步:闭包与高阶函数——用Python的“魔法函数“重构Java思维

副标题&#xff1a;当严谨的Java遇上"七十二变"的Python函数式编程 历经变量战争、语法迷雾、函数对决&#xff0c;此刻我们将踏入Python最迷人的领域——函数式编程。当Java工程师还在用接口和匿名类实现回调时&#xff0c;Python的闭包已化身"智能机器人"…...

算法日记20:SC72最小生成树(prim朴素算法)

一、题目&#xff1a; 二、题解 2.1&#xff1a;朴素prim的步骤解析 O ( n 2 ) O(n^2) O(n2)(n<1e3) 0、假设&#xff0c;我们现在有这样一个有权图 1、我们随便找一个点&#xff0c;作为起点开始构建最小生成树(一般是1号)&#xff0c;并且存入intree[]状态数组中&#xf…...

玩转SpringCloud Stream

背景及痛点 现如今消息中间件(MQ)在互联网项目中被广泛的应用&#xff0c;特别是大数据行业应用的特别的多&#xff0c;现在市面上也流行这多个消息中间件框架&#xff0c;比如ActiveMQ、RabbitMQ、RocketMQ、Kafka等&#xff0c;这些消息中间件各有各的优劣&#xff0c;但是想…...

嵌入式经常用到串口,如何判断串口数据接收完成?

说起通信&#xff0c;首先想到的肯定是串口&#xff0c;日常中232和485的使用比比皆是&#xff0c;数据的发送、接收是串口通信最基础的内容。这篇文章主要讨论串口接收数据的断帧操作。 空闲中断断帧 一些mcu&#xff08;如&#xff1a;stm32f103&#xff09;在出厂时就已经在…...

iOS App的启动与优化

App的启动流程 App启动分为冷启动和热启动 冷启动&#xff1a;从0开始启动App热启动&#xff1a;App已经在内存中&#xff0c;但是后台还挂着&#xff0c;再次点击图标启动App。 一般对App启动的优化都是针对冷启动。 App冷启动可分为三个阶段&#xff1a; dyld&#xff1a…...

导出指定文件夹下的文件结构 工具模块-Python

python模块代码 import os import json import xml.etree.ElementTree as ET from typing import List, Optional, Dict, Union from pathlib import Path class DirectoryTreeExporter:def __init__(self,root_path: str,output_file: str,fmt: str txt,show_root: boo…...

Leetcode - 周赛436

目录 一、3446. 按对角线进行矩阵排序二、3447. 将元素分配给有约束条件的组三、3448. 统计可以被最后一个数位整除的子字符串数目四、3449. 最大化游戏分数的最小值 一、3446. 按对角线进行矩阵排序 题目链接 本题可以暴力枚举&#xff0c;在确定了每一个对角线的第一个元素…...

【pytest】编写自动化测试用例命名规范README

API_autoTest 项目介绍 1. pytest命名规范 测试文件&#xff1a; 文件名需要以 test_ 开头或者以 _test.py 结尾。例如&#xff0c;test_login.py、user_management_test.py 这样的命名方式&#xff0c;pytest 能够自动识别并将其作为测试文件来执行其中的测试用例。 测试类…...

Compose常用UI组件

Compose常用UI组件 概述Modifier 修饰符常用Modifier修饰符作用域限定Modifier Modifier 实现原理Modifier.Element链的构建链的解析 常用基础组件常用布局组件列表组件 概述 Compose 预置了很多基础组件&#xff0c;如 Button&#xff0c;TextField&#xff0c;TopAppBar等&a…...

斐波那契数列模型:在动态规划的丝绸之路上追寻斐波那契的足迹(上)

文章目录 引言递归与动态规划的对比递归解法的初探动态规划的优雅与高效自顶向下的记忆化搜索自底向上的迭代法 性能分析与比较小结 引言 斐波那契数列&#xff0c;这一数列如同一条无形的丝线&#xff0c;穿越千年时光&#xff0c;悄然延续其魅力。其定义简单而优美&#xff…...

Hackthebox- Season7- Titanic 简记 [Easy]

简记 ip重定向到 http://titanic.htb,先添加hosts 收集子域名 wfuzz -c -u http://titanic.htb/ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -H Host:FUZZ.titanic.htb --hl 9 ******************************************************** * Wfu…...

Sa-Token 根据官方文档简单实现登录认证的示例

Sa-Token 根据官方文档实现登录鉴权测试 功能实现步骤依赖配置文件启动类创建 controller启动项目测试不用密码登录查看cookie状态 密码登录查看cookie状态 修改token名称 Apipost 测试无 cookie 模式【使用 token】后端将 token 返回到前端修改代码&#xff1a;测试&#xff1…...

rustdesk编译修改名字

最近&#xff0c;我用Rust重写了一个2W行C代码的linux内核模块。在此记录一点经验。我此前没写过内核模块&#xff0c;认识比较疏浅&#xff0c;有错误欢迎指正。 为什么要重写&#xff1f; 这个模块2W行代码量看起来不多&#xff0c;却在线上时常故障&#xff0c;永远改不完。…...

BS5852英国家具防火安全条款主要包括哪几个方面呢?

什么是BS5852检测&#xff1f; BS5852是英国针对家用家具的强制性安全要求&#xff0c;主要测试家具在受到燃烧香烟和火柴等火源时的可燃性。这个标准通常分为四个部分进行测试&#xff0c;但实际应用中主要测试第一部分和第二部分&#xff0c;包括烟头测试和利用乙炔火焰模拟…...

【运维】源码编译安装cmake

背景&#xff1a; 已经在本地源码编译安装gcc/g&#xff0c;现在源码安装cmake 下载源码 下载地址&#xff1a;CMake - Upgrade Your Software Build System 安装步骤&#xff1a; ./bootstrap --prefix/usr/local/cmake make make install 错误处理 1、提示找不到libmpc.…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...