深入解析:从 “坑“ 到 “通“:Spring AOP 通知执行顺序深度解密

news/2025/11/16 9:48:34/文章来源:https://www.cnblogs.com/slgkaifa/p/19227168

深入解析:从 “坑“ 到 “通“:Spring AOP 通知执行顺序深度解密

前言:为什么你必须搞懂 AOP 通知通知执行顺序?

在 Spring 开发中,AOP(面向切面编程)是实现代码解耦的利器,日志记录、事务管理、权限控制等横切逻辑都离不开它。但你是否遇到过这些困惑:

  • 同样的 @Before 和 @After 注解,在不同项目中执行顺序居然不一样?
  • 升级 Spring 版本后,切面逻辑突然出错,排查半天发现是通知执行顺序变了?
  • 方法抛出异常时,为什么有的 @AfterReturning 通知不执行了?

这些问题的根源,都指向一个核心知识点:Spring AOP 通知的执行顺序。更关键的是,Spring 在 5.2.7 版本对通知执行顺序做了重大调整,这导致很多开发者在升级后遭遇了 "灵异现象"。

本文将从底层原理到实战案例,全方位剖析 Spring AOP 通知的执行顺序,特别是 5.2.7 版本前后的差异,帮你彻底掌握这一核心知识点,避免在实际开发中踩坑。

一、Spring AOP 通知类型全解析

在探讨执行顺序之前,我们先明确 Spring AOP 支持的 5 种通知类型,这是理解后续内容的基础。

1.1 5 种通知类型及作用

  • @Before(前置通知):在目标方法执行前执行
  • @After(后置通知):在目标方法执行后执行,无论是否发生异常
  • @AfterReturning(返回通知):在目标方法正常返回后执行
  • @AfterThrowing(异常通知):在目标方法抛出异常后执行
  • @Around(环绕通知):环绕目标方法执行,可控制目标方法的执行时机,功能最强大

1.2 通知类型的核心区别

通知类型执行时机能否阻止目标方法执行能否获取返回值能否获取异常
@Before目标方法执行前不能(除非抛出异常)不能不能
@After目标方法执行后不能不能不能
@AfterReturning目标方法正常返回后不能不能
@AfterThrowing目标方法抛出异常后不能不能
@Around可自定义执行时机能(不调用 proceed ())

二、Spring 5.2.7 版本前的通知执行顺序

在 Spring 5.2.7 版本之前,通知的执行顺序有其特定的规则,尤其是多个切面作用于同一个目标方法时,顺序更为复杂。

2.1 单个切面内的通知执行顺序

当一个切面中定义了多种通知类型,作用于同一个目标方法时,其执行顺序如下:

正常执行(无异常)情况

异常执行情况

2.2 多个切面的通知执行顺序

当多个切面作用于同一个目标方法时,Spring 采用 "同心圆" 模型:

  • 外层切面的 @Before 先执行,内层切面的 @Before 后执行(类似剥洋葱,从外到内)
  • 目标方法执行后,内层切面的 @After 先执行,外层切面的 @After 后执行(从内到外)

2.3 5.2.7 版本前的代码示例

我们通过一个完整示例来验证上述执行顺序。

1. 项目依赖(pom.xml)

4.0.0com.examplespring-aop-order-demo1.0-SNAPSHOT17175.2.6.RELEASE org.springframeworkspring-context${spring.version}org.springframeworkspring-aop${spring.version}org.aspectjaspectjweaver1.9.6org.projectlomboklombok1.18.30provided
2. 业务服务类
/*** 订单服务类* @author ken*/
@Service
@Slf4j
public class OrderService {/*** 创建订单* @param orderId 订单ID* @return 订单创建结果*/public String createOrder(String orderId) {log.info("执行订单创建逻辑,订单ID:{}", orderId);// 模拟正常执行return "订单创建成功:" + orderId;// 模拟异常情况// throw new RuntimeException("订单创建失败:库存不足");}
}
3. 外层切面
/*** 外层切面(日志切面)* @author ken*/
@Aspect
@Component
@Order(1) // 数值越小,优先级越高,越先执行
@Slf4j
public class LogAspect {/*** 定义切入点*/@Pointcut("execution(* com.example.service.OrderService.createOrder(..))")public void orderPointcut() {}/*** 前置通知*/@Before("orderPointcut()")public void before() {log.info("LogAspect - @Before 前置通知执行");}/*** 后置通知*/@After("orderPointcut()")public void after() {log.info("LogAspect - @After 后置通知执行");}/*** 返回通知*/@AfterReturning(pointcut = "orderPointcut()", returning = "result")public void afterReturning(Object result) {log.info("LogAspect - @AfterReturning 返回通知执行,返回结果:{}", result);}/*** 异常通知*/@AfterThrowing(pointcut = "orderPointcut()", throwing = "ex")public void afterThrowing(Exception ex) {log.info("LogAspect - @AfterThrowing 异常通知执行,异常信息:{}", ex.getMessage());}/*** 环绕通知*/@Around("orderPointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("LogAspect - @Around 环绕通知开始");Object result = joinPoint.proceed(); // 执行目标方法log.info("LogAspect - @Around 环绕通知结束");return result;}
}
4. 内层切面
/*** 内层切面(性能监控切面)* @author ken*/
@Aspect
@Component
@Order(2) // 优先级低于LogAspect
@Slf4j
public class PerformanceAspect {/*** 定义切入点(与LogAspect相同)*/@Pointcut("execution(* com.example.service.OrderService.createOrder(..))")public void orderPointcut() {}/*** 前置通知*/@Before("orderPointcut()")public void before() {log.info("PerformanceAspect - @Before 前置通知执行");}/*** 后置通知*/@After("orderPointcut()")public void after() {log.info("PerformanceAspect - @After 后置通知执行");}/*** 返回通知*/@AfterReturning(pointcut = "orderPointcut()", returning = "result")public void afterReturning(Object result) {log.info("PerformanceAspect - @AfterReturning 返回通知执行,返回结果:{}", result);}/*** 异常通知*/@AfterThrowing(pointcut = "orderPointcut()", throwing = "ex")public void afterThrowing(Exception ex) {log.info("PerformanceAspect - @AfterThrowing 异常通知执行,异常信息:{}", ex.getMessage());}/*** 环绕通知*/@Around("orderPointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("PerformanceAspect - @Around 环绕通知开始");Object result = joinPoint.proceed(); // 执行目标方法log.info("PerformanceAspect - @Around 环绕通知结束");return result;}
}
5. Spring 配置类
/*** Spring配置类* @author ken*/
@Configuration
@ComponentScan("com.example")
@EnableAspectJAutoProxy
public class AppConfig {
}
6. 测试类
/*** AOP通知顺序测试类* @author ken*/
@Slf4j
public class AopOrderTest {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);OrderService orderService = context.getBean(OrderService.class);log.info("====== 开始执行订单创建 ======");orderService.createOrder("ORDER_001");log.info("====== 订单创建执行完毕 ======");}
}
7. 执行结果(正常情况)
====== 开始执行订单创建 ======
LogAspect - @Around 环绕通知开始
LogAspect - @Before 前置通知执行
PerformanceAspect - @Around 环绕通知开始
PerformanceAspect - @Before 前置通知执行
执行订单创建逻辑,订单ID:ORDER_001
PerformanceAspect - @Around 环绕通知结束
PerformanceAspect - @After 后置通知执行
PerformanceAspect - @AfterReturning 返回通知执行,返回结果:订单创建成功:ORDER_001
LogAspect - @Around 环绕通知结束
LogAspect - @After 后置通知执行
LogAspect - @AfterReturning 返回通知执行,返回结果:订单创建成功:ORDER_001
====== 订单创建执行完毕 ======
8. 执行结果(异常情况)

将 OrderService 中的代码改为抛出异常:

public String createOrder(String orderId) {log.info("执行订单创建逻辑,订单ID:{}", orderId);// 模拟异常情况throw new RuntimeException("订单创建失败:库存不足");
}

执行结果:

====== 开始执行订单创建 ======
LogAspect - @Around 环绕通知开始
LogAspect - @Before 前置通知执行
PerformanceAspect - @Around 环绕通知开始
PerformanceAspect - @Before 前置通知执行
执行订单创建逻辑,订单ID:ORDER_001
PerformanceAspect - @After 后置通知执行
PerformanceAspect - @AfterThrowing 异常通知执行,异常信息:订单创建失败:库存不足
LogAspect - @After 后置通知执行
LogAspect - @AfterThrowing 异常通知执行,异常信息:订单创建失败:库存不足
Exception in thread "main" java.lang.RuntimeException: 订单创建失败:库存不足...

2.4 5.2.7 版本前顺序总结

  1. 环绕通知的前置部分最早执行
  2. 前置通知按照 @Order 注解的顺序(从小到大)执行
  3. 目标方法执行
  4. 环绕通知的后置部分接着执行
  5. 后置通知按照 @Order 注解的逆序(从大到小)执行
  6. 最后执行返回通知或异常通知(也按照 @Order 逆序)

三、Spring 5.2.7 版本后的通知执行顺序

Spring 在 5.2.7 版本(包含该版本)对 AOP 通知执行顺序进行了调整,主要是为了让通知执行顺序更符合直觉,并与 AspectJ 保持一致。

3.1 版本调整的背景与原因

在 Spring 官方文档中,这次调整的原因被描述为:

"调整 AOP 通知的执行顺序,使其与 AspectJ 的行为保持一致,同时修复了多个切面情况下 @After 通知执行顺序不符合预期的问题。"

简单来说,旧版本的执行顺序在多个切面时,@After 通知的执行顺序容易让人混淆,调整后的顺序更符合 "先入后出" 的原则。

3.2 单个切面内的通知执行顺序

单个切面内的通知执行顺序在版本调整前后变化不大:

正常执行情况

异常执行情况

3.3 多个切面的通知执行顺序

多个切面的执行顺序变化较大,调整后的顺序如下:

3.4 5.2.7 版本后的代码示例

我们只需修改 pom.xml 中的 Spring 版本:

17175.2.7.RELEASE 

其他代码保持不变,执行测试类。

1. 执行结果(正常情况)
====== 开始执行订单创建 ======
LogAspect - @Around 环绕通知开始
LogAspect - @Before 前置通知执行
PerformanceAspect - @Around 环绕通知开始
PerformanceAspect - @Before 前置通知执行
执行订单创建逻辑,订单ID:ORDER_001
PerformanceAspect - @AfterReturning 返回通知执行,返回结果:订单创建成功:ORDER_001
PerformanceAspect - @After 后置通知执行
PerformanceAspect - @Around 环绕通知结束
LogAspect - @AfterReturning 返回通知执行,返回结果:订单创建成功:ORDER_001
LogAspect - @After 后置通知执行
LogAspect - @Around 环绕通知结束
====== 订单创建执行完毕 ======
2. 执行结果(异常情况)
====== 开始执行订单创建 ======
LogAspect - @Around 环绕通知开始
LogAspect - @Before 前置通知执行
PerformanceAspect - @Around 环绕通知开始
PerformanceAspect - @Before 前置通知执行
执行订单创建逻辑,订单ID:ORDER_001
PerformanceAspect - @AfterThrowing 异常通知执行,异常信息:订单创建失败:库存不足
PerformanceAspect - @After 后置通知执行
LogAspect - @AfterThrowing 异常通知执行,异常信息:订单创建失败:库存不足
LogAspect - @After 后置通知执行
Exception in thread "main" java.lang.RuntimeException: 订单创建失败:库存不足...

3.5 5.2.7 版本后顺序总结

  1. 环绕通知的前置部分和前置通知的执行顺序与旧版本一致(按 @Order 从小到大)
  2. 目标方法执行后,返回通知或异常通知先执行(按 @Order 从大到小)
  3. 然后执行后置通知(按 @Order 从大到小)
  4. 最后执行环绕通知的后置部分(按 @Order 从大到小)

简单来说,调整后的顺序让 @AfterReturning、@AfterThrowing 和 @After 在每个切面内部先执行完毕,再执行外层切面的对应通知,更符合直觉。

四、版本差异对比与迁移指南

4.1 核心差异点对比

通知类型5.2.7 版本前执行顺序5.2.7 版本后执行顺序
@Around(后置部分)在 @After 和 @AfterReturning/@AfterThrowing 之前在 @After 和 @AfterReturning/@AfterThrowing 之后
@After在 @AfterReturning/@AfterThrowing 之前在 @AfterReturning/@AfterThrowing 之后
多个切面的 @After按 @Order 逆序执行,但在 @Around 后置部分之前按 @Order 逆序执行,在 @AfterReturning/@AfterThrowing 之后,@Around 后置部分之前

4.2 可视化对比

4.3 升级 Spring 版本的迁移指南

如果你的项目需要从 5.2.7 之前的版本升级到之后的版本,为避免 AOP 通知顺序变化导致的问题,可按以下步骤操作:

  1. 全面梳理现有切面:列出项目中所有的切面类,特别是那些依赖通知执行顺序的切面。

  2. 识别关键顺序依赖:找出那些 @After、@AfterReturning、@AfterThrowing 和 @Around(后置部分)中存在逻辑依赖的代码。

  3. 调整通知类型或顺序

    • 对于依赖 @After 在 @AfterReturning 之前执行的逻辑,可将 @After 中的代码迁移到 @AfterReturning 中
    • 对于依赖 @Around 后置部分在 @After 之前执行的逻辑,可调整代码顺序或拆分切面
  4. 增加集成测试:为关键业务流程编写集成测试,验证 AOP 通知执行顺序是否符合预期。

  5. 逐步升级验证:先在测试环境升级,全面验证通过后再部署到生产环境。

五、环绕通知的特殊处理

环绕通知(@Around)是功能最强大的通知类型,它可以控制目标方法的执行时机,因此其执行顺序需要特别关注。

5.1 环绕通知的内部结构

一个完整的环绕通知通常包含三个部分:

@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 1. 前置部分:在目标方法执行前执行log.info("环绕通知 - 前置部分");Object result;try {// 执行目标方法result = joinPoint.proceed();// 2. 后置部分:在目标方法正常返回后执行log.info("环绕通知 - 后置部分");} catch (Exception e) {// 3. 异常部分:在目标方法抛出异常后执行log.info("环绕通知 - 异常部分");throw e;}return result;
}

5.2 环绕通知与其他通知的交互

在 5.2.7 版本前后,环绕通知与其他通知的交互关系发生了变化:

5.2.7 版本前

5.2.7 版本后

这种变化意味着,在新版本中,其他通知的执行结果可以影响环绕通知的后置处理逻辑,这在事务管理等场景中非常有用。

5.3 环绕通知的最佳实践

  1. 始终调用 proceed () 方法:除非你明确要阻止目标方法执行,否则一定要调用 joinPoint.proceed (),否则目标方法和其他通知都不会执行。

  2. 正确处理异常:环绕通知中需要正确处理异常,要么捕获处理,要么重新抛出,避免异常被吞噬。

  3. 避免过度使用:虽然环绕通知功能强大,但也容易导致代码复杂,简单场景优先使用其他通知类型。

  4. 版本兼容考虑:如果项目可能跨版本运行,在环绕通知的后置部分避免依赖其他通知的执行结果。

六、实战中的常见问题与解决方案

6.1 通知执行顺序不一致

问题:在不同环境或不同版本中,通知执行顺序不一致。

解决方案

  1. 显式指定 @Order 注解:确保所有切面都添加了 @Order 注解,明确指定执行顺序。
@Aspect
@Component
@Order(1) // 明确指定顺序
public class FirstAspect { ... }
@Aspect
@Component
@Order(2) // 明确指定顺序
public class SecondAspect { ... }
  1. 避免依赖默认顺序:不要依赖类名或加载顺序来决定切面执行顺序,这在不同环境中可能不同。

  2. 版本适配:如果需要兼容多个 Spring 版本,尽量避免在 @After、@AfterReturning 和 @Around(后置部分)中编写有顺序依赖的逻辑。

6.2 @AfterReturning 不执行

问题:目标方法正常执行,但 @AfterReturning 通知不执行。

可能原因及解决方案

  1. 目标方法被环绕通知拦截且未调用 proceed ()
// 错误示例
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) {log.info("环绕通知执行");// 忘记调用proceed(),目标方法和@AfterReturning都不会执行return null;
}
// 正确示例
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("环绕通知执行");return joinPoint.proceed(); // 必须调用proceed()
}
  1. 切入点表达式不匹配:检查 @AfterReturning 的 pointcut 表达式是否正确匹配目标方法。

  2. 目标方法抛出了异常:@AfterReturning 仅在目标方法正常返回时执行,如果抛出异常,应使用 @AfterThrowing。

6.3 多个 @AfterThrowing 通知的执行顺序

问题:多个切面的 @AfterThrowing 通知执行顺序不符合预期。

解决方案

  1. 明确指定 @Order 注解,@AfterThrowing 的执行顺序遵循 @Order 的逆序(5.2.7 版本后)。

  2. 避免在多个切面中处理同一类型的异常,这可能导致逻辑混乱。

  3. 对于异常处理,优先使用环绕通知,它可以更灵活地控制异常处理逻辑。

6.4 自调用导致通知不执行

问题:在同一个类中,一个方法调用另一个方法时,被调用方法的通知不执行。

解决方案

  1. 通过 AopContext 获取代理对象
@Service
public class OrderService {public void methodA() {// 错误方式:直接调用,通知不执行// methodB();// 正确方式:通过AopContext获取代理对象调用OrderService proxy = (OrderService) AopContext.currentProxy();proxy.methodB();}public void methodB() {// ...}
}

需要在 @EnableAspectJAutoProxy 中设置 exposeProxy = true:

@Configuration
@EnableAspectJAutoProxy(exposeProxy = true)
public class AppConfig { ... }
  1. 拆分服务类:将方法拆分到不同的服务类中,避免自调用。

  2. 使用 AspectJ 的编译期织入:AspectJ 的字节码织入可以解决自调用问题,但配置相对复杂。

七、最佳实践与总结

7.1 通知使用最佳实践

  1. 按功能划分切面:一个切面专注于一个功能(如日志、事务、权限),避免大而全的切面。

  2. 合理选择通知类型

    • 日志记录:优先使用 @Before 和 @AfterReturning/@AfterThrowing
    • 性能监控:优先使用 @Around,可以记录方法执行时间
    • 资源释放:优先使用 @After,确保无论是否异常都会执行
    • 事务管理:必须使用 @Around 或 @Before+@AfterReturning/@AfterThrowing 组合
  3. 明确指定 @Order:只要有多个切面,就必须显式指定 @Order,避免依赖默认顺序。

  4. 避免通知之间的依赖:尽量让每个通知独立工作,不依赖其他通知的执行结果。

  5. 谨慎使用环绕通知:只有在需要控制目标方法执行时机时才使用,否则优先使用其他通知类型。

7.2 版本选择建议

  1. 新项目:直接使用最新稳定版本(如 Spring 6.x),遵循新版本的执行顺序。

  2. 旧项目升级

    • 如无特殊需求,可保持现有版本,避免升级带来的风险
    • 如需升级,务必全面测试 AOP 相关逻辑
    • 可考虑分阶段升级,先升级到 5.2.7 版本,适应新的执行顺序后再升级到更高版本
  3. 跨版本组件:如果开发的是供多个项目使用的组件,需要兼容不同 Spring 版本,应避免在通知中编写依赖特定执行顺序的逻辑。

7.3 核心知识点总结

  1. Spring AOP 有 5 种通知类型:@Before、@After、@AfterReturning、@AfterThrowing 和 @Around。

  2. Spring 5.2.7 版本是通知执行顺序的分水岭,主要变化是 @After、@AfterReturning/@AfterThrowing 和 @Around(后置部分)的相对顺序。

  3. 5.2.7 版本前:@Around(后置)→ @After → @AfterReturning/@AfterThrowing

  4. 5.2.7 版本后:@AfterReturning/@AfterThrowing → @After → @Around(后置)

  5. 多个切面的执行顺序由 @Order 注解控制,数值越小越先执行,且遵循 "先入后出" 原则。

  6. 环绕通知功能最强大,但也最复杂,使用时需特别注意调用 proceed () 方法和异常处理。

掌握 Spring AOP 通知的执行顺序,不仅能帮助你写出更可靠的切面代码,还能在遇到问题时快速定位原因。无论是旧版本的项目维护,还是新版本的项目开发,理解这些核心原理都至关重要。记住,AOP 是一把双刃剑,只有正确使用才能发挥其威力,否则可能带来难以调试的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025年热门的极简定制家具拉手厂家最新热销排行

2025年热门的极简定制家具拉手厂家最新热销排行行业背景与市场趋势随着现代家居设计理念的不断演进,极简主义风格在2025年依然保持着强劲的市场需求。根据中国家具协会最新发布的《2025年中国定制家具五金配件市场报告…

the mission of English

English has much shortest words, also as an analytically Phonetic language. English will replace all other Phonetic language. is English good? I dont know. just like where do the English scam texts, e…

2025年桂圆品牌权威推荐榜单:优质选择与行业洞察

摘要 桂圆行业在2025年持续增长,受益于健康饮食趋势和消费者对高品质干果的需求提升。行业注重食品安全、溯源技术和定制化服务,头部品牌通过严格质检和智能供应链占据优势。本文基于市场数据和用户口碑,整理出桂圆…

2025年知名的胶印油墨厂家最新用户好评榜

2025年知名的胶印油墨厂家最新用户好评榜胶印油墨行业背景与市场趋势胶印油墨作为印刷行业的核心材料,其市场需求与印刷业发展息息相关。根据《2024-2025全球印刷油墨市场报告》显示,全球胶印油墨市场规模预计在2025…

2025年比较好的无极绳气动煤矿道岔高评价厂家推荐榜

2025年比较好的无极绳气动煤矿道岔高评价厂家推荐榜行业背景与市场趋势煤矿道岔作为矿山运输系统的关键部件,其性能直接影响煤矿生产效率和安全性。随着我国煤矿智能化建设的深入推进,2023-2025年煤矿道岔市场规模预…

十、HTML 符号

HTML 符号实体 在上一章中已经讲解了 HTML 实体。 普通键盘上不存在众多数学、技术和货币符号。 如需将此类符号添加到 HTML 页面,你可以使用 HTML 实体名称(HTML entity name)。 如果不存在实体名称,则可使用实体…

HTML4----------文字笔记

如何使用 CSSCSS 是在 HTML 4 开始使用的,是为了更好的染 HTML 元素而引入的。CSS 可以通过以下方式添加到 HTML 中:● 内联样式- HTML 元素中使用"style"属性● 内部样式表 - HTML 文档头部 <head>…

2025年靠谱的淄博防爆潜水泵最新TOP厂家排名

2025年靠谱的淄博防爆潜水泵最新TOP厂家排名行业背景与市场趋势防爆潜水泵作为工业领域的关键设备,在石油化工、矿山开采、市政工程等高风险环境中发挥着不可替代的作用。根据中国泵行业协会最新发布的《2024-2025年中…

2025年口碑好的高温伴热带TOP实力厂家推荐榜

2025年口碑好的高温伴热带TOP实力厂家推荐榜行业背景与市场趋势高温伴热带作为工业管道伴热、屋面天沟融雪等领域的关键设备,近年来随着全球工业化进程加速和极端气候频发,市场需求持续增长。据《2024-2029全球高温伴…

2025年全新碳酸钙吨袋厂家最新热销排行

2025年全新碳酸钙吨袋厂家最新热销排行行业背景与市场趋势碳酸钙作为重要的工业原料,广泛应用于塑料、造纸、涂料、橡胶等行业。随着全球环保政策的趋严和循环经济的推进,碳酸钙吨袋作为其运输包装的核心载体,市场需…

2025年知名的污水格栅机厂家最新权威推荐排行榜

2025年知名的污水格栅机厂家最新权威推荐排行榜 行业背景与市场趋势 随着全球环保意识的提升和污水处理标准的日益严格,污水格栅机作为污水处理系统的关键设备,市场需求持续增长。根据《2024-2025年中国环保设备行…

mysql查询数据的细节良好习惯

mysql查询数据的细节良好习惯1.ORDER BY t1.level DESC,t1.id DESC 這個語句的區別。 ORDER BY t1.id DESC,t1.level DESC 这样写等于没有生效了。 不会按level了。 分组多条件排序,不能根据id来,不能放在最前面。2.…

ehviewer白色版1.9.8.0使用教程

ehviewer白色版1.9.8.0使用教程ehviewer白色版1.9.8.0是一款漫画阅读软件,涵盖各种不同题材的漫画作品,并且漫画资源更新的速度非常快,用户能够在每日规定的时间阅读精彩的剧情内容,资源库当中包含的所有漫画资源提…

2025年知名的边料粉碎机TOP实力厂家推荐榜

2025年知名的边料粉碎机TOP实力厂家推荐榜行业背景与市场趋势随着全球制造业的持续发展和环保要求的不断提高,边料粉碎机作为塑料加工行业的重要辅助设备,市场需求呈现稳定增长态势。据《2024-2029全球边料粉碎机行业…

2025年比较好的变流器高压直流继电器实力厂家TOP推荐榜

2025年比较好的变流器高压直流继电器实力厂家TOP推荐榜行业背景与市场趋势随着全球能源结构转型加速推进,高压直流输电技术因其传输损耗低、容量大、距离远等优势,在新能源发电、电动汽车、智能电网等领域得到广泛应…

2025年质量好的铝箔橡塑保温管行业内口碑厂家排行榜

2025年质量好的铝箔橡塑保温管行业内口碑厂家排行榜行业背景与市场趋势铝箔橡塑保温管作为建筑节能领域的重要材料,近年来随着国家"双碳"目标的推进和建筑节能标准的不断提高,市场需求呈现稳定增长态势。根…

002 vue3-admin项目的目录及文件说明之tsconfig.app.json

说明 tsconfig.app.json 是 TypeScript 配置文件,专门用于应用代码的编译配置。 基本结构{"extends": "@vue/tsconfig/tsconfig.dom.json","compilerOptions": {"tsBuildInfoFile…

2025年知名的保安公司推荐榜

2025年知名的保安公司推荐榜 行业背景与市场趋势 随着社会经济的快速发展和安全需求的不断提升,保安服务行业已成为现代服务业的重要组成部分。根据中国保安协会发布的《2024年中国保安行业发展报告》,2023年中国保…

2025年知名的定制家具厂家推荐及选购指南

2025年知名的定制家具厂家推荐及选购指南行业背景与市场趋势随着中国城镇化进程的加快和居民消费升级,定制家具行业近年来呈现爆发式增长。据中国家具协会最新数据显示,2024年中国定制家具市场规模已达5800亿元,预计…

11.11 联合查询 union /union all

SELECT 字段列表 。。。。。。(完整查询) union SELECT 字段列表...(完整); union直接合并未去重; SELECT....(完整) union all SELECT......(完整); union all去重了