在最近发布的 Spring AI 2.0.0-RC1 中,有一项让人有些意外的代码重构:将原本直接调用的 SLF4J 门面替换为了传统的 Commons Logging API (LogFactory)。这很容易让人产生“设计倒退”的疑惑。本文将为你梳理这一改动的真实背景,带你了解 Spring 统一的 spring-jcl 日志门面机制以及其背后的工程考量。

Spring AI 2.0.0-RC1 发布说明中,有这样一项变更:

Replace SLF4J with org.apache.commons.logging.LogFactory.

代码由:

private static final Logger logger =
        LoggerFactory.getLogger(MyClass.class);

变成:

private final Log logger =
        LogFactory.getLog(getClass());

这很容易让人误以为 Spring AI 从 SLF4J“倒退”到了 Apache Commons Logging。实际情况并非如此。

包名属于 Apache,实现属于 Spring

LogLogFactory 的包名确实是:

org.apache.commons.logging

但在现代 Spring 应用中,这些类通常由 Spring Framework 的 spring-jcl 模块提供,而不是传统的 Apache commons-logging

可以在 Spring Framework 仓库看到这个独立模块:

也可以在程序中确认类来自哪个 JAR:

System.out.println(
    LogFactory.class.getProtectionDomain()
        .getCodeSource()
        .getLocation()
);

一般会看到:

spring-jcl-*.jar

而不是:

commons-logging-*.jar

Spring 什么时候开始使用 spring-jcl?

Spring Framework 很早就使用 Commons Logging API。

真正的变化发生在 Spring Framework 5.0,于 2017 年发布:Spring 开始提供自己的 spring-jcl 兼容实现。

证据可以从 Spring Framework 5.0 的源码和 Maven 构件中看到:

因此,Spring AI 2.0 并不是创造了一种新的日志方案,只是在向 Spring Framework 已有的规范靠拢。

Spring Boot 不是使用 SLF4J 吗?

更准确地说:

  • Spring Boot 内部代码使用 Commons Logging API,由 spring-jcl 提供。
  • Spring Boot 默认选择 Logback 作为日志实现。
  • SLF4J 是其中重要的路由和兼容层。

Spring Boot 官方文档明确说明:

Spring Boot uses Commons Logging for all internal logging but leaves the underlying log implementation open.

同时,使用 Starter 时默认采用 Logback,并负责把 SLF4J、JUL、Commons Logging 等日志统一路由。

参考:Spring Boot Logging 官方文档

典型调用链可以简化为:

Spring Framework / Spring AI

       spring-jcl

    SLF4J 兼容与路由

        Logback

因此,Spring AI 改用 LogFactory 后,Boot 应用最终仍然通常由 Logback 输出日志。

为什么 Spring 不彻底迁移到 SLF4J?

SLF4J 本身没有问题。Spring 没有全面迁移,主要是工程权衡。

收益有限

在 Spring Boot 应用中,各类日志本来就会被统一路由到默认的 Logback。修改 Spring Framework 全部源码,对最终用户几乎没有可见收益。

兼容成本较高

Spring 长期使用 org.apache.commons.logging API。迁移可能影响:

  • Framework 内部代码
  • Spring 组合项目
  • 第三方扩展
  • 暴露 Log 类型的受保护或公共 API
  • 已经编译的二进制代码

即使安排在大版本中,这仍是一项成本很高、收益有限的变化。

Framework 与 Boot 的职责不同

Spring Framework 是基础库,允许脱离 Spring Boot 使用,因此需要适应不同日志环境。

Spring Boot 面向最终应用,可以提供更明确的默认组合:

Spring Boot Starter → SLF4J 生态 → Logback

Framework 保留自己的适配层,可以避免直接把 SLF4J 作为内部日志契约。

Spring 可以控制日志发现逻辑

传统 Commons Logging 的运行时发现机制曾带来类加载问题。spring-jcl 保留兼容 API,但实际适配逻辑由 Spring 控制。

这也是它继续使用原包名的原因:兼容旧代码,而不是继续依赖旧实现。

对开发者有什么影响?

对于普通 Spring Boot 应用,基本没有影响:

logging.level.org.springframework.ai=DEBUG

这类配置仍然有效,日志也仍然默认由 Logback 输出。

不要因为看到 org.apache.commons.logging 就主动添加:

<artifactId>commons-logging</artifactId>

传统 commons-loggingspring-jcl 提供同名类,放在一起可能产生冲突。

结论

Spring AI 2.0 的变化不是:

SLF4J → 老旧 Commons Logging

而是:

直接使用 SLF4J

改用 Spring 统一的 spring-jcl 门面

运行时仍可进入 SLF4J / Logback

SLF4J 没有什么技术问题。这次调整主要是为了让 Spring AI 与 Spring Framework 和其他 Spring 项目保持一致,同时继续维持日志后端的可替换性和历史兼容性。