前面的构建模块描述在很多的情况下使 Spring 是一个合理的选择,从资源受限的嵌入式程序到成熟的企业应用程序都可以使用 Spring 事务管理功能和 web 框架集成。
Figure 2.2. Typical full-fledged Spring web application
Spring [声明式事务管理特性](../IV. Data Access/12.5. Declarative transaction management.md)使 Web 应用程序拥有完全的事务,就像你使用 EJB 容器管理的事务。所有的自定义业务逻辑可以用简单的 POJO 实现,用 Spring 的 IoC 容器进行管理。额外的服务包括发送电子邮件和验证,是独立的网络层的支持,它可以让你选择在何处执行验证规则。Spring 的 ORM 支持集成 JPA,Hibernate,JDO;例如,当使用 Hibernate,您可以继续使用现有的映射文件和 标准的 Hibernate 的 SessionFactory 配置。表单控制器将 Web 层和领域模型无缝集成,消除 ActionForms 或其他类用于变换 HTTP 参数成为您的域模型值的需要。
Figure 2.3. Spring middle-tier using a third-party web framework
有时,不允许你完全切换到一个不同的框架。Spring Framework 不强制使用它,它不是一个全有或全无的解决方案。现有的前端 Struts, Tapestry, JSF 或其他 UI 框架,可以集成一个基于 Spring 中间件,它允许你使用 Spring 事务的功能。你只需要将业务逻辑连接使用 ApplicationContext 和使用WebApplicationContext 集成到你的 web 层。
Figure 2.4. Remoting usage scenario
当你需要通过 web 服务访问现有代码时,可以使用 Spring 的 Hessian-, Burlap-, Rmi- 或 JaxRpcProxyFactory 类。启用远程访问现有的应用程序并不难。
Figure 2.5. EJBs - Wrapping existing POJOs
Spring Framework 也提供了 Enterprise JavaBeans [访问和抽象层](../VI. Integration/23. Enterprise JavaBeans integration.md) 使你能重用现有的 POJOs,并且在需要声明安全的时候打包他们成为无状态的 bean 用于可伸缩,安全的 web 应用里。
依赖管理和依赖注入是不同的概念。为了让 Spring 的这些不错的功能运用到运用程序中(比如依赖注入),你需要收集所有的需要的库(JAR文件),并且在编译、运行的时候将它们放到你的类路径中。这些依赖不是虚拟组件的注入,而是物理的资源在文件系统中(通常)。依赖管理过程包括定位这些资源,存储它们并添加它们到类路径。依赖可以是直接(如我的应用程序运行时依赖于 Spring ),或者是间接(如我的应用程序依赖 commons-dbcp ,而 commons-dbcp 又依赖于 commons-pool)。间接的依赖也被称为 “transitive (传递)”,它是最难识别和管理的依赖。
如果你将使用 Spring,你需要复制哪些包含你需要的 Spring 功能的 jar包。为了使这个过程更加简单,Spring 被打包为一组模块,这些模块尽可能多的分开依赖关系。例如,如果不想写一个 web 应用程序,你就不需要引入 Spring-web 模块。为了在本指南中标记 Spring 库模块我们使用了速记命名约定 spring-* 或者 spring-.jar ,其中代表模块的短名(比如 spring-core,spring-webmvc, spring-jms 等)。实际的jar 文件的名字,通常是用模块名字和版本号级联(如 spring-core-4.3.0.RELEASE.jar)
每个 Spring Framework 发行版本将会放到下面的位置:
spring-*-<version>.jar
, Maven groupId 是 org.springframework
所以首先,你要决定用什么方式管理你的依赖,通常建议你使用自动系统像 Maven, Gradle 或 Ivy, 当然你也可以下载 jar
下面是 Spring 中的组件列表。更多描述,详见2.2. Modules (模块)
Table 2.1. Spring Framework Artifacts
GroupId | ArtifactId | Description |
---|---|---|
org.springframework |
spring-aop |
Proxy-based AOP support |
org.springframework |
spring-aspects |
AspectJ based aspects |
org.springframework |
spring-beans |
Beans support, including Groovy |
org.springframework |
spring-context |
Application context runtime, including scheduling and remoting abstractions |
org.springframework |
spring-context-support |
Support classes for integrating common third-party libraries into a Spring application context |
org.springframework |
spring-core |
Core utilities, used by many other Spring modules |
org.springframework |
spring-expression |
Spring Expression Language (SpEL) |
org.springframework |
spring-instrument |
Instrumentation agent for JVM bootstrapping |
org.springframework |
spring-instrument-tomcat |
Instrumentation agent for Tomcat |
org.springframework |
spring-jdbc |
JDBC support package, including DataSource setup and JDBC access support |
org.springframework |
spring-jms |
JMS support package, including helper classes to send and receive JMS messages |
org.springframework |
spring-messaging |
Support for messaging architectures and protocols |
org.springframework |
spring-orm |
Object/Relational Mapping, including JPA and Hibernate support |
org.springframework |
spring-oxm |
Object/XML Mapping |
org.springframework |
spring-test |
Support for unit testing and integration testing Spring components |
org.springframework |
spring-tx |
Transaction infrastructure, including DAO support and JCA integration |
org.springframework |
spring-web |
Web support packages, including client and web remoting |
org.springframework |
spring-webmvc |
REST Web Services and model-view-controller implementation for web applications |
org.springframework |
spring-webmvc-portlet |
MVC implementation to be used in a Portlet environment |
org.springframework |
spring-websocket |
WebSocket and SockJS implementations, including STOMP support |
虽然 Spring 提供了集成在一个大范围的企业和其他外部工具的支持,它故意保持其强制性依赖关系降到最低:在简单的用例里,你无需查找并下载(甚至自动)一大批 jar 库来使用 Spring 。基本的依赖注入只有一个外部强制性的依赖,这是用来做日志的(见下面更详细地描述日志选项)。
接下来我们将一步步展示如果配置依赖 Spring 的程序,首先用 Maven 然后用 Gradle 和最后用 Ivy。在所有的情况下,如果有不清楚的地方,查看的依赖性管理系统的文档,或看一些示例代码。Spring 本身是使用 Gradle 来管理依赖的,我们的很多示例也是使用 Gradle 或 Maven。
译者注:有关 Gradle 的使用,可以参见笔者的另外一部作品《Gradle 2 用户指南》
如果您使用的是 Maven 的依赖管理你甚至不需要明确提供日志依赖。例如,要创建一个应用程序的上下文和使用依赖注入来配置应用程序,你的Maven 依赖将看起来像这样:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.0.RELEASE</version>
<scope>runtime</scope>
</dependency>
</dependencies>
就是它。注意 scope 可声明为 runtime (运行时)如果你不需要编译 Spring 的API,这通常是基本的依赖注入使用的案例。
以上与 Maven Central 存储库工程实例。使用 Spring Maven 存储库(如里程碑或开发者快照),你需要在你的 Maven 配置指定的存储位置。如下:
<repositories>
<repository>
<id>io.spring.repo.maven.release</id>
<url>http://repo.spring.io/release/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
里程碑:
<repositories>
<repository>
<id>io.spring.repo.maven.milestone</id>
<url>http://repo.spring.io/milestone/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
以及快照:
<repositories>
<repository>
<id>io.spring.repo.maven.snapshot</id>
<url>http://repo.spring.io/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
有可能不小心使用不同版本的 Spring JAR 在使用 Maven 时。例如,你可能发现一个第三方的库,或另一 Spring 的项目,拉取了一个在传递依赖较早的发布包。如果你自己忘记了显式声明一个直接依赖,各种意想不到的问题出现。
为了克服这些问题,Maven 支持 "bill of materials" (BOM) 的依赖的概念。你可以在你的 dependencyManagement 部分引入 spring-framework-bom 来确保所有 spring依赖(包括直接和传递的)是同一版本。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
使用 BOM 后,当依赖 Spring Framework 组件后,无需指定<version>
属性
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependencies>
用 Gradle 来使用 Spring ,在 repositories 中填入适当的 URL :
repositories {
mavenCentral()
// and optionally...
maven { url "http://repo.spring.io/release" }
}
可以适当修改 URL 从 /release 到 /milestone 或 /snapshot 。库一旦配置,就能声明 Gradle 依赖:
dependencies {
compile("org.springframework:spring-context:4.3.0.RELEASE")
testCompile("org.springframework:spring-test:4.3.0.RELEASE")
}
如果你更喜欢用Ivy管理依赖也有类似的配置选项。
配置 Ivy ,将指向 Spring 的库 添加下面的 resolver(解析器)到你ivysettings.xml:
<resolvers>
<ibiblio name="io.spring.repo.maven.release"
m2compatible="true"
root="http://repo.spring.io/release/"/>
</resolvers>
可以适当修改 root URL 从 /release 到 /milestone 或 /snapshot 。
一旦配置,就能添加依赖,举例 (在 ivy.xml):
<dependency org="org.springframework"
name="spring-core" rev="4.3.0.RELEASE" conf="compile->runtime"/>
虽然使用构建系统,支持依赖管理是推荐的方式获得了 Spring Framework,,它仍然是可下载分发的 zip 文件。
分发的 zip 文件是发布到 Spring Maven Repository (这是为了我们的便利,在下载这些文件的时候你不需要 Maven 或者其他的构建系统)。
下载一个 Zip,在web 浏览器打开 http://repo.spring.io/release/org/springframework/spring ,选择适当的文件夹的版本。下载完毕文件结尾是 -dist.zip,例如, spring-framework-{spring-version}-RELEASE-dist.zip 。分发也支持发布里程碑和快照。
对于 Spring 日志是非常重要的依赖,因为:a)它是唯一的外部强制性的依赖;b)每个人都喜欢从他们使用的工具看到一些输出;c)Spring 结合很多其他工具都选择了日志依赖。应用开发者的一个目标就是往往是有统一的日志配置在一个中心位置为了整个应用程序,包括所有的外部元件。这就更加困难,因为它可能已经有太多选择的日志框架。
在 Spring 强制性的日志依赖 是 Jakarta Commons Logging API(JCL)。我们编译 JCL,我们也使得 JCL Log
对象对 Spring Framework 的扩展类可见。所有版本的 Spring 使用同样的日志库,这对于用户来说是很重要的:迁移就会变得容易向后兼容性,即使扩展 Spring的应用程序。我们这样做是为了使 Spring 的模块之一明确依赖 commons-logging (JCL的典型实现),然后使得其他的所有模块在编译的时候都依赖它。使用 Maven 为例,如果你想知道何处依赖了 commons-logging ,那么就是来自 Spring 的并且明确来自中心模块 spring-core。
关于 commons-logging 的好处是你不需要任何东西就能让你的应用程序程序跑起来。它有一个运行时发现算法,该算法在众所周知的classpath路径下寻找其他的日志框架并且使用它认为适合的(或者你可以告诉它你需要的是哪一个)。如果没有其他的日志框架存在,你可以从JDK(Java.util.logging 或者JUL 的简称)获得日志。在大多数情况下,你可以在控制台查看你的Spring 应用程序工作和日志,并且这是很重要的。
不幸的是,commons-logging 的运行时日志框架发现算法,确实方便了最终用户,但却是有问题的。如果我们能够时光倒流,现在从新开始 Spring 项目并且他使用了不同的日志依赖。第一个选择很可能是Simple Logging Facade for Java ( SLF4J),过去也曾被许多其他工具通过 Spring 使用到他们的应用程序。
主要有两种方法可以关闭commons-logging:
排除 commons-logging,添加以下的内容到 dependencyManagement 部分:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
现在,这个应用程序可能运行不了,因为在类路径上没有实现 JCL API,因此要修复它就必须提供一个新的(日志框架)。在下一节我们将向你展示如何提供另一种实现 JCL,使用 SLF4J 作为例子的另一种实现。
SLF4J 是一个更加简洁的依赖,在运行时相对于 commons-logging 更加的有效因为它使用编译时绑定来代替运行时发现其他日志框架的集成。这也意味着,你不得不更加明确你想在运行时发生什么,并相应的声明它或者配置它。SLF4J 提供绑定很多的常见日志框架,因此你可以选择一个你已经使用的,并且绑定到配置和管理。
SLF4J 提供了绑定很多的常见日志框架,包括 JCL,它也做了反向工作:是其他日志框架和它自己之间的桥梁。因此在 Spring 中使用 SLF4J 时,你需要使用 SLF4J-JCL 桥接替换掉 commons-logging 的依赖。一旦你这么做了,Spring 调用日志就会调用 SLF4J API,因此如果在你的应用程序中的其他库使用这个API,那么你就需要有个地方配置和管理日志。
一个常见的选择就是桥接 Spring 和 SLF4J,提供显示的绑定 SLF4J 到Log4J 上。你需要支持 4 个的依赖(排除现有的 commons-logging):桥接,SLF4J API,绑定 Log4J 和 Log4J 实现自身。在 Maven 中你可以这样做:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.0.RELEASE</version>
<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.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
</dependencies>
这似乎是一个很大的依赖性,其仅仅是为了得到一些日志文件。它的确如此,但它是可选的,它在关于类加载器的问题上应该比 commons-logging 表现的更加的好,特别是当它运行在在一个严格的容器中像OSGi 平台。据说还有一个性能优势是因为绑定是在编译时而不是在运行时。
对于SLF4J用户来说,一个更常见的选择是,使用更少的步骤和产生更少的依赖,那就是直接绑定 Logback。这消除了多余的绑定步骤,因为 Logback 直接实现了 SLF4J,因此你只需要依赖两个库而不是4个(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-core</artifactId>
<version>4.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</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
####Runtime Containers with Native JCL
很多的人在提供 JCL 实现的容器下运行他们的 Spring 应用程序。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 的依赖关系,而不是容器。该选项不是总是开放的,但是有很多其他的建议在公共领域的替代方法,你的里程可能取决于确切的版本和特定的容器。