ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
## 1.3 使用方案 前面描述的模块使得 Spring 可以在很多方案中作为业务逻辑实现的选择,从 applet 到 使用了 Spring 的事务管理功能和 Web 框架整合,功能完善的企业级应用程序。 ![image](img/Image_004.jpg) 典型地功能完善的 Spring Web 应用 Spring 的声明式事务管理特性(11.5 节)使得 Web 应用程序可以全部事务化,就好像 使用了 EJB 容器管理的事务。全部的自定义业务逻辑可以使用简单的 POJO 来实现并且被 Spring 的 IoC 容器来管理。其它的服务包含对发送邮件的支持,验证对 Web 层独立,这可以 让你选择在哪里执行验证规则。Spring 的 ORM 支持对 JPA,Hibernate,JDO 和 iBatis 进行了整合;比如,当使用 Hibernate 时,你可以继续使用已有的映射文件和标准的 Hibernate 的 SessionFactory 配置 。表 单控 制器 无缝 地整 合了 Web 层 和领 域模 型, 也去 除了 ActionForm 或其它为领域模型转换 HTTP 参数的类的需要。 ![image](img/Image_005.jpg) 使用了第三方 Web 框架的 Spring 中间层 有时,开发和运行环境并不允许你完全转换到一个不同的框架中。Spring Framework 不强制你使用其中的部分;它并不是一个所有或没有的方案。已有使用了 WebWork,Struts, Tapestry 或其它 UI 框架构建的前端应用程序也可以使用基于 Spring 的中间层来进行整合, 这 就 允 许 你 去 使用 Spring 的 事 务 特 性 。 你 仅 仅 需 要 做 的 是 给 业 务 逻 辑 装 上 ApplicationContext,对整合的 Web 层使用 WebApplicationContext。 ![image](img/Image_006.jpg) 远程调用使用方案 当你需要通过 Web Service 访问已有的代码时,你可以使用 Spring 的 Hessian-, Burlap-,Rmi-或 JaxPpcProxyFactory 类。对已有的应用程序开启远程访问并不困难。 ![image](img/Image_007.jpg) EJB – 包装已有的 POJO Spring Framework 也提供了对企业级 Java Bean(EJB,译者注)的访问和抽象层(第 21 章),这就可以重用已有的 POJO,可以扩展、包装它们到无状态会话 bean,不安全的 Web 应用程序可能也需要声明式安全。 ### 1.3.1 依赖管理和命名规约 依赖管理和依赖注入是不同的概念。要在应用程序中添加 Spring 的优雅特性(比如依 赖注入),你需要放置所需的类库(jar 文件),并且添加到运行时环境的类路径中,通常在 编译时也是需要这些类库文件的。这些依赖不是注入的虚拟组件,而是文件系统(通常来说 是这样)上的物理资源。依赖管理的过程包括定位资源,存储它们,并将它们添加到类路径 中。依赖可以是直接的(比如应用程序在运行时需要 Spring),或者是间接的(比如应用程 序需要的 commons-dbcp 组件还依赖于 commons-pool 组件)。间接的依赖也被认为是“过 度的”,而且那些依赖本身就难以识别和管理。 如果你决定使用 Spring,那么你需要获得 Spring 的 jar 包,其中要包括你所需要使用的 Spring 的模块。为了使用的便捷,Spring 被打包成各个模块的集合,尽可能地分离其中的相 互依赖,那么如果你不想编写 Web 应用程序,就不需要添加 spring-web 模块的 jar 包。要参 考 Spring 模 块 的 库 , 本 指 南 使 用 了 一 个 可以 速记 的 命 名 规 约 , spring-* 或者 spring-*.jar ,这里的“ * ” 代 表 了 各 模 块 的 短 名 称 ( 比 如 , spring-core , spring-webmvc,spring-jms 等)。实际上 jar 文件命名或许就是这种形式的(参考下 面的示例),但也有可能不是这种情况,通常它的文件名中还包含一个版本号(比如, spring-core-3.0.0.RELEASE.jar)。 通常,Spring 在四个不同的地方发布组件: * 在社区下载点 [http://www.springsource.org/dow nloads/comm unity](http://www.springsource.org/downloads/community)。在这儿你可以找到所 有的 Spring jar 包,它们被压缩到一个 zip 文件中,可以自由下载。这里 jar 包的命名从 3.0 版本开始,都以 org.springframework.*-<version>.jar 格式进行。 * Maven 的中央库,也是 Maven 默认检索的资源库,它并不会检索特殊的配置来使用。 很多 Spring 所依赖的常用类库也可以从 Maven 的中央库中获得,同时 Spring 社区的绝大多数用户都使用 Maven 作为依赖管理工具,这对于他们来说是很方便的。这里的 jar 包的命名规则是 spring-*-<version>.jar 格式的,并且 Maven 里的 groupId 是 org.springframework。 * 企业级资源库(Enterprise Bundle Repository,EBR),这是由 SpringSource 组织运营的, 同时也提供了和 Spring 整合的所有类库。对于所有 Spring 的 jar 包及其依赖,这里也有 Maven 和 Ivy 的资源库,同时这里还有很多开发人员在使用 Spring 编写应用程序时能用到的其它大量常用类库。而且,发布的版本,里程碑版本和开发版本也都部署在这里。 这 里 jar 文 件 的 命 名 规 则 和 从 社 区 下 载 的 (`org.springframework.*-<version>.jar`)一致,并且所依赖的外部类库(不 是来自 SpringSource 的)也是使用的这种“长命名”的形式,并以 com.springsource 作为前缀。可以参考 [FAQ](http://ebr.springsource.com/repository/app/faq) 部分获取更多信息。 * 在 Amazon S3 为开发和里程碑版本发布(最终发布的版本这里也会有)而设置的公共Maven 资源库。Jar 文件的名称和 Maven 中央库是一样的,那么这里是获取 Spring 开发版本的地方,其它的类库是部署于 Maven 中央库的。 那么,首先你要决定的事情是如何管理这些依赖:很多人使用自动化的系统,比如 Maven 和 Ivy,但是你也可以手动下载所有的 jar 文件。当使用 Maven 或 Ivy 获取 Spring 时,之后你就需要决定从哪里来获取它们。通常来说,如果你关注 OSGi 的话,那么就使用 EBR,因为 它对所有的 Spring 依赖兼容 OSGi,比如 Hibernate 和 Freemarker。如果对 OSGi 不感兴趣, 那么使用哪个下载都是可以的,但是他们也各有利弊。通常来讲,为你的项目选择一个或者备用的一个;但是请不要混用。因为相对于 Maven 中央库而言,EBR 组件非常需要一个不同的命名规则,那么这就特别重要了。 表 1.1 Maven 中央库和 SpringSource EBR 资源库的比较 | 特性 | Maven 中央库 | EBR | | --- | --- | --- | | OSGi 的兼容 | 不明确 | 是 | | 组件数量 | 成千上万;所有种类 | 几百个;是 Spring 整合中会用到的 | | 一致的命名规约 | 没有 | 有 | | 命名规约:GroupId | 相异。新组件通常使用域名,比如 org.slf4j。老组件 通常仅仅使用组件名,比 如 log4j。 | 原 始 的 域 名 或 主 包 的 根 路 径 , 比 如 org.springframework | | 命名规约:ArtifactId | 相异。通常是项目或模块 名,使用连字符“-”分隔, 比如 spring-core,log4j。 | 捆绑符号名称,从主包的路径分离,比如 org.springframework.beans。如果 jar 需要修补以保证兼容 OSGi,那么就附加 com.springsource , 比 如 com.springsource.org.apache.log4j | | 命名规约:Version | 相异。很多新的组件使用 m.m.m 或 m.m.m.X(m 是 数字,X 是文本)。老的使 用 m.m。有一些也不是。 顺序是确定的但通常并不 可靠,所以不是严格的可 靠。 | OSGi 版本数字 m.m.m.X,比如 3.0.0.RC3。 文本标识符使用相同的数字值规定版本 的字母顺序。 | | 发布 | 通常是自动通过 rsync 或源码控制更新。项目作者 可以上传独立的 jar 文件到 JIRA。 | 手动(由 SpringSource 控制的 JIRA) | | 质量保证 | 根据政策。精确度是作者的责任。 | 宽泛的 OSGi 清单,Maven POM 和 Ivy 元数据。QA 由 Spring 团队执行。 | | 主机 | Contegix 提供。由 Sonatype 和一些镜像构成。 | 由 SpringSource 的 S3 构成 | | 搜索工具 | 很多 | [http://www.springsource.com/repository](http://www.springsource.com/repository) | | 和 SpringSource 工具整合 | 通过和 Maven 依赖管理的 STS 来整合 | 通过 Maven ,Roo ,CloudFoundry 的STS 进行宽泛整合 | #### 1.3.1.1 Spring 依赖和基于 Spring 尽管 Spring 提供对整合的支持,以及对大量企业级应用及其外部工具的支持,那么它 也有心保持它的强制依赖在一个绝对小的数目上:你不需要为了简单的使用去定位并下载 (甚至是自动地)大量的 jar 来使用 Spring。对于基本的依赖注入那只需要一个必须的外部 依赖,就是日志(可以参考下面关于日志的深入介绍)。 下面,我们来概述一下配置一个基于 Spring 的应用程序所需的基本配置,首先使用 Maven 和 Ivy。在所有的示例中,如果有哪一点不清楚,可以参考你所使用的依赖管理系统 的相关文档,或者参考一些示例代码 – Spring 本身在构建时使用了 Ivy 来管理依赖,而我们 大多数的示例却使用 Maven。 #### 1.3.1.2 Maven 依赖管理 如果你正使用 Maven 来进行依赖管理,那么你就不需要明确地提供日志依赖。比如, 要为应用程序配置创建应用上下文并使用依赖注入特性,那么,你的 Maven 依赖配置文件 可以是这样的: ``` <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.0.0.RELEASE</version> <scope>runtime</scope> </dependency> </dependencies> ``` 就是这么简单。要注意的是,如果你在编译代码时不需要使用 Spring 的 API,那么 scope 元素可以声明为 runtime,这就可以用于典型的依赖注入用例。 在上面的示例中,我们使用了 Maven 中央库中的命名规约,那么它就可以在 Maven 中 央库或 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> ``` 要使用 SrpingSource 的 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/repositor y](http://www.springsource.com/repository),这可以用来搜索和下 载依赖。它也有 Maven 和 Ivy 配置的代码片段,你可以复制下来并粘贴到你所使用的工具中, 很方便使用。 #### 1.3.1.3 Ivy 依赖管理 如果你使用 [Ivy](http://ant.apache.org/ivy) 来管理依赖,那么有一些简单的命名规约和配置选项。 要配置 Ivy 定位到 SpringSource EBR 中,需要添加如下的解析器元素到你的配置文件 ivysettings.xml 中: ``` <resolvers> <url name="com.springsource.repository.bundles.release"> <ivy pattern="http://repository.springsource.com/ivy/bundles/release / [organisation]/[module]/[revision]/[artifact] -[revision].[ext]" /> <artifact pattern="http://repository.springsource.com/ivy/bundles/release / [organisation]/[module]/[revision]/ [artifact]-[revision].[ext]" /> </url> <url name="com.springsource.repository.bundles.external"> <ivy pattern="http://repository.springsource.com/ivy/bundles/externa l/ [organisation]/[module]/[revision]/[artifact]-[revision].[ext]" /> <artifact pattern="http://repository.springsource.com/ivy/bundles/externa l/ [organisation]/[module]/[revision]/[artifact]-[revision].[ext]" /> </url> </resolvers> ``` 上面的 XML 并不是合法的格式,因为行太长了 – 如果你要复制粘贴,那么要移除中部 url 模式部分结尾额外的行。(本文档中已经去除) 一旦 Ivy 配置好去查看 EBR,那么添加依赖就很简单了。只要拉起在资源库浏览器中的 捆绑详细信息页面,你就会发现为你准备好的 Ivy 代码片段,那么就可以将它包含到你的依 赖部分中了。比如(在 ivy.xml 中): ``` <dependency org="org.springframework" name="org.springframework.core" rev="3.0.0.RELEASE" conf="compile->runtime"/> ``` ### 1.3.2 日志 对于 Spring 来说,日志是一个非常重要的依赖,因为 a)这是唯一强制的外部依赖,b) 开发人员都会想看到他们所使用的工具的一些输出内容,而且 c)Spring 整合了多种实用工具, 它们都会选择一种日志依赖包。应用程序开发人员的目标之一就是在核心位置对整个应用程 序有一个统一的日志配置,包括对所有的外部组件。因为日志框架有多种选择,那么这就可 能有些难以确定了。 Spring 中强制的日志依赖包是 Jakarta 的 Commons Logging API(JCL)。我们对 Spring 的 编译是基于 JCL 的,而且我们使扩展了 Spring Framework 的类对 JCL 包的 Log 对象都是可见 的。对于用户来说,所有 Spring 的版本都使用相同的日志包也是很重要的:因为保留了向 后兼容的特性,那么迁移是很容易进行的,同理,扩展 Spring 的应用程序也是这样。我们 这样做就是使得 Spring 中的模块明确地使用基于 commons-logging(JCL 的典型实现)的 日志实现,在编译时也使得其它模块都基于这个日志包。如果你正在使用 Maven 的话,同 时 想知道 在 哪儿 获 取 到的 commons-logging 依赖 ,那就是从 Spring 中被称作是 spring-core 的核心模块中获取的。 关于 commons-logging 比较好的做法是你不需要做其它的步骤就可以使应用程序运 行起来。它有一个运行时的查找算法,在我们都知道的类路径下寻找其它的日志框架,并且 使用 Spring 认为是比较合适的(或者告诉 Spring 你需要使用的具体是哪一个)一个。如果 没有找到可用的,那么你会得到一个来自 JDK(java.util.logging 或者简称为 JUL)本身看起来 还不错的日志依赖。那么,你会发现在很多时候,Spring 应用程序运行中会有日志信息打印 到控制台上,这点是很重要的。 #### 1.3.2.1 不使用 Commons Logging 不幸的是,commons-logging 的运行时查找算法对最终用户方便是有些问题的。如 果我们可以让时光倒流并让 Spring 从现在开始作为一个新的项目进行,那么我们会使用不 同的日志依赖。首选的日志依赖可能就是 Java 简单的日志门面([SLF4 J](http://www.slf4j.org/)),SLF4J 也被 Spring 的开发人员在他们其它应用程序中的很多工具所使用。 关闭 commons-logging 也很简单:仅仅要确认在运行时,它的包不在类路径下就可 以了。在 Maven 中要去掉这个依赖,因为 Spring 依赖声明的时候会带进来,那么就需要这 么来进行。 ``` <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.0.0.RELEASE</version> <scope>runtime</scope> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> </dependencies> ``` 目前这个应用程序可能就不能运行了,因为在类路径中已经没有了 JCL API 的实现了, 所以要修复这个问题的话,就要提供另外一种日志的实现了。在下一节中,我们来说明如何 提供一个其它的 JCL 的实现,这里,我们使用 SLF4J 作为示例。 #### 1.3.2.2 使用 SLF4J SLF4J 是一个整洁的日志依赖,在运行时的效率也比 commons-logging 更高,因为 SLF4J 使用了编译时构建,而不是运行时去查找其它整合的日志框架。这也就意味着你可以 更明确地在运行时去做些什么,并且去声明或配置。SLF4J 为很多通用的日志框架提供的绑 定,所以通常你可以选择已有的一个,并去绑定配置或管理。 SLF4J 为很多通用日志框架提供绑定,包括 JCL,并且也可以反向进行:桥接其它的日志 框架和 SLF4J 本身。所以如果要在 Spring 中使用 SLF4J,那么就需要使用 SLF4J-JCL 桥来代替 commons-logging 依赖。只要配置好了,那么来自 Spring 的日志调用就会被翻译成调用 SLF4J 的 API 了,如果在应用程序中的其它类库使用了原有的 API,那么会有一个单独的地方 来进行配置和管理的日志。 一个常用的选择就是桥接 Spring 到 SLF4J API,之后提供从 SLF4J 到 Log4J 的明确绑定。 你需要提供 4 种依赖(并排除已经存在的 commons-logging 依赖):桥接工具,SLF4J 的 API,绑定到 Log4J 的工具,还有 Log4J 本身的实现。那么在 Maven 的配置文件中,你就可 以这样来做: ``` <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.0.0.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.5.8</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.5.8</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.5.8</version> <scope>runtime</scope> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.14</version> <scope>runtime</scope> </dependency> </dependencies> ``` 这样,看起来好像很多依赖都需要日志了。情况确实是这样,但 SLF4J 是可选的,而且 它的性能要比含有类加载器问题的 commons-logging 依赖好很多,尤其是如果你使用了 一个严格限制的容器,比如 OSGi 平台。据称那也会有性能优势,因为绑定是编译时进行的, 而不是在运行时。 另外,在 SLF4J 的用户中一个较为常见的选择是使用很少步骤,同时产生更少的依赖, 那也就是直接绑定到 [Logback](http://logback.qos.ch/) 上。这会去掉很多额外的绑定步骤,因为 Logback 本身直接实 现了 SLF4J , 这 样 的 话, 你 就仅 需 要两个依赖的类库 ,而不 原先 的 是四 个 了( 就是 jcl-over-slf4j 和 logback)。如果你也确实那么来做了,你可能还需要从其它外部依 赖(而不是 Spring)中去掉 slf4j-api 的依赖,因为在类路径中只保留一个 API 的一个版本就 行了。 #### 1.3.2.3 使用 Log4J 很多用户出于配置和管理的目的而使用 [Log4j](http://logging.apache.org/log4j) 作为日志框架。这样也同样很有效率并且 易于创建,而且,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.0.0.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 ``` 运行时容器和本地的 JCL 很多用户在容器中运行 Spring 的应用程序,而该容器本身也提供了 JCL 的实现。比如 IBM 的 Webshphere 应用服务器(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 的依赖。这种选择并不总是开 放的,但是在公共领域中的替代方法也有其它的建议,根据确切的版本和容器的特性集,您所要求的效果可能会有不同。