当前位置: 首页 > 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.…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

省略号和可变参数模板

本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...

6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础

第三周 Day 3 &#x1f3af; 今日目标 理解类&#xff08;class&#xff09;和对象&#xff08;object&#xff09;的关系学会定义类的属性、方法和构造函数&#xff08;init&#xff09;掌握对象的创建与使用初识封装、继承和多态的基本概念&#xff08;预告&#xff09; &a…...

倒装芯片凸点成型工艺

UBM&#xff08;Under Bump Metallization&#xff09;与Bump&#xff08;焊球&#xff09;形成工艺流程。我们可以将整张流程图分为三大阶段来理解&#xff1a; &#x1f527; 一、UBM&#xff08;Under Bump Metallization&#xff09;工艺流程&#xff08;黄色区域&#xff…...

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法

用神经网络读懂你的“心情”:揭秘情绪识别系统背后的AI魔法 大家好,我是Echo_Wish。最近刷短视频、看直播,有没有发现,越来越多的应用都开始“懂你”了——它们能感知你的情绪,推荐更合适的内容,甚至帮客服识别用户情绪,提升服务体验。这背后,神经网络在悄悄发力,撑起…...