【AOP】AOP-面向切面编程 (系统性知识体系全解)

文章目录

  • [AOP 面向切面编程 系统性知识体系全解](#AOP 面向切面编程 系统性知识体系全解)
    • [一、AOP 基础认知层](#一、AOP 基础认知层)
      • [1.1 核心定义](#1.1 核心定义)
      • [1.2 诞生背景与解决的核心痛点](#1.2 诞生背景与解决的核心痛点)
      • [1.3 核心价值](#1.3 核心价值)
    • [二、AOP 核心概念体系](#二、AOP 核心概念体系)
      • [2.1 核心结构术语](#2.1 核心结构术语)
      • [2.2 通知(Advice)的5种标准类型](#2.2 通知(Advice)的5种标准类型)
    • [三、AOP 底层实现机制](#三、AOP 底层实现机制)
      • [3.1 织入时机的三大分类](#3.1 织入时机的三大分类)
      • [3.2 静态AOP实现](#3.2 静态AOP实现)
      • [3.3 动态AOP实现](#3.3 动态AOP实现)
        • [3.3.1 JDK动态代理](#3.3.1 JDK动态代理)
        • [3.3.2 CGLIB动态代理](#3.3.2 CGLIB动态代理)
        • [3.3.3 JDK动态代理 vs CGLIB动态代理 核心对比](#3.3.3 JDK动态代理 vs CGLIB动态代理 核心对比)
    • 四、主流AOP框架实现详解
      • [4.1 AspectJ](#4.1 AspectJ)
      • [4.2 Spring AOP](#4.2 Spring AOP)
      • [4.3 Spring AOP vs AspectJ 核心对比](#4.3 Spring AOP vs AspectJ 核心对比)
    • [五、AOP 核心应用场景](#五、AOP 核心应用场景)
      • [5.1 通用技术场景](#5.1 通用技术场景)
      • [5.2 业务定制场景](#5.2 业务定制场景)
    • [六、AOP 进阶与高级特性](#六、AOP 进阶与高级特性)
      • [6.1 切点表达式高级用法](#6.1 切点表达式高级用法)
      • [6.2 切面优先级控制](#6.2 切面优先级控制)
      • [6.3 引入(Introduction)类型间声明](#6.3 引入(Introduction)类型间声明)
      • [6.4 高级织入能力](#6.4 高级织入能力)
    • [七、AOP 常见问题、避坑指南与最佳实践](#七、AOP 常见问题、避坑指南与最佳实践)
    • [八、AOP 与相关编程范式/技术的对比](#八、AOP 与相关编程范式/技术的对比)
      • [8.1 AOP vs OOP](#8.1 AOP vs OOP)
      • [8.2 AOP vs 装饰器模式](#8.2 AOP vs 装饰器模式)
      • [8.3 AOP vs 过滤器(Filter) vs 拦截器(Interceptor)](#8.3 AOP vs 过滤器(Filter) vs 拦截器(Interceptor))
      • [8.4 AOP vs 责任链模式](#8.4 AOP vs 责任链模式)

AOP 面向切面编程 系统性知识体系全解

本文从基础认知、核心概念、底层原理、主流实现、工程应用、进阶特性、避坑实践、范式对比8大维度,构建AOP完整的知识体系,覆盖从入门到精通的全链路内容。


一、AOP 基础认知层

1.1 核心定义

AOP(Aspect-Oriented Programming,面向切面编程),是一种横向抽象的编程范式 ,是OOP(面向对象编程)的重要补充,核心目标是将跨多个业务模块的横切关注点(与核心业务逻辑无关的通用行为)与业务主逻辑解耦,实现非侵入式的代码增强、统一管控与复用。

1.2 诞生背景与解决的核心痛点

OOP以纵向继承、封装为核心,解决了业务模块的划分与复用问题,但面对横切关注点时存在天然缺陷:

  • 代码冗余:通用逻辑(日志、权限、事务、监控)分散在大量业务方法中,重复编码
  • 耦合度高:通用逻辑与业务逻辑强绑定,修改通用逻辑需要改动所有业务代码
  • 维护性差:通用逻辑散落在各处,无法统一管控,排查问题成本极高
  • 侵入性强:业务代码中充斥大量非业务代码,破坏了业务逻辑的纯粹性

AOP通过横向抽取、统一织入的思想,完美解决了上述问题。

1.3 核心价值

  1. 解耦:横切逻辑与业务逻辑完全分离,互不干扰
  2. 复用:一套切面逻辑可应用于无数个业务节点,无需重复编码
  3. 无侵入:业务代码无需任何修改,即可获得增强能力
  4. 集中管控:所有横切逻辑统一维护,修改一处全量生效
  5. 高可维护性:降低代码复杂度,提升系统可测试性与可扩展性

二、AOP 核心概念体系

AOP的所有能力都基于以下核心术语构建,术语间的关联关系是理解AOP的核心。

2.1 核心结构术语

术语 核心定义 核心定位
切面(Aspect) 横切关注点的模块化封装,是切点+通知的组合体,比如日志切面、事务切面 AOP的核心载体,一个切面对应一类通用能力
连接点(JoinPoint) 程序执行过程中能够被拦截的节点,比如方法执行、构造器调用、字段访问、异常抛出等;Spring AOP仅支持方法执行类型的连接点 AOP的作用目标,是切面可以插入代码的位置
切点(Pointcut) 用于匹配连接点的规则表达式,定义了哪些连接点会被拦截、被增强 AOP的筛选规则,解决"在哪里增强"的问题
通知/增强(Advice) 在匹配的连接点上要执行的具体横切逻辑代码,比如日志打印、权限校验 AOP的执行逻辑,解决"什么时候增强、做什么"的问题
目标对象(Target) 被AOP增强的原始业务对象,也叫被代理对象 AOP的增强对象,是业务逻辑的载体
织入(Weaving) 将切面逻辑应用到目标对象,生成增强后的代理对象的过程 AOP的实现手段,是连接切面与目标对象的桥梁
引入(Introduction) 也叫类型间声明,为现有类动态添加新的方法/属性,无需修改原类代码 AOP的进阶能力,实现对类结构的动态扩展

2.2 通知(Advice)的5种标准类型

通知定义了切面逻辑的执行时机,是AOP最核心的执行单元,按执行顺序与场景分为5类:

  1. 前置通知(@Before):连接点(目标方法)执行之前执行,无法阻止目标方法执行(除非主动抛出异常),常用于参数校验、权限校验、日志记录。
  2. 后置返回通知(@AfterReturning) :目标方法正常执行完成、无异常抛出后执行,可获取目标方法的返回值,常用于正常流程的日志记录、返回值处理。
  3. 后置异常通知(@AfterThrowing) :目标方法抛出指定异常时执行,可获取异常对象,常用于异常监控、告警、统一异常处理。
  4. 后置最终通知(@After) :目标方法执行完成后,无论正常结束还是异常抛出,都会执行,类似Java的finally块,常用于资源释放。
  5. 环绕通知(@Around) :包围整个目标方法的执行,可在方法执行前后自定义逻辑,甚至可以决定是否执行目标方法、修改入参/返回值、处理异常,是功能最强大的通知类型,常用于事务管理、缓存、限流、性能监控。
通知标准执行顺序
  • 正常执行流程:环绕前置逻辑 → 前置通知 → 目标方法 → 后置返回通知 → 后置最终通知 → 环绕后置逻辑
  • 异常执行流程:环绕前置逻辑 → 前置通知 → 目标方法抛异常 → 后置异常通知 → 后置最终通知 → 环绕异常处理逻辑

三、AOP 底层实现机制

AOP的核心实现基于代理模式 ,根据织入时机的不同,分为静态AOP动态AOP两大实现体系。

3.1 织入时机的三大分类

织入是AOP的核心流程,决定了切面逻辑何时被嵌入到目标代码中,分为3个阶段:

织入时机 执行阶段 代表实现 核心特点
编译期织入(CTW) Java源码编译为Class字节码时 AspectJ ajc编译器 织入彻底,运行期无开销,需特殊编译器,灵活性低
类加载期织入(LTW) JVM加载Class文件时 AspectJ LTW、Spring LTW 无需修改源码/编译器,通过javaagent修改字节码,织入能力强
运行期织入(RTW) 程序运行过程中 Spring AOP(JDK/CGLIB动态代理) 无需修改编译/加载流程,完全动态,灵活性高,运行期有少量开销

3.2 静态AOP实现

静态AOP在程序运行前,就将切面逻辑织入到目标类的字节码中,生成的Class文件已经包含完整的增强逻辑,运行期直接执行。

  • 核心代表:AspectJ
  • 核心原理:通过专用的ajc编译器,在编译期/编译后(二进制织入)修改字节码,将切面代码直接插入到目标类的对应连接点位置
  • 优势:性能极高(运行期无反射/代理开销)、织入彻底、支持所有类型的连接点、无自调用问题
  • 劣势:依赖专用编译器,编译期确定增强逻辑,灵活性差,调试成本略高

3.3 动态AOP实现

动态AOP在程序运行期,动态生成代理对象,将切面逻辑嵌入到代理对象中,不修改原始目标类的字节码,是Java企业级开发中最主流的实现方式,核心分为两种代理机制。

3.3.1 JDK动态代理
  • 核心原理:基于Java原生反射机制实现,要求目标类必须实现至少一个接口 ;运行期动态生成一个实现了目标类所有接口的代理类,代理类通过InvocationHandlerinvoke方法,在调用目标方法前后执行增强逻辑。
  • 核心API:java.lang.reflect.Proxy(代理类生成)、java.lang.reflect.InvocationHandler(增强逻辑处理器)
  • 核心特点:
    1. 必须基于接口,只能代理接口中定义的方法
    2. JDK原生支持,无需第三方依赖
    3. 基于接口实现,无继承限制,可代理final类(只要实现了接口)
    4. 无法代理类的非接口方法、private方法、static方法
3.3.2 CGLIB动态代理

CGLIB(Code Generation Library),基于ASM字节码生成框架实现,是JDK动态代理的补充方案。

  • 核心原理:运行期动态生成目标类的子类,重写目标类的非final方法,在子类的重写方法中嵌入增强逻辑,通过继承实现代理。
  • 核心特点:
    1. 无需目标类实现接口,可代理普通类
    2. 基于继承实现,无法代理final类、final方法、private方法、static方法
    3. Spring 3.2+ 内置CGLIB,无需额外引入依赖
    4. Spring Boot 2.x+ 默认强制使用CGLIB代理,无需手动配置
3.3.3 JDK动态代理 vs CGLIB动态代理 核心对比
对比维度 JDK动态代理 CGLIB动态代理
底层依赖 JDK原生反射机制 ASM字节码生成框架
接口要求 目标类必须实现接口 无接口要求
实现方式 实现目标类的接口 继承目标类生成子类
限制场景 无法代理非接口方法 无法代理final类、final方法
性能表现 JDK 1.8+ 性能优于CGLIB 早期版本性能占优,高版本略逊于JDK代理
Spring默认规则 目标类实现接口时默认使用 目标类未实现接口时默认使用,Boot 2.x+ 全局默认

四、主流AOP框架实现详解

Java生态中最主流的AOP实现为Spring AOPAspectJ,二者定位不同,适用场景差异显著。

4.1 AspectJ

  • 产品定位:完整的、企业级AOP解决方案,是AOP规范的标准实现,功能覆盖AOP的全部能力
  • 核心特性:
    1. 支持全类型连接点:方法执行、构造器调用、字段访问/修改、静态方法执行、异常抛出、类初始化等
    2. 支持全阶段织入:编译期织入、编译后织入、类加载期织入
    3. 语法完善:支持复杂的切点表达式、切面作用域、类型间声明等高级特性
    4. 性能极致:静态织入无运行期开销,性能与原生代码几乎无差异
  • 适用场景:对性能要求极高、需要细粒度AOP控制、非Spring环境、需要解决代理自调用问题的场景

4.2 Spring AOP

  • 产品定位:Spring生态内置的轻量级AOP实现,与Spring IoC容器深度整合,专为Spring业务开发设计

  • 核心特性:

    1. 仅支持方法执行类型的连接点,不支持字段、构造器等其他连接点
    2. 基于运行期动态代理实现(JDK/CGLIB),无编译期依赖
    3. 兼容AspectJ的注解语法(@Aspect、@Pointcut等),但底层仍为Spring动态代理,并非AspectJ编译器
    4. 与Spring IoC无缝整合,仅支持Spring容器管理的Bean的增强
    5. 学习成本低,配置简单,开箱即用
  • 核心注解(@AspectJ风格):

    注解 作用
    @Aspect 标记一个类为切面类
    @Pointcut 定义切点表达式,实现切点复用
    @Before 定义前置通知
    @AfterReturning 定义后置返回通知
    @AfterThrowing 定义后置异常通知
    @After 定义后置最终通知
    @Around 定义环绕通知
    @DeclareParents 实现引入功能,为类动态添加接口实现
    @Order 定义切面的执行优先级
  • 适用场景:Spring/Spring Boot生态下的业务开发,解决通用横切关注点(事务、日志、权限、缓存等),是Java后端开发的首选AOP方案

4.3 Spring AOP vs AspectJ 核心对比

对比维度 Spring AOP AspectJ
定位 轻量级、Spring生态专属的AOP实现 完整的、全场景的AOP标准实现
底层实现 运行期动态代理(JDK/CGLIB) 静态字节码织入(编译期/类加载期)
连接点支持 仅支持方法执行 支持所有类型的连接点
织入时机 仅运行期织入 编译期、编译后、类加载期
性能 有动态代理与反射的少量开销 静态织入,性能接近原生代码
自调用问题 无法解决,需额外处理 原生支持,无自调用问题
学习成本 低,配置简单,与Spring无缝整合 略高,需学习专用语法与织入配置
适用范围 仅Spring容器管理的Bean 所有Java类,包括第三方库

五、AOP 核心应用场景

AOP的核心价值在于处理跨模块的横切关注点,分为通用技术场景业务定制场景两大类,覆盖Java后端开发的绝大多数通用需求。

5.1 通用技术场景

  1. 统一日志记录:接口入参出参、执行耗时、操作人、异常日志的统一打印,替代每个方法中的重复日志代码
  2. 声明式事务管理 :Spring核心功能@Transactional,底层基于AOP环绕通知实现,统一处理事务的开启、提交、回滚
  3. 统一权限校验:接口访问权限、操作权限的统一校验,替代每个接口中的重复权限判断代码
  4. 统一性能监控:方法执行耗时统计、接口吞吐量监控、慢接口告警,实现系统性能的统一管控
  5. 统一缓存管理 :Spring Cache的@Cacheable/@CachePut/@CacheEvict,基于AOP实现缓存的读写、更新、删除的统一处理
  6. 限流熔断降级:Sentinel、Resilience4j、Hystrix的注解式限流熔断,基于AOP实现接口的流量控制与容错处理
  7. 统一异常处理:特定模块/层级的异常捕获、封装、告警,与全局异常处理器形成互补
  8. 统一参数校验:自定义参数校验规则的统一执行,补充JSR-380标准校验的能力

5.2 业务定制场景

  1. 操作审计日志:记录用户的操作行为,包括操作人、操作时间、操作类型、修改前后的数据对比,满足合规审计要求
  2. 数据权限过滤:基于AOP动态为SQL添加数据权限条件,实现用户只能查看自身/所属部门数据的需求
  3. 注解式分布式锁:基于Redisson、Curator等框架,通过AOP实现注解式分布式锁的自动加锁、释放、超时处理
  4. 多数据源动态切换:基于AOP根据注解/业务场景,动态切换数据源,实现读写分离、分库分表的数据源路由
  5. 敏感数据脱敏:接口返回数据时,对手机号、身份证号、银行卡号等敏感字段进行统一脱敏处理
  6. 接口重复提交防护:基于AOP+Redis实现接口幂等性控制,防止用户重复提交表单
  7. 业务流程埋点:对核心业务流程的节点进行统一埋点,上报用户行为数据,用于数据分析与业务优化

六、AOP 进阶与高级特性

6.1 切点表达式高级用法

切点表达式是AOP的核心筛选规则,Spring AOP支持AspectJ的切点表达式语法,核心分为9类指示器,支持逻辑组合与复用。

  1. 核心执行指示器
    • execution:最常用的指示器,匹配方法执行的连接点,语法:execution(修饰符? 返回值类型 包名.类名.方法名(参数列表) 异常?)
    • 示例:execution(* com.xxx.service.*.*(..)) 匹配service包下所有类的所有方法
  2. 类型匹配指示器within,匹配指定类型内的所有方法,示例:within(com.xxx.service.*)
  3. 代理/目标对象匹配this(匹配代理对象类型)、target(匹配目标对象类型)
  4. 参数匹配指示器args,匹配方法参数类型符合要求的连接点
  5. 注解匹配指示器 (业务开发最常用):
    • @annotation:匹配方法上有指定注解的连接点,示例:@annotation(org.springframework.transaction.annotation.Transactional)
    • @within:匹配类上有指定注解的所有方法
    • @target:匹配目标对象上有指定注解的所有方法
    • @args:匹配方法参数上有指定注解的连接点
  6. 组合与复用 :通过&&||!实现多规则逻辑组合;通过@Pointcut定义命名切点,实现表达式的复用。

6.2 切面优先级控制

多个切面作用于同一个连接点时,可通过优先级控制执行顺序,规则为:优先级值越小,切面优先级越高;高优先级切面的前置通知先执行,后置通知后执行(先进后出的栈结构)

  • 实现方式1:切面类实现org.springframework.core.Ordered接口,重写getOrder()方法返回优先级值
  • 实现方式2:切面类添加@Order注解,示例:@Order(1)
  • 注意:同一个切面内的多个通知,执行顺序由Spring固定,无法通过Order修改。

6.3 引入(Introduction)类型间声明

引入是AOP的进阶能力,可为目标类动态添加新的接口和实现,无需修改原类的任何代码。

  • Spring AOP实现方式:通过@DeclareParents注解实现

  • 语法示例:

    java 复制代码
    // 为所有Service接口的实现类,动态添加LogService接口的实现
    @DeclareParents(value = "com.xxx.service.*+", defaultImpl = DefaultLogService.class)
    public static LogService logService;
  • 适用场景:为一批类统一添加通用能力接口,比如日志、校验、数据脱敏等。

6.4 高级织入能力

  1. 类加载期织入(LTW) :通过JVM的javaagent机制,在类加载到JVM时,通过字节码转换器织入切面逻辑;Spring提供了原生的LTW支持,无需修改编译器,即可实现比动态代理更彻底的织入,解决部分自调用问题。
  2. 二进制织入:AspectJ支持对已编译好的Class文件、Jar包进行织入,可对第三方依赖库的类进行AOP增强,无需修改第三方源码。

七、AOP 常见问题、避坑指南与最佳实践

7.1 高频问题与避坑方案

问题1:代理自调用问题(最常见)
  • 现象:同一个类内部,方法A调用方法B,方法B上的AOP切面不生效
  • 根因:AOP的增强逻辑仅在代理对象上生效,内部调用是this.方法B()this是原始目标对象,而非代理对象,因此无法触发切面
  • 解决方案(按推荐优先级排序):
    1. 【最优】将被调用的方法B拆分到独立的Spring Bean中,彻底避免自调用
    2. 【兼容方案】开启代理暴露,通过AopContext.currentProxy()获取当前代理对象,再调用方法B,需在启动类添加@EnableAspectJAutoProxy(exposeProxy = true)
    3. 【终极方案】使用AspectJ静态织入,直接修改目标类字节码,无代理对象,彻底解决自调用问题
    4. 【不推荐】从Spring容器中手动获取当前Bean的代理对象,耦合Spring容器API
问题2:切面不生效的通用排查思路
  1. 检查切面类是否添加了@Aspect注解,且被Spring扫描到(添加了@Component等注解,在启动类扫描包范围内)
  2. 检查目标对象是否是Spring容器管理的Bean,非Spring管理的对象无法被Spring AOP增强
  3. 检查切点表达式是否正确,是否能匹配到目标连接点
  4. 检查被增强的方法是否是privatefinalstatic类型,此类方法无法被动态代理增强
  5. 检查是否存在自调用问题
  6. Spring Boot环境下,检查是否开启了@EnableAspectJAutoProxy注解(Spring Boot 2.x+ 默认开启)
问题3:@Transactional事务注解不生效

本质是AOP切面不生效,除上述排查点外,额外注意:

  1. 事务注解只能作用于public方法,非public方法的事务注解会被Spring忽略
  2. 异常被try-catch捕获且未重新抛出,事务无法感知异常,不会回滚
  3. 抛出的异常不是RuntimeException/Error,Spring默认只对非受检异常回滚,需通过rollbackFor指定受检异常
  4. 事务方法的自调用,导致切面不生效
问题4:环绕通知常见坑
  1. 未调用ProceedingJoinPoint.proceed()方法,导致目标方法无法执行
  2. 未返回proceed()方法的执行结果,导致目标方法的返回值丢失
  3. proceed()方法传入的参数与目标方法的入参不匹配,导致参数传递异常
  4. 未处理proceed()方法抛出的异常,导致异常被吞掉或向上传递异常
问题5:通知执行顺序不符合预期
  1. @After(后置最终通知)会在@AfterReturning/@AfterThrowing之前执行,这是Spring的固定设计,@After对应finally块,无论是否异常都会先执行
  2. 多个切面未指定@Order/Ordered接口,导致执行顺序随机,不可控

7.2 工程最佳实践

  1. 切面职责单一:一个切面仅处理一类横切关注点,比如日志切面只处理日志,权限切面只处理权限,避免切面逻辑臃肿
  2. 最小粒度切点 :尽量使用精准的切点表达式,优先使用@annotation匹配注解,避免使用过于宽泛的全量匹配,减少不必要的性能开销与意外增强
  3. 优先使用轻量通知:非必要不使用环绕通知,优先使用前置、后置通知,降低代码出错概率
  4. 切面无业务侵入:切面中仅编写横切通用逻辑,禁止编写业务代码,保持切面的纯粹性
  5. 异常处理规范:切面中禁止无差别吞掉异常,除非明确业务需求,否则必须将异常向上抛出,避免掩盖业务异常
  6. 明确优先级 :为所有切面指定明确的@Order优先级,避免执行顺序混乱导致的业务问题
  7. 可测试性:切面逻辑必须可单元测试,避免编写强依赖Spring容器的切面代码
  8. 性能优先:性能敏感场景优先使用AspectJ静态织入,而非Spring动态代理;切面中避免编写耗时逻辑,尤其是环绕通知中

八、AOP 与相关编程范式/技术的对比

8.1 AOP vs OOP

  • OOP:核心是纵向抽象,通过对象、封装、继承、多态,解决业务模块的划分与核心业务逻辑的复用问题,是系统架构的主体
  • AOP:核心是横向抽象,通过切面、织入,解决跨模块的通用横切逻辑的复用与解耦问题,是OOP的补充,而非替代
  • 关系:二者相辅相成,共同构建高内聚、低耦合的系统架构

8.2 AOP vs 装饰器模式

  • 装饰器模式:静态的手动增强,需要为每个目标类编写装饰器,实现相同的接口/继承相同的父类,编译期确定增强逻辑,适合单个对象的增强
  • AOP:动态的自动增强,无需修改业务代码,一套切面可批量增强无数个目标对象,灵活性极高,适合大规模的横切逻辑增强
  • 底层关联:AOP的动态代理,本质上是动态的、自动化的装饰器模式实现

8.3 AOP vs 过滤器(Filter) vs 拦截器(Interceptor)

技术 作用范围 底层实现 粒度 适用场景
Filter Servlet规范,Web请求入口,在Servlet之前执行 函数回调 粗粒度 Web请求的全局编码、跨域、全局限流等
Interceptor Spring MVC,仅作用于Controller层的方法 动态代理 中粒度 Controller层的登录校验、权限校验、日志记录等
AOP Spring全场景,可作用于任意Spring Bean的任意方法 动态代理/静态织入 细粒度 全层级(Controller/Service/Dao)的通用横切逻辑,事务、缓存、监控等

8.4 AOP vs 责任链模式

  • 责任链模式:将多个处理器串联成链,依次处理请求,每个处理器可决定是否终止请求传递,适合有明确顺序的请求处理流程
  • AOP:多个切面的执行顺序,本质上就是责任链模式的实现,Spring AOP的拦截器链就是典型的责任链结构
  • 差异:AOP是更高层次的抽象,通过注解/配置即可实现责任链的能力,无需手动编写链结构,开发成本更低,适配性更强
相关推荐
鹏多多2 小时前
Flutter使用pretty_qr_code生成高颜值二维码
android·前端·flutter
安逸sgr2 小时前
MCP 协议深度解析(一):MCP 协议概览与架构设计
服务器·网络·人工智能·网络协议·agent·mcp
XiaoLeisj2 小时前
Android 文件与数据存储实战:SharedPreferences、SQLite 与 Room 的渐进式实现
android·java·数据库·ui·sqlite·room·sp
MegaDataFlowers2 小时前
认识O(NlogN)的排序
java·开发语言·排序算法
Qiuner2 小时前
浏览器拓展通用安装方法 edge浏览器、谷歌浏览器、google浏览器、火狐浏览器
前端·google·edge
小鸡吃米…2 小时前
调试线程应用程序
开发语言·python
MasonYyp2 小时前
简单使用代码沙箱技术
python
苍何2 小时前
用 AI 多角度出图,电商产品图有救了!
后端
xiaoxue..2 小时前
前后端双令牌认证(Access Token + Refresh Token)全方案实现:安全与体验兼得
前端·后端·web安全·面试·typescript·nestjs