领先一步
VMware 提供培训和认证,助您加速进步。
了解更多本文档将探讨 Spring 的选择以及开发者在构建 Spring 应用程序时可用的日志记录选项。这篇文档的发布恰逢 Spring 3.0 的即将发布,并非因为我们做了很多改变(尽管我们现在对依赖元数据更加谨慎),而是为了让您能够做出明智的决定,了解如何在您的应用程序中实现和配置日志记录。首先,我们将简要回顾 Spring 的强制性依赖,然后详细讨论如何设置应用程序以使用一些常见的日志库。作为示例,我将展示使用 Maven Central 风格的工件命名约定进行的依赖配置。
尽管 Spring 提供了对大量企业和其他外部工具的集成和支持,但它有意将强制性依赖项保持在绝对最小值:您不必为了在简单用例中使用 Spring 而查找和下载(即使是自动下载)大量的 jar 库。对于基本的依赖注入,只有一个强制性的外部依赖,那就是日志记录(有关日志记录选项的更详细描述,请参阅下文)。如果您使用 Maven 进行依赖管理,您甚至不需要显式提供日志依赖。例如,要创建一个应用程序上下文并使用依赖注入来配置应用程序,您的 Maven 依赖项将如下所示:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.2.RELEASE</version>
<scope>runtime</scope>
</dependency>
</dependencies>
就是这样。请注意,范围可以声明为 runtime,如果您不需要编译 Spring API,这通常是基本依赖注入用例的情况。
我们在上面的示例中使用了 Maven Central 命名约定,因此它适用于 Maven Central 或 SpringSource S3 Maven 存储库。要使用 S3 Maven 存储库(例如,用于里程碑或开发者快照),您需要在 Maven 配置中指定存储库位置。对于完整版本:
<repositories>
<repository>
<id>com.springsource.repository.maven.release</id>
<url>http://maven.springframework.org/release/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
对于里程碑:
<repositories>
<repository>
<id>com.springsource.repository.maven.milestone</id>
<url>http://maven.springframework.org/milestone/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
对于快照:
<repositories>
<repository>
<id>com.springsource.repository.maven.snapshot</id>
<url>http://maven.springframework.org/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
要使用 SpringSource EBR,您需要为依赖项使用不同的命名约定。名称通常很容易猜测,例如,在这种情况下是:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.context</artifactId>
<version>3.0.0.RELEASE</version>
<scope>runtime</scope>
</dependency>
</dependencies>
您还需要显式声明存储库的位置(仅 URL 重要):
<repositories>
<repository>
<id>com.springsource.repository.bundles.release</id>
<url>http://repository.springsource.com/maven/bundles/release/</url>
</repository>
</repositories>
如果您手动管理依赖项,上述存储库声明中的 URL 是不可浏览的,但是 http://www.springsource.com/repository 上的用户界面可以用来搜索和下载依赖项。它还提供了实用的 Maven 和 Ivy 配置代码片段,如果您正在使用这些工具,可以复制粘贴。
如果您更喜欢使用 Ivy 来管理依赖项,那么在那里也有类似的名称和配置选项(请参阅您的依赖管理系统的文档,或查看一些示例代码 - Spring 本身在构建时使用 Ivy 来管理依赖项)。
日志记录是 Spring 的一个非常重要的依赖项,因为 a) 它是唯一强制性的外部依赖项,b) 每个人都希望看到他们使用的工具的一些输出,并且 c) Spring 与许多其他工具集成,所有这些工具也都选择了日志依赖项。应用程序开发者的一个目标通常是在一个中心位置为整个应用程序(包括所有外部组件)配置统一的日志记录。这比看起来要困难,因为日志框架的选择非常多。
Spring 的强制性日志依赖项是 Jakarta Commons Logging API (JCL)。我们针对 JCL 进行编译,并且还为扩展 Spring Framework 的类公开 JCL Log 对象。所有版本的 Spring 都使用相同的日志库对用户很重要:迁移很容易,因为即使是扩展 Spring 的应用程序也保留了向后兼容性。我们的做法是让 Spring 的一个模块显式依赖于 commons-logging(JCL 的规范实现),然后让所有其他模块在编译时依赖于它。例如,如果您正在使用 Maven,并且想知道您在哪里获得了 commons-logging 的依赖项,那么它来自 Spring,特别是来自名为 spring-core 的核心模块。
commons-logging 的好处是您不需要其他任何东西就能使您的应用程序正常工作。它有一个运行时发现算法,可以在已知的类路径位置查找其他日志框架,并使用它认为合适的框架(或者您可以告诉它您需要哪个)。如果没有其他可用,您将仅从 JDK(java.util.logging 或简称 JUL)获得相当不错的日志。您应该会发现您的 Spring 应用程序在大多数情况下都能开箱即用地工作并愉快地输出到控制台,这一点很重要。
不幸的是,commons-logging 最糟糕的地方,也是使其在新工具中不受欢迎的原因,也是其运行时发现算法。如果我们能够回到过去,现在重新启动 Spring 项目,它会使用不同的日志依赖项。也许第一个选择将是 Simple Logging Facade for Java (SLF4J)(SLF4J),这也是许多人在应用程序中使用 Spring 时与其他工具一起使用的。
关闭 commons-logging 非常简单:只需确保在运行时它不在类路径上。在 Maven terms 中,您需要排除该依赖项,并且由于 Spring 依赖项的声明方式,您只需要执行一次。
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.2.RELEASE</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
现在这个应用程序可能已损坏,因为类路径上没有 JCL API 的实现,因此必须提供一个新的。在下一节中,我们将以 SLF4J 为例,向您展示如何提供 JCL 的替代实现。
SLF4J 是一个比 commons-logging 更简洁、运行时效率更高的依赖项,因为它使用编译时绑定而不是运行时发现它集成的其他日志框架。这也意味着您必须更明确地说明您希望在运行时发生什么,并相应地声明或配置它。SLF4J 提供了许多常见日志框架的绑定,因此您通常可以选择您已经使用的框架,并将其绑定以进行配置和管理。
SLF4J 提供了许多常见日志框架的绑定,包括 JCL,它也反过来:在其他日志框架和自身之间进行桥接。因此,要将 SLF4J 与 Spring 一起使用,您需要将 commons-logging 依赖项替换为 SLF4J-JCL 桥。一旦完成,Spring 内部的日志记录调用将被翻译成对 SLF4J API 的日志记录调用,因此如果您的应用程序中的其他库使用该 API,那么您就有了一个统一的地方来配置和管理日志记录。
一个常见的选择可能是将 Spring 桥接到 SLF4J,然后提供从 SLF4J 到 Log4J 的显式绑定。您需要提供 4 个依赖项(并排除现有的 commons-logging):桥接器、SLF4J API、到 Log4J 的绑定以及 Log4J 实现本身。在 Maven 中,您会这样做:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.2.RELEASE</version>
<scope>runtime</scope>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<scope>runtime</scope>
</dependency>
</dependencies>
仅仅为了获得一些日志记录,这似乎有很多依赖项。是的,但是它
SLF4J 用户中更常见的选择是直接绑定到 Logback,这需要更少的步骤并且生成的依赖项更少。这消除了额外的绑定步骤,因为 Logback 直接实现 SLF4J,所以您只需要依赖两个库而不是四个(jcl-over-slf4j 和 logback)。如果您这样做,您可能还需要从其他外部依赖项(不是 Spring)中排除 slf4j-api 依赖项,因为您只需要一个版本的该 API 在类路径上。
许多人使用 Log4j 作为日志记录框架来进行配置和管理。它高效且成熟,事实上,它也是我们在构建和测试 Spring 时在运行时使用的。Spring 还提供了一些用于配置和初始化 Log4j 的实用程序,因此在某些模块中它对 Log4j 具有可选的编译时依赖项。
要使 Log4j 与默认的 JCL 依赖项(commons-logging)一起工作,您所要做的就是将 Log4j 放在类路径上,并为其提供一个配置文件(log4j.properties 或 log4j.xml 在类路径的根目录)。因此,对于 Maven 用户,这是您的依赖项声明:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.2.RELEASE</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
<scope>runtime</scope>
</dependency>
</dependencies>
这是一个用于将日志记录到控制台的示例 log4j.properties:
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
log4j.category.org.springframework.beans.factory=DEBUG
许多人将他们的 Spring 应用程序运行在本身提供 JCL 实现的容器中。IBM Websphere Application Server (WAS) 是一个典型例子。这经常会引起问题,而且不幸的是,没有一劳永逸的解决方案;在大多数情况下,简单地从您的应用程序中排除 commons-logging 是不够的。
明确一点:报告的问题通常不是 JCL 本身,甚至不是 commons-logging:而是将 commons-logging 绑定到另一个框架(通常是 Log4J)。这可能会失败,因为 commons-logging 在一些容器中找到的旧版本(1.0)和大多数人现在使用的现代版本(1.1)之间更改了运行时发现方式。Spring 不使用 JCL API 的任何不寻常部分,因此那里没有破损,但是一旦 Spring 或您的应用程序尝试进行任何日志记录,您就会发现到 Log4J 的绑定不起作用。
在这种情况下,使用 WAS 的最简单方法是反转类加载器层次结构(IBM 称之为“parent last”),以便应用程序控制 JCL 依赖项,而不是容器。这个选项并非总是开放的,但公开领域有许多关于替代方法的建议,您的体验可能会因容器的确切版本和功能集而异。
(注意:Spring 和 slf4j 的版本已从原始文章更新,以便于复制粘贴。)