翻译 Spring Framework Reference Documentation,有增删改
Spring 框架是一个轻量级的解决方案,可以一站式地构建企业级应用。Spring 是模块化的,开发者可以在项目中只使用其中自己需要的部分,例如,可以在任何 web 框架上使用控制反转(IoC),也可以只使用 Hibernate 集成代码或 JDBC 抽象层。它支持声明式事务管理、通过 RMI 或 web 服务实现远程访问,并可以使用多种方式持久化数据。它提供了功能全面的 MVC 框架 ,也可以透明地集成 AOP 到软件中。
Spring 是非侵入式的,这意味着项目的域逻辑代码通常不会依赖于框架本身。在集成层(比如数据访问层)可能会同时依赖于数据访问技术和 Spring,但是这些依赖可以很容易地从代码中分离出来。
Spring框架是基于 Java 平台的,为开发Java应用提供了全方位的基础设施支持,所以你只需要关注你的应用本身。
Spring可以使用 POJO(plain old Java object)创建应用,并且可以将企业服务非侵入式地应用到 POJO。这项功能适用于 Java SE 编程模型以及全部或部分的 Java EE。
那么,Spring 框架对 JAVA 开发项目有那些帮助?
- 不用关心事务 API 就可以执行数据库事务;
- 不用关心远程 API 就可以使用远程操作;
- 不用关心 JMX API 就可以进行管理操作;
- 不用关心 JMS API 就可以进行消息处理。
一. Spring Framework 基本构成
Spring Framework 大致按功能划分为20个模块,并可分为 Core Container、Data Access/Integration、Web, AOP (Aspect Oriented Programming)、Instrumentation、Messaging以及 Test 这7组,如下图所示:
GroupId | ArtifactId | Description |
---|---|---|
org.springframework | spring-aop | 基于代理的AOP支持 |
org.springframework | spring-aspects | 基于AspectJ 的切面 |
org.springframework | spring-beans | Beans 支持,包含 Groovy |
org.springframework | spring-context | 应用上下文运行时,包括调度和远程抽象 |
org.springframework | spring-context-support | 支持将常见的第三方类库集成到 Spring 应用上下文 |
org.springframework | spring-core | Spring 其他模块所依赖的核心模块 |
org.springframework | spring-expression | Spring 表达式语言,SpEL |
org.springframework | spring-instrument | JVM 引导的仪表(监测器)代理 |
org.springframework | spring-instrument-tomcat | Tomcat 的仪表(监测器)代理 |
org.springframework | spring-jdbc | JDBC(Java Data Base Connectivity) 支持包,包括数据源设置和 JDBC 访问支持 |
org.springframework | spring-jms | JMS(Java Message Service) 支持包,包括发送/接收JMS消息的助手类 |
org.springframework | spring-messaging | 对消息架构和协议的支持 |
org.springframework | spring-orm | 对象/关系映射(Object/Relational Mapping,ORM),包括对 JPA 和 Hibernate 的支持 |
org.springframework | spring-oxm | 对象/XML 映射(Object/XML Mapping,OXM) |
org.springframework | spring-test | 单元测试和集成测试支持组件 |
org.springframework | spring-tx | 事务基础组件,包括对 DAO 的支持及 JCA 的集成 |
org.springframework | spring-web | web支持包,包括客户端及web远程调用 |
org.springframework | spring-webmvc | REST web 服务及 web 应用的 MVC 实现 |
org.springframework | spring-webmvc-portlet | 用于 Portlet 环境的MVC实现 |
org.springframework | spring-websocket | WebSocket 和 SockJS 实现,包括对 STOMP 的支持 |
以下章节简要说明各部分内容。
1.1 Core Container - 核心容器
Spring Core Container 包含 spring-core, spring-beans, spring-context, spring-context-support, 以及 spring-expression (Spring Expression Language,Spring 表达式语言,SpEL)模块。
其中,spring-core 和 spring-beans 模块提供了 Spring 框架 的基础功能,包括控制反转(IoC)和依赖注入。BeanFactory 是工厂模式的一种复杂而精致的实现,它使开发者不再需要编码式单例(programmatic singletons),并可将配置和依赖从实际编码逻辑中解耦。
spring-context 模块使得由 Core 和 Beans 提供的基础功能真正构建成坚实的地基:这意味着Spring 工程能以框架模式访问对象,类似于JNDI注册表。Context 模块继承了Beans 模块的特性并增加了对国际化(例如资源绑定)、事件传播、资源加载和context 透明化(例如 Servlet container)。同时,也支持JAVA EE 特性,例如 EJB、 JMX 和 基本的远程访问。Context 模块的关键是 ApplicationContext 接口。spring-context-support 则提供了对第三方库集成到 Spring-context 的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
spring-expression 模块为在运行时查询和操作对象图提供了强大的表达式语言。它是JSP2.1规范中定义的统一表达式语言的扩展,支持 set 和 get 属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。
1.2 Data Access/Integration - 数据访问与集成
数据访问与集成层包含 JDBC、ORM、OXM、JMS和事务模块。
spring-jdbc 模块提供了 JDBC抽象层,它消除了冗长的 JDBC 编码和对数据库供应商特定错误代码的解析。
spring-tx 模块支持编程式事务和声明式事务,可用于实现了特定接口的类和所有的 POJO 对象。编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由 spring 自动处理,编程式事务粒度更细。
spring-orm 模块提供了对流行的对象关系映射 API的集成,包括 JPA、JDO 和 Hibernate 等。通过此模块可以让这些 ORM 框架和 spring 的其它功能整合,比如前面提及的事务管理。
spring-oxm 模块提供了对 OXM 实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
spring-jms 模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了 spring-messaging 模块。
1.3 AOP
spring-aop 模块提供了面向切面编程(AOP)的实现,可以定义诸如方法拦截器和切入点等,从而使实现功能的代码彻底的解耦。使用源码级的元数据,可以用类似于.Net 属性的方式合并行为信息到代码中。
spring-aspects 模块提供了对 AspectJ 的集成。
1.4 Instrumentation
spring-instrument 模块提供了对检测类的支持和用于特定的应用服务器的类加载器的实现。
spring-instrument-tomcat 模块包含了用于 Tomcat 的Spring 检测代理。
1.5 Messaging - 消息处理
从 Spring 4 开始集成了spring-messaging 模块,是从一些 Spring 集成项目的关键抽象中提取出来的,这些项目包括 Message、MessageChannel、MessageHandler 和其它服务于消息处理的项目。这个模块也包含一系列的注解用于映射消息到方法,这类似于Spring MVC 基于编码模型的注解。
1.6 Web
Web 层包括 spring-web、spring-webmvc、spring-websocket、spring-webmvc-portlet 等模块。
spring-web 模块提供面向 web 的基本功能和面向 web 的应用上下文,比如 multipart 文件上传功能、使用 Servlet 监听器初始化 IoC 容器等。它还包括 HTTP 客户端以及 Spring 远程调用中与 web 相关的部分。
spring-webmvc 模块(即 Web-Servlet 模块)为 web 应用提供了模型视图控制(MVC)和 REST Web 服务的实现。Spring 的 MVC 框架可以使领域模型代码和 web 表单完全地分离,且可以与 Spring 框架的其它所有功能进行集成。
spring-webmvc-portlet 模块(即Web-Portlet模块)提供了用于 Portlet 环境的 MVC 实现,并反映了 pring-webmvc 模块的功能。
1.7 Test
spring-test 模块通过 JUnit 和 TestNG 组件支持单元测试和集成测试。它提供了一致性地加载和缓存 Spring 上下文,也提供了用于单独测试代码的模拟对象(mock object)。
二. 使用场景
不管是资源受限的嵌入式应用还是使用了事务管理和 web 集成框架的成熟的企业级应用,Spring 都是一个值得信赖的选择。
2.1 经典的 Spring web 应用
Spring 的声明式事务管理可以使 web 应用完全事务化,就像使用 EJB 容器管理的事务。所有订制的业务逻辑都可用 POJO 实现,并用 Spring 的 IoC 容器进行管理。此外还包括发邮件和验证功能,其中验证功能是从 web 层分离的,由你决定何处执行验证。Spring 的 ORM 可以集成 JPA、hibernate和 JDO 等。比如使用 Hibernate 时,可以继续使用已存在的映射文件和标准的 Hibernate SessionFactory 配置。表单控制器无缝地把 web 层和领域模型集成在一起,移除了 ActionForms 和其它把 HTTP 参数转换成领域模型的类。
2.2 使用第三方 web 框架和 Spring 中间件
一些场景可能不允许你完全切换到另一个框架。然而,Spring 框架不强制你使用它所有的东西,它不是非此即彼(all-or-nothing)的解决方案。前端使用 Struts、Tapestry、JSF 或别的 UI 框架可以和 Spring 中间件集成,从而使用 Spring 的事务管理功能。仅仅只需要使用 ApplicationContext 连接业务逻辑,并使用 WebApplicationContext 集成 web 层即可。
2.3 远程调用使用场景
当需要通过 web 服务访问现有代码时,可以使用Spring的Hessian-,Burlap-,Rmi-或者JaxRpcProxyFactory类,远程访问现有的应用并非难事。
2.4 EJBs - 包装现有的POJO
Spring框架也为 EJB 提供了访问抽象层,可以重新使用现有的 POJO 并把它们包装到无状态的会话 bean 中,以使其用于可扩展的安全的 web 应用中。
三. 依赖管理
依赖管理和依赖注入是两码事。为了让应用程序拥有 Spring 的这些非常棒的功能(如依赖注入),需要在工程中导入所需的jar 包,并在运行时放在classpath 下,有可能的话编译期也应该放在 classpath 下。这些依赖并不是被注入的虚拟组件,而是文件系统上实在的物理资源。依赖管理的处理过程涉及到定位这些资源、存储并添加它们到 classpath 下。依赖可能是直接的(比如运行时依赖于 Spring),也可能是间接的(比如依赖于 commons-dbcp,commons-dbcp 又依赖于 commons-pool)。间接的依赖又被称作“传递”,它们是最难识别和管理的。
如果准备使用 Spring,则需要拷贝一份所需 Spring 模块的 jar 包。为了便于使用,Spring 被打包成一系列的模块以尽可能地减少依赖,比如,如果不是在写一个web应用,那就没必要引入 spring-web 模块。
每个版本的Spring都会在以下地方发布artifact:
- Maven中央仓库,默认的 Maven 查询仓库,并且不需要特殊的配置就可以使用。许多 Spring 依赖的公共库也可以从 Maven 中央仓库获得,并且大部分的 Spring 社区也使用 Maven 作为依赖管理工具,所以很方便。Maven中的jar包命名格式为spring-*-<version>.jar,其groupId是org.springframework。
- 特别托管 Spring 的公共 Maven 仓库。除了最终的 GA 版本,这个仓库也托管了开发的快照版本和里程碑版本。jar 包和 Maven 中央仓库中的命名一致,所以这也是一个获取 Spring 的开发版本的有用地方,可以和 Maven 中央仓库中部署的其它库一起使用。这个仓库也包含了所有 Spring jar 包的发行版的zip文件,以便于下载。
因此,首先要做的事就是决定如何管理依赖关系,我们一般推荐使用成熟的依赖和构建生命周期管理系统,比如 Maven、Gradle 或 Ivy,当然你也可以手动下载所有的jar包。
Spring 对大部分企业和其它外部工具提供了集成和支持,把强制性的外部依赖降到了最低,这样就不需要为了简单地使用 Spring 而去寻找和下载大量的 jar 包了。基本的依赖注入只有一个强制性的外部依赖,那就是日志管理(参考下面关于日志管理选项的详细描述)。
下面列出依赖于 Spring 的应用的基本配置步骤,首先使用 Maven,然后 Gradle,最后 Ivy。在所有案例中,如果有什么不清楚的地方,参考所用的依赖管理系统的文档或查看一些范例代码—— Spring 构建时本身使用 Gradle 管理依赖,所以我们的范例大部分使用 Gradle 或 Maven。
3.1 使用 Maven 管理依赖
如果使用Maven作为依赖管理工具,甚至不需要明确地提供日志管理的依赖。例如,创建应用上下文并使用依赖注入配置应用程序,Maven的依赖关系看起来像下面一样:
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
<scope>runtime</scope>
</dependency>
</dependencies></pre>
就这样,注意如果不需要编译Spring API,可以把scope声明为runtime,这是依赖注入使用的典型案例。
上面的示例使用Maven中央仓库,如果使用Spring的Maven仓库(例如,里程碑或开发快照),需要在Maven配置中指定仓库位置。
release版本:
<pre><repositories>
<repository>
<id>io.spring.repo.maven.release</id>
<url>http://repo.spring.io/release/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories></pre>
里程碑版本:
<pre><repositories>
<repository>
<id>io.spring.repo.maven.milestone</id>
<url>http://repo.spring.io/milestone/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories></pre>
快照版本:
<pre><repositories>
<repository>
<id>io.spring.repo.maven.snapshot</id>
<url>http://repo.spring.io/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories></pre>
使用 Maven 时可能会不小心混合了 Spring 不同版本的 jar 包。例如,你可能会发现第三方库或其它的 Spring 项目存在旧版本的传递依赖。如果没有明确地声明直接依赖,各种各样不可预料的情况将会出现。
为了解决这个问题,Maven 提出了“物料清单式”(Bill Of Materials,BOM)依赖的概念。可以在 dependencyManagement 部分导入 spring-framework-bom 以保证所有的 Spring 依赖(不管直接还是传递依赖)使用相同的版本。
<pre><dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement></pre>
使用 BOM 的另外一个好处是不再需要指定 <version> 属性了:
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependencies></pre>
3.2 使用 Gradle 管理依赖
为了在 Gradle 构建系统中使用 Spring 仓库,需要在 repositories 部分包含合适的 URL:
<pre>repositories {
mavenCentral()
// and optionally...
maven { url "http://repo.spring.io/release" }
}
</pre>
可以酌情把repositories URL中的/release修改为/milestone或/snapshot。一旦仓库配置好了,就可以按Gradle的方式声明依赖关系了。
<pre>dependencies {
compile("org.springframework:spring-context:4.3.9.RELEASE")
testCompile("org.springframework:spring-test:4.3.9.RELEASE")
}</pre>
3.3 使用 Ivy 管理依赖
使用 Ivy 管理依赖关系有相似的配置选项。
在 ivysettings.xml 中添加 resolver 配置使 Ivy 指向 Spring 仓库:
<pre><resolvers>
<ibiblio name="io.spring.repo.maven.release"
m2compatible="true"
root="http://repo.spring.io/release/"/>
</resolvers></pre>
可以酌情把root URL中的/release修改为/milestone或/snapshot。一旦配置好了,就可以按照惯例添加依赖了(在ivy.xml中):
<pre><dependency org="org.springframework"
name="spring-core" rev="4.3.9.RELEASE" conf="compile->runtime"/></pre>
四. 日志
对于 Spring 来说日志管理是非常重要的依赖关系,因为:
- 它是唯一的强制性外部依赖;
- 每个人都喜欢从他们使用的工具看到一些输出;
- Spring 集成了许多其它工具,它们都各自选择了日志管理的依赖。
开发者通常在整个应用程序(包括所有的外部组件)的中心位置统一配置日志管理,这是非常困难的,因为现在有很多日志管理的框架可供选择。
Spring 中强制的日志管理依赖是 Jakarta Commons Logging API(JCL)。我们编译了 JCL,并使 JCL 的 Log 对象对继承了 Spring 框架的类可见。对用户来说所有版本的 Spring 使用相同的日志管理库很重要,因为这使得迁移很简单,因为Spring保存了向后兼容,即使对于扩展了 Spring 的应用也能向后兼容。
这是怎么做到的呢?我们让 Spring 的一个模块明确地依赖于 commons-logging(JCL的典型实现),然后让所有其它模块都在编译期依赖于这个模块。例如,使用 Maven,你想知道哪里依赖了 commons-logging,其实是 Spring 确切地说是其核心模块 spring-core 依赖了。
commons-logging 的优点是不需要其它任何东西就可以使应用程序运转起来。它拥有一个运行时发现算法,用于在 classpath 中寻找其它日志管理框架并且适当地选择一个使用(或者告诉它使用哪个)。如果不需要其它的功能了,你就可以从 JDK(java.util.logging 或 JUL)得到一份看起来很漂亮的日志了。你会发现大多数情况下你的 Spring 应用程序工作得很好且日志很好地输出到了控制台,这很重要。
4.1 禁用 Commons Logging
不幸的是,commons-logging 的运行时发现算法虽然对于终端用户很方便,但存在一定的问题。有两种方式关掉 commons-logging:
- 从 spring-core 模块中去除对 commons-logging 的依赖(因为这是唯一明确依赖于 commons-logging 的地方)
- 依赖于一个特定的 commons-logging 且把其 jar 包换成一个空 jar 包(具体做法参考SLF4J FAQ)
如下,在 dependencyManagement 中添加部分代码就可以排除掉 commons-logging 了:
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.9.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies></pre>
现在这个应用可能是残缺的,因为在classpath上没有 JCL API 的实现,所以需要提供一个新的去修复它。
4.2 使用 Log4j
许多人使用 Log4j 作为日志管理框架。它是高效和完善的,实际上在构建和测试 Spring时我们使用的就是它。Spring 也提供了一些工具用于配置和初始化 Log4j,所以某些模块在编译期也可以选择依赖于 Log4j。
为了使用 Log4j 2.x,开发者需要拷贝相应版本的 Log4j 到 classpath 并提供相应的配置文档(log4j2.xml、log4j2.properties 或 其他支持的配置格式)。对于 Maven 项目而言,最小依赖如下所示:
<pre><dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.6.2</version>
</dependency>
</dependencies></pre>
如果你也想将 SLF4J 日志委托给 Log4j,例如在项目依赖的其他库默认使用 SLF4J 的情况下,还需要以下依赖:
<pre><dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.6.2</version>
</dependency>
</dependencies></pre>
以下是使用 Logfj2.xml 配置输出日志到 console 的例子:
<pre><?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="org.springframework.beans.factory" level="DEBUG"/>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration></pre>
4.3 使用 SLF4J 与 Log4j 或 Logback
SLF4J(Simple Logging Facade for Java )是 Spring 中其他模块经常采用的 API,它通常与 Logback 一块儿使用。后者是 SLF4J API 的一种本地化实现。
SLF4J 提供了与多种其他常见日志框架的绑定桥梁以委托日志输出,包括Log4j。开发者可以决定将SLF4J管理的日志委托给Log4j输出,或者反过来。为了使用 SLF4J,需要在依赖中将 commons-logging 替换成 SLF4J-JCL 桥。此后 Spring 内部的日志调用将转换为调用 SLF4J API,如果应用中的其他依赖也使用了 SLF4J,那么日志就能在一个地方统一管理了。
一个常用的选择是将 Spring 桥接到 SLF4J 后 再显式地将 SLF4J 绑定到 Log4j,那么需要提供以下依赖并排除 commons-logging:JCL 桥、SLF4J到Log4j 的绑定、以及 Log4j 本身。
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.9.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.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies></pre>
另一个常见选择是直接绑定到 Logback。这可以移除其他额外步骤和依赖,因为 Logback 直接实现了 SLF4J API:
<pre><dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
</dependencies></pre>
4.4 使用 JUL (java.util.logging)
如果 classpath 目录下没有Log4j,commons-logging 将默认委托给 java.util.logging,所以在这种情况下无需任何其他配置。
4.5 带有本地 JCL 的运行时容器
许多人在一个容器中运行 Spring 应用程序,而这个容器本身又提供了JCL的实现,例如 IBM 的 Websphere Application Server(WAS)。这本身没什么问题,但要考虑到以下两种不同场景:
- 在 “parent first” 类加载委托模型(WAS即默认如此)下,应用通常选用父容器提供的 commons-logging 版本,委托给 WAS 日志子系统(这个子系统实际上是基于 JUL 的)。而应用本身提供的 JCL 变体(不管是 Spring 默认的 commons-logging 版本还是JCL-over-SLF4J 桥)则被忽略,应用所依赖的其他库所默认的 日志管理框架也同样会被完全忽略。
- 在“parent last” 委托模型(在常规的Servlet容器中既如此,WAS中则需要显式配置)中,应用会默认选用自身配置的日志管理框架。如果本身没有特别配置,则和 “parent first” 场景相同。
总而言之,我们推荐采用 “parent last” 场景。