2019独角兽企业重金招聘Python工程师标准>>>
I.Spring框架概览
Spring框架是一个轻量级解决方案,也是构建企业级应用的潜在一站式服务。然而,Spring的模块化允许你只使用你需要的部分,而不必全部引入。你可以使用IOC容器搭配其他web框架,也可以集成Hibernate或JDBC抽象层。Spring框架支持声明式事务管理,和通过RMI或web services进行远程访问,以及持久化数据的多种选择。它还提供了一个全功能的MVC框架,同时支持透明的AOP配置。
Spring的设计是非侵入式的,意味着在集成层(如数据访问层)你本身的逻辑代码通常不用依赖框架本身,但对数据访问技术和Spring库的一些依赖不可避免。然而这些依赖可以同你的逻辑代码轻易地隔离开。
本文档是Spring框架功能的参考指南。如果你对本文档有任何要求,意见或问题,可以发送到用户邮箱列表。关于框架的问题可以访问StackOverflow(https://spring.io/questions);
1.Spring入门
这篇参考指南提供了关于Spring框架的详细信息,是包含所有功能的综合文档,以及Spring所拥护的基本概念(如依赖注入)的一些背景知识。
如果你刚接触Spring,可以通过创建一个Spring Boot应用来开始使用Spring框架。Spring Boot提供了一种快速的(和全配置的)方式来创建一个生产级的Spring应用。它基于Spring框架,遵循一些配置惯例,旨在让你的应用快速运行起来。
你可以使用start.spring.io来生成一个基本的工程,或者参考入门指南中一个,比如构建RESTful Web Service入门。除了很容器理解,这些指南都是任务方式的,其中大部分也都是基于Spring Boot的。它们还涵盖了Spring解决方案中你可能考虑解决特定问题的其他项目。
2.Spring框架简介
Spring框架是一个Java平台,为开发Java应用程序提供全面的基础设施支持。Spring接管了基础设施,以便你可以专注于你的应用。
Spring使你能够使用普通的Java对象(POJO)构建应用程序,并将企业服务非侵入式地应用于POJO。此功能适用于Java SE编程模型以及完整和部分Java EE。
作为一个应用开发着,你可以从Spring平台受益以下内容:
- 在数据库事务中执行一个java方法,而不用处理事务APIs。
- 让一个java本地方法进行远程调用,而不用处理远程APIs。
- 管理一个java本地方法,而不用处理JMX APIs。
- 执行一个java本地方法发送消息,而不用处理JMS APIs。
2.1 依赖注入和控制反转
Java应用程序——从受限的嵌入式应用程序到n层服务器端企业应用程序运行范围的宽松术语通常由协作形成应用程序的对象组成。因而应用程序中的对象互相依赖。
虽然Java平台提供了丰富的应用程序开发功能,但它缺乏将基本组织成整体的方法,将该任务留给架构师和开发人员。虽然你可以使用工厂(Factory),抽象工厂(Abstract Factory),建造者(Builder),装饰器(Decorator),服务定位(Service Locator)等设计模式来组合构成应用程序的各种类和对象实例,但这些模式只有简单的说明:模式的名称,描述,应用于哪里以及能解决哪种问题等等。而你必须自己在应用中将模式的最佳实践付诸实施。
Spring框架控制反转(IOC)组件提供将不同组件组成完整的可使用的应用程序的实践方法。 Spring框架将形式化的设计模式作为首要的对象集成到应用程序进行编译。许多组织机构以这种方式使用Spring框架来设计强大的可维护的应用程序。
背景知识 Martin Fowler2004年在他的网站中提出关于控制反转(IOC)的问题:"问题是,反转的是什么方面的控制"。Fowler建议重新命名规则,使其更加自明,并提出依赖注入。
2.2 模块
Spring框架由大约20个模块功能组成,这些模块可以分成核心容器(Core Container),数据访问/集成(Data Access/Integration),Web,AOP(Aspect Oriented Programming),设备(Instrumentation)、,消息发送(Messaging),和测试(Test)几个部分,具体如下图所示。
以下部分列出了每个功能的可用模块同产品名称及其涵盖的主题。产品名称与依赖管理工具(Dependency Management tools)的产品id相关联。
2.2.1 核心容器
核心容器由spring-core
,spring-beans
,spring-context
,spring-context-support
,和spring-expression
(Spring表达式语言)模块组成。
spring-core
和spring-beans
模块提供了框架的基础部分,包含控制反转和依赖注入。BeanFactory
是工厂模式的一个复杂实现。它消除了对单例编程化的需要并允许你将依赖关系的配置和规范与实际程序逻辑相分离。
上下文(spring-context
)模块建立在由Core和Beans模块提供的实体基础上:它是以类似JNDI注册表的框架式方式访问对象的一种手段。上下文模块从Beans模块继承其功能,并增加对国际化的支持(如使用资源组),事件传播,资源加载和上下文的透明创建,比如Servlet容器。上下文模块也支持JavaEE的功能,诸如EJB,JMX和基本远程调用。ApplicationContext接口是上下文模块的焦点。spring-context-support
支持常见的第三方库集成到Spring应用程序环境中,特别是缓存(EhCache,JCache)和调度(CommonJ,Quartz)。
spring-expression
模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中规定的统一表达语言(统一EL)的扩展。该语言支持设置和获取属性值,属性分配,方式调用,访问数组,集合和索引器的内容,逻辑和算术运算符,命名变量以及从Spring的IOC容器中以名称检索对象。它还支持列表投影和选择以及常见列表聚合。
2.2.2 AOP和设备
spring-aop
模块提供了符合AOP联盟的面向切面编程的实现,允许你定义方法拦截器和切入点,以便干净地解除那些实现了应该分离的功能的代码。使用源码级元数据功能,你还可以将行为信息合并到代码中,类似于.NET的属性的方式。
单独的spring-aspects
模块提供与AspectJ的集成。
spring-instrument
模块提供了类级别的设备支持和可以在某些应用服务里使用的类加载器级别的实现。spring-instrument-tomcat
模块包含了Tomcat的spring设备代理。
2.2.3 消息发送
Spring4框架包含一个spring-messaging
模块,其中包含来自Spring Integration项目(如Message
,MessageChannel
,MessageHandler
)的关键抽象,以及基于消息的应用的基础的其他部分。模块还包含了映射消息到方法的注解集合,类似Spring MVC基于编程方式的注解。
2.2.4 数据访问/集成
数据访问/集成层包含了JDBC、ORM、OXM、JMS和事务模块。
spring-jdbc
模块提供了JDBC抽象层,不再需要处理繁杂的JDBC编码以及数据库供应商特定错误码的解析。
spring-tx
模块支持为实现指定接口的类以及所有POJOs(普通Java对象)的编程和声明式事务管理。
spring-orm
模块为流行的对象关系映射APIs提供集成层,包括JPA和Hibernate。通过spring-orm
模块,你可以将O/R映射框架组合Spring提供的其他功能一起使用,比如前面提及的简单声明式事务管理功能。
spring-oxm
模块为Object/XML映射实现提供抽象层,诸如JAXB,Castor,JiBX和XStream。
spring-jms
模块(Java消息服务)包含生产和消费消息的功能。从Spring框架4.1版本开始,它提供与spring-messaging
模块的集成。
2.2.5 Web
Web层包含spring-web
,spring-webmvc
和spring-websocket
模块。
spring-web
模块提供基础的面向web的集成功能,支持多文件上传功能,以及通过Servlet监听器和面向web的应用上下文初始化IOC容器。同时还包含HTTP客户端和Spring远程支持的web相关部分。
spring-webmvc
模块(也称为Web-Servlet模块)包含支持web应用的Spring模型-试图-控制(MVC)和REST风格的web服务实现。Spring的MVC框架提供了域模型代码(domain model code)和Web表单的清晰分离,并与Spring框架所有其他功能相集成。
2.2.6 测试
spring-test
模块支持使用JUnit或TestNG进行Spring组件的单元测试或集成测试。它提供了Spring ApplicationContexts的一致加载和缓存。同时提供用于隔离测试的模拟对象(mock objects)。
2.3 使用场景
前面描述的构件块使得Spring成为许多场景中的合理选择,从在资源受限设备上运行的嵌入式应用程序到使用Spring事务管理功能和Web框架集成的全面的企业应用程序。
图2.2 典型的成熟的Spring web应用程序
Spring的声明式事务管理功能支持web应用程序全事务化,就同你使用EJB容器管理的事务一样。所有你的定制业务逻辑都可以由简单的POJOs实现,并由Spring IoC容器管理。其他服务包括发送email和独立于web层的校验,而你可以选择何处去执行校验规则。Spring的ORM支持同JPA和Hibernate的整合,比如,当你使用Hibernate时,可以保持原有的映射文件及标准Hibernate SessionFactory
配置。表单控制器无缝整合了web层和域模型,无需那些转换HTTP参数到域模型的ActionForms
或其他类。
图2.3 Spring中间层使用第三方web框架
有时情况并不允许你完全切换到一个不同的框架。Spring框架不是一个要么使用全部特性要么什么都用不了的解决方案,不强制使用其中的每个功能。现存的前端如Struts,Tapestry,JSF或其他UI框架都可以同基于Spring的中间层整合在一起,从而使你能过使用Spring事务功能。你只需要使用ApplicationContext
连接你的业务逻辑以及通过WebApplicationContext
整合你的web层。
图2.4 远程调用使用场景
你可以使用Spring的Hessian-
,Rmi-
或HttpInvokerProxyFactoryBean
类来通过web服务访问现存的代码。远程访问现存应用程序并不困难。
图2.5 EJBs-包装现存POJOs
Spring框架还为企业JavaBeans提供了一个访问抽象层,使你能够重用现有的POJO,并将其包装在无状态会话bean中,以便在可能需要声名式安全的可扩展,故障安全的web应用程序中使用。
2.3.1 依赖管理和命名约定
依赖管理不同于依赖注入。要在你的应用程序中使用Spring的这些功能(比如依赖注入),需要 组合需要的库(jar包),并在运行时将其置于classpath下,编译时也尽可能这样做。这些依赖并不是要注入的虚拟组件,而是文件系统中的资源(通常情况下)。依赖管理的过程包括资源定位,资源存储并将其置于classpath。依赖可能是直接的(比如运行时应用依赖于Spring的)和间接的(如应用中依赖commons-dbcp,而它又依赖于commons-pool)。间接的依赖往往是“过渡的”,而这些依赖是最难去识别和管理的。
如果你将要使用Spring,你需要拷贝一份包含你所需要的Spring部分的jar包。为了让这种操作更加容易,Spring被打包成一组模块,并且尽可能地分离依赖关系,因而比如当你不想写一个web应用程序时,就不必用spring-web模块。在本指南中引用Spring库模块,我们使用简要命名约定spring-*
或者spring-*.jar
,*代表模块的简写(如spring-core
,spring-webmvc
,spring-jms
等)。你使用的真实jar包文件名通常是模块名拼上版本号(如spring-core-5.0.0.M5.jar)。
Spring框架的每个release版都会发布到以下地方:
- Maven中心库,Maven查询的默认库,不需要额外的配置就能使用。许多Spring依赖的常用库在Maven中心也是可用的,且很多Spring社区使用Maven作为依赖管理工具,因此非常方便。在这里jar包的名称是以
spring-*-
的形式,而Maven groupId是.jar org.springframework
。 - 在专门用于Spring的公共Maven存储库中。此外最终GA发布版,资源库还存储开发快照和里程碑版本。jar包的文件名称同Maven中心库一致,因而可以获取Spring开发版本来搭配部署在Maven中心库的其他库一起使用。资源库也提供包含捆绑Spring所有jar包方便下载的捆绑分发zip文件。
因此你需要决定的第一件事就是怎么管理你的依赖:我们通常推荐使用自动化系统,比如Maven,Gradle或Ivy,但是你也可以手工下载所有jar包。
你将在下面找到Spring的工件列表。更多详细的模块描述,请见2.2节模块。
表2.1.Spring框架工件
|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表达式语言 | | org.springframework | spring-instrument | JVM自带的设备代理 | | org.springframework | spring-instrument-tomcat | Tomcat的设备代理 | | org.springframework | spring-jdbc | JDBC支持包,包括数据源启动和JDBC访问支持 | | org.springframework | spring-jms | JMS支持包,包括发送和接收JMS消息的帮助类 | | org.springframework | spring-messaging | 支持消息架构和协议 | | org.springframework | spring-orm | 对象关系映射,包含JPA和Hibernate支持 | | org.springframework | spring-oxm | 对象和XML映射 | | org.springframework | spring-test | Spring组件的单元测试和集成测试支持 | | org.springframework | spring-tx | 事务基础设施,包含DAO支持和JCA整合 | | org.springframework | spring-web | Web支持包,包含客户端和web远程调用 | | org.springframework | spring-webmvc | REST风格web服务和web应用的MVC实现 | | org.springframework | spring-websocket | WebSocket和SockJS的实现,包含STOMP支持 |
Spring依赖和依赖于Spring的
尽管Spring提供了大量企业和其他外部工具的整合和支持,但是还是有意将强制性的依赖减少到最小:你不必为了使用Spring创建简单的用例而寻找和下载大量的jar包(尽管是自动的)。基本的依赖注入只有一个强制的外部依赖,就是日志(关于日志的详细描述见下方)。
接下来我们概括一下依赖于Spring的应用配置的基本步骤。首先是使用Maven,然后是Gradle,最后是Ivy。任何情况下,如果有不清楚的地方,参考依赖管理系统的文档,或者一些样例代码。Spring自身使用Gradle管理依赖来构建,而我们的样例大多数使用Gradle或Maven。
Maven依赖管理
如果你使用Maven作为依赖管理,不必明确地提供日志依赖。比如,创建一个应用上下文并使用依赖注入配置一个应用程序,你的Maven依赖应该像这样:
org.springframework
spring-context
5.0.0.M5
runtime
就是这样。注意如果你不需要像通常基本依赖注入的用例一样,编译Spring API,就可以将scope声明为runtime。
Maven的中心库支持上面的例子。如果要使用Spring Maven库(如里程碑或开发快照版本),你需要在Maven配置中指定资源库的位置。支持所有release:
io.spring.repo.maven.release
http://repo.spring.io/release/
false
支持里程碑:
io.spring.repo.maven.milestone
http://repo.spring.io/milestone/
false
支持快照:
io.spring.repo.maven.snapshot
http://repo.spring.io/snapshot/
true
Maven“物料清单”依赖
使用Maven时意外混合Spring的多个版本是很有可能的。比如,你可能发现一个第三方库或其他的Spring工程,引用了一个旧版本。如果你忘记明确地声明依赖版本,各种意想不到的问题就会出现。
为了解决此类问题,Maven提供了“物料清单”(BOM)依赖的概念。你可以在dependencyManagement
块中导入spring-framework-bom
,来确保所有spring依赖(直接或间接的)都处于同一版本中。
org.springframework
spring-framework-bom
5.0.0.M5
pom
import
使用BOM的另外的好处是当你依赖于Spring框架时,不必再指定version属性。
org.springframework
spring-context
org.springframework
spring-web
Gradle依赖管理
通过Gradle构建系统使用Spring资源库,在repositories
块中设置适当的URL:
repositories {
mavenCentral()
// 可选的
maven { url "http://repo.spring.io/release" }
}
你可以根据需要改变repositories
的URL,从/release
到/milestone
或/snapshot
。资源库配置后,你可以使用通常的Gradle方式声明依赖:
dependencies {
compile("org.springframework:spring-context:5.0.0.M5")
testCompile("org.springframework:spring-test:5.0.0.M5")
}
Ivy依赖管理
如果你喜欢使用Ivy管理依赖,也是相似的配置操作。
在你的ivysettings.xml
中添加如下的resolver来配置Ivy指定Spring资源库:
你可以根据需要改变root
URL从/release
到/milestone
或/snapshot
。
配置后,按照通常的方式添加依赖。如下ivy.xml
:
分发Zip文件
尽管使用支持依赖管理的构建系统来获取Spring是推荐的方式,然而也可以直接下载分发的Zip文件。
分发zip包被发布在Spring的Maven库中。(这只是我们图方便的方式,不必通过Maven或其他构建系统来下载它)。
要下载一个分发zip包,打开浏览器访问http://repo.spring.io/release/org/springframework/spring ,选择适合版本的子文件夹。分发文件以-dist.zip
结尾,比如spring-framework-{spring-version}-RELEASE-dist.zip。发布的也包括里程碑和快照版本。
2.3.2 日志
日志对于Spring是一个非常重要的依赖,因为a)它是唯一强制性的外部依赖,b)每个人都喜欢看到他们使用的工具有一些输出,c)Spring整合的许多其他工具也需要选择日志依赖。应用开发者的目标之一往往就是为整个应用再某个中央位置统一日志配置,包括所有外部的组件。由于太多的日志框架因而常常比较困难。
Spring强制的日志依赖是Jakarta Commons Logging API (JCL)。我们编译JCL并使JCL的Log对象对于继承Spring框架的类可见。Spring所有版本使用相同的日志库对于用户来说非常重要:迁移很容易,因为继承自Spring的应用程序都保留向后的兼容性。我们要做的就是使Spring的一个模块明确地依赖commons-logging
(JCL的规范实现),然后其他所有的模块在编译期依赖于这个模块。假如你正在使用Maven,想知道哪里依赖了commons-logging
,那么它明确地来自Spring的核心模块spring-core
。
commons-logging
很棒的是你不需要做任何事就可以让你的应用跑起来。它有一个运行时发现算法,来寻找classpath上约定位置上的其他日志框架并选择一个适宜的来使用(或者你可以指定一个你需要的)。如果没有找到可用的,默认会使用JDK(java.util.logging,简称JUL)生成好看的日志。你会发现你的Spring应用工作起来,而且大多数情况下控制台都输出了日志,这通常是很重要的。
不使用Commons Logging
不幸的是,commons-logging
的运行时发现算法虽然对终端用户很方便,但是是有问题的。如果时间可以倒流,重新再做Spring项目的话,将会使用另外一个日志依赖。而首选的可能就是Simple Logging Facade for Java ( SLF4J),人们在应用中同Spring一起使用的许多其他工具也使用它。
有两种方式可以替换commons-logging:
- 从
spring-core
模块中排除这个依赖(因为这是唯一明确依赖commons-logging
的模块) - 将资源库中的
commons-logging
替换成一个空的jar包,然后依赖这个特殊的commons-logging
(详细见SLF4J FAQ)
要排除commons-logging,在你的dependencyManagement
块中增加以下内容:
org.springframework
spring-core
5.0.0.M5
commons-logging
commons-logging
现在应用因为classpath下没有JCL API的实现而可能无法启动,因而需要一个新的日志框架。下一节我们将使用SLF4J作为例子展示如何提供一个可选的JCL实现。
使用SLF4J
SLF4J相较于commons-logging
在运行时更干净也更高效,因为它使用编译期绑定来集成其他日志框架,替换了运行时发现的方式。这也就意味着你必须明确地清楚在运行时你想要的,然后相应地声明或配置。SLF4J提供了对许多常用的日志框架的绑定,因而你往往可以选择之前使用的来绑定,从而得到配置和管理。
SLF4J提供与许多常用日志框架的绑定,包括JCL,并且还提供了相反的方式:介于其他日志框架和自己的桥梁。因此在Spring使用SLF4J你需要用SLF4J-JCL桥梁来替代commons-logging
依赖。然后Spring内部的日志调用将会被转换为SLF4J API的日志调用,因此如果应用中的其他库也使用这个API,你就可以在一个唯一的地方来配置和管理日志。
一个常用的选择是连接Spring和SLF4J,并提供从SLF4J到Log4j的明确绑定。你需要提供几个依赖(以及排除已存在的commons-logging
):Log4j的SLF4J实现桥梁和Log4j实现自己。Maven中你可以配置如下:
org.springframework
spring-core
5.0.0.M5
commons-logging
commons-logging
org.slf4j
jcl-over-slf4j
1.7.22
org.apache.logging.log4j
log4j-slf4j-impl
2.7
org.apache.logging.log4j
log4j-api
2.7
org.apache.logging.log4j
log4j-core
2.7
看上去获取一些日志需要配置很多依赖。是的,但是可选的。对于类加载器问题,它应该比vanilla commons-logging
更好,特别是如果你处于像OSGi平台这样的严格容器中。 据称它有更好的性能,因为它的绑定作用于编译期而非运行时。
一种相对于SLF4J用户更常用的选择是直接绑定到Logback,使用更少的步骤和产生更少的依赖。由于Logback直接实现了SLF4J,所以移除了额外的绑定步骤,你只需要依赖于两个库而不再是四个(jcl-over-slf4j
和logback
)。如果你这样做,你需要从其他外部的依赖(非Spring)排除slf4j-api依赖,因为你希望classpath上的API只有一个version。
使用Log4j
Log4j 1.x项目结束了,接下来应用的是Log4j 2
很多人使用Log4j作为配置和管理日志的框架。它高效且成熟,实际上我们在编译和测试Spring的运行时也在使用。Spring也提供配置和初始化Log4j的一些工具,在一些模块有可选的Log4j编译期依赖。
想通过JCL使用Log4j,所有你要做的就是将Log4j放到classpath下并提供一个配置文件(log4j2.xml,log4j2.properties或其他支持的配置格式)。对于Maven用户,最简要的配置如下:
org.apache.logging.log4j
log4j-core
2.7
org.apache.logging.log4j
log4j-jcl
2.7
如果你希望使用SLF4J,下面的依赖也需要:
org.apache.logging.log4j
log4j-slf4j-impl
2.7
下面是控制台日志配置的log4j2.xml:
原生JCL运行容器
许多人在其本身提供JCL实现的容器中运行他们的Spring应用程序。IBM Websphere应用服务器(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依赖关系,而非容器。该选项并不总是开放的,但在公共领域还有喝多其他建议可供选择,你的方案可能因容器的确切版本和功能集而不同。