关于本书
本书
本书编写主要目的在于翻译官方spring.io
关于SpringFramework
模块文档之核心篇,但是本书不仅仅是简单的翻译,我会根据相应的模块给出一些代码的操作实践以及给出相应的源码分析,SpringFramework
文档我个人认为在所有开源框架中算是写得最好的了,But如果对于初学者或者是实践经验较少的小伙伴来说还是比较困难的。这里不仅仅是文档全是英文形式而且根据文档的概述是比较难理解和应用到实践项目开发中,这里需要开发人员积累了相应的项目实践经验才行。So我在翻译SpringFramework
过程中会不断编写相应的示例代码、结合文档和源码分析达到更好的理解。在后面的书中我可能会翻译现在比较流行的微服务框架SpringBoot
、SpringCloud
以及Spring的其他模块比如:SpringData
、SpringSession
、SpringSecurity
等,SpringFramework
在整个Spring体系中起着基石的作用,SpringBoot
基于SpringFramework
进行了相应的拓展包括:自动化配置
、外部化配置
、组件化(start模式)
等,而SpringCloud
基于SpringBoot
对整个微服务体系的服务治理、服务注册发现、统一配置中心等做了相关的集成和统一相关的抽象例如: Spring Cloud Commons
等。对应SpringFramework
这块主要分为以下模块来解析:Spring核心
、测试
、数据存储
、Web Servlet
、Web Reactive
、集成
。
版本
当前选择的版本Version 5.2.7.RELEASE
进行分析。
约束
- 使用字体加粗对重点关键字进行标注
- 使用灰色线框对专业术语进行标识
- 本文使用不同层级对文档结构进行描述
- 文章的一级、二级分类尽量保存英文不做翻译(能够帮助读者能够快速定位原文位置)
- 示例代码地址:https://gitee.com/newitman/SpringFramework5.2.6.git
- 本书开始编写时间2020-05-05
作者
个人从事金融行业,就职过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就职于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。
CSDN:https://blog.csdn.net/liyong1028826685
微信公众号:
技术交流群:
概述
Spring
使得开发人员更加容易的创建企业级应用程序,它提供了在企业Java生态技术栈中的任何技术体系,同时也提供了Groovy
和Kotlin
对JVM的支持,并且可以根据应用程序的需要灵活地创建多种体系结构,从Spring Framework 5.1
开始Spring
需要JDK1.8+,同时也提供了对JDK 11 LTS开箱即用的支持。 Java SE 8 update 60
作为Java 8最低的补丁版本被建议使用,但是一般推荐使用最近发布的补丁版本。
Spring
支持广泛的应用场景。在大型企业中,应用程序可能长时间的存在,而且需要运行在JDk上,同时应用程序的升级维护生命周期不受开发人员的控制。其他可能作为单独的jar运行或者嵌入式运行,也可能运行在云环境中。也可能是不需要服务器的一个独立的应用程序(例如:批处理、集成负载)。
Groovy
和Kotlin
和Java
一样可以编译成JVM支持的字节码格式,运行在JVM中。
Spring
是一个开源的框架。Spring
拥有一个非常强大和活跃的社区,它提供了一个范围广泛的实际应用案例的持续反馈。这有助于Spring
长时间的成功发展。
What We Mean by "Spring"
Spring
术语在不同上下文意义是不同。在这里Spring
就是指SrpingFramework
项目本身。然而随着时间的推移,其他的Spring
项目被构建在SrpingFramework
基础上。大多数情况下,我们所说的Spring
是包括整个项目体系。这篇文档主要聚焦在Spring Framework
框架本身。
SpringFramework
分为多个模块。应用程序根据需要选择合适的模块。核心容器的模块是核心包括:配置模型、依赖注入机制。除此之外,SpringFramework
提供一些基础的为不同应用架构、消息、事务数据和持久化、web提供支持。它也包括了基于Servlet基础的SpringMVC的web框架和支持WebFlux
的reactive编程模型的web框架。
关于模块需要注意的:Spring
的这些框架jar包允许被部署到JDK9的模块路径。为了在支持Jigsaw
的应用程序中使用,Spring Framework 5
jar附带了自动模块名称
清单条目,这些清单条目定义了与jar无关的稳定语言级别的模块名称( spring.core
, spring.context
等)名称(这些jar遵循相同的命名模式,用-
代替.
,例如spring-core
和spring-context
)。当然,Spring的框架jar可以在JDK 8和9的类路径上正常工作。
History of Spring and the Spring Framework
介于早期的J2EE
规范过于复杂,在2003年Spring
诞生了,尽管有些人认为Java EE和Spring
竞争,但事实上Spring
是Java EE的补充。Spring
编程模型不包含Java EE平台规范。相反,它精心选择各个规范进行集成
- Servlet API (JSR 340)
- WebSocket API (JSR 356)
- Concurrency Utilities (JSR 236)
- JSON Binding API (JSR 367)
- Bean Validation (JSR 303)
- JPA (JSR 338)
- JMS (JSR 914)
- JTA/JCA(分布式事务)
SpringFramework
同时也支持依赖注入(JSR 330)和通用注解规范 (JSR 250) ,应用程序开发人员可以选择使用这些规范来代替Spring框架提供的特定于Spring的机制。
从SpringFramework5.0
后,Spring
的最低要求 Java EE 7(Servlet 3.1+
, JPA 2.1+
)同时提供了开箱即用的集成Java EE 8最新的API ( JSON Binding API
),这样使得Spring
能够更好的兼容例如:Tomcat 8
、 Tomcat 9
, WebSphere 9
, JBoss EAP 7
服务容器。
随着时间的推移,J2EE
在应用开发中的作用已经发生改变。在J2EE和Spring
的早期,应用程序需要部署到应用服务器上。如今,在SpringBoot
的帮助下,这些应用可以通过devops
和友好的方式被创建,同时通过Servlet
容器的嵌入和一些很小的改变。从SpringFramework5.0
开始WebFlux
的应用程序甚至不需要直接使用Servlet
的API并且能够直接运行在非Servlet
容器服务器上。
Spring
不断的创新和发展。除了Spring Framework
,还有其他项目,例如Spring Boot
,Spring Security
,Spring Data
,Spring Cloud
,Spring Batch
等。请务必记住,每个项目都有自己的源代码存储库,问题跟踪程序和发布节奏。请查看spring.io/projects 罗列了完整的Spring
项目清单。
Design Philosophy(哲学)
当你在学习框架的时候,最重要的是不仅仅知道它能做什么,而且要遵循什么原则。以下是Spring
框架的指导原则。
- Spring提供在各个级别的选择。让开发者尽可能的推迟对设计的抉择。例如:你可以在不更改代码的情况下使用配置来切换数据的持久化方案。对于其他许多基础架构问题以及与第三方API的集成也是一样可以通过配置来调整。
- 容纳不同的观点。
Spring
支持灵活性,对于事情应该如何完成没有任何意见。它支持具有不同视角的广泛应用程序需求(备注:意思是Spring
设计相当灵活、对应怎样去完成逻辑处理Spring
不会干涉) - Spring保存了强的向后兼容性。Spring的演进被精心的管理以确保在不同版本间的变更尽量小。Spring非常小心的选择JDK和第三方库的版本,以方便维护依赖于Spring的应用程序和库。
- Spring的API精心设计。Spring花费了大量的时间和精力来设计API,并在许多版本和很多年中都适用的API。
- 高标准的代码质量要求。Spring框架非常强调有意义的、当前的和准确的
javadoc
。它是极少数可以代码结构整洁且程序包之间没有循环依赖关系的项目之一。
Spring核心
The IoC Container
1.1 Spring IoC容器和bean
这章节主要包括了SpringFramework
对IoC
容器的控制反转的原理。IoC
也被称作依赖注入(DI
)。这是一个对象仅通过构造函数参数、工厂方法的参数或对象实例构造或从工厂方法返回后在对象实例上设置的属性来定义其依赖项(即与之一起工作的其他对象)的过程。此过程从根本上讲是通过使用类的直接构造或诸如服务定位器模式之类的机制来控制其依赖项的实例化或位置的bean的逆过程(因此称为Control Inversion
)。(备注:大白话的意思就是为实例对象注入依赖的实例,注入方式包括构造函数注入、对象属性注入、setXXX
方法注入等。)
org.springframework.beans
和org.springframework.context
包是SpringFramework
容器的基础。BeanFactory
接口提供一个高级的配置机制能力,可以管理任何类型的对象。ApplicationContext
是BeanFactory
的子接口。增加了如下能力:
- 更容易的集成
Spring
的AOP
特性 - 消息资源处理(国际化)
- 事件发布器
- 应用层特定的上下文,例如:对应web应用上下文
WebApplicationContext
简要来说,BeanFactory
提供了配置框架和基本的功能,ApplicationContext
提供了更多的企业级功能。ApplicationContext
是一个完整的BeanFactory
的超集,在这章节中只是用来描述为Spring
的容器。更多的ApplicationContext
替换BeanFactory
的使用查看BeanFactory
。
在Spring
中,构成你的应用程序的骨架和被容器所管理的对象被称为bean。bean是被Spring
容器所实例化、包装、和管理的对象。bean也是在我们的应用程序中许多对象中之一。bean及bean之间的依赖关系是通过容器的配置元数据反映的。
参考示例代码:
com.liyong.ioccontainer.IoCContainer
1.2 Spring IoC容器概述
org.springframework.context.ApplicationContext
接口代表Spring
的IoC
容器,同时有责任对这些bean的实例化、配置和组装。容器通过获取配置元数据知道哪些对象需要进行实例化、配置和组装。这些配置元数据通过xml
、java的注解或者java的配置(Java Config
)。它允许你去表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。
在Spring
中提供了一些关于ApplicationContext
的实现。在一个独立的应用程序中,一般通过ClassPathXmlApplicationContext
或 FileSystemXmlApplicationContext
去创建容器实例。而XML
作为传统的定义配置元数据的方式,同时你可以引导容器去使用Java的注解或者Java Config作为元数据格式,通过提供少量的XML配置去声明激活这些附加元数据配置格式。
在大多数应用场景下,显示的实例化一个或多个Spring IoC
容器实例是没有必要的。例如:在Web应用场景中,在应用程序web.xml
文件中简单的xml配置描述通常就足够。如果使用Spring Tools for Eclipse工具,我们将非常容易的创建样板配置。
下面通过图片来展示Spring
是怎样工作的,我们的应用程序类(业务类)与配置元数据结合在一起,在ApplicationContext
被创建和实例化后,我们将拥有一个完整的配置和可执行的应用系统。
1.2.1 配置元数据
上图所示,Spring
容器获取配置元数据。那么这些元数据是怎样被表示或者是怎样配置的呢,作为一个开发人员,在你的应用程序中去告诉Spring
容器怎样去实例化、配置和组装这些对象。
XML
格式作为一种传统的配置元数据方式,在这个章节中大部分内容使用这些关键概念和特性。
基于
XML
的配置元数据不是唯一被允许使用的配置方式。Spring
容器本身是和这些配置元数据是解偶的。现在,大多数的开发人员使用Java base configuration作为Spring
应用的配置方式。
关于更多的Spring容器的元数据配置方式以下罗列出来:
- 基于注解的配置方式:从Spring 2.5开始引入对注解元数据配置的支持
- 基于Java-base configuration方式:从Spring 3.0开始,
Spring
的JavaConfig项目成为SpringFramework
的核心部分,许多的特性被提供。因此,我们可以使用Java Config的方式定义外部bean而不是XML文件。我们可以使用这些新特性注解 例如:@Configuration
、@Bean
、@Import
和@DependsOn
。
Spring
配置组成至少通常超过一个bean的定义被容器管理。基于XML
的配置元数据将这些bean配置为*元素内的元素。Java Config通常使用@Bean
注解在方法上同时所在的类被@Configuration
标注。
这些bean的定义对应到真实的bean对象实例同时我们的应用程序由这些对象实例所构建。比如:我们定义的service层,数据存储层(dao),展示层 比如:Struts的Action
实例、Hibernate的SessionFactories
实例、JMS的Queues
等等。典型地,不需要在容器中配置细粒度的域对象,因为通常由DAO和业务逻辑负责创建和加载域对象。但是,你可以使用Spring
与AspectJ
的集成来配置在IoC容器控制范围之外创建的对象。
以下使用基于xml配置的元数据
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
</beans>
参考示例代码:
com.liyong.ioccontainer.XmlIocContainer
- id属性表示一个bean的唯一id
- class属性表示这个bean的全路径类型名称例如:
com.liyong.ioccontainer.ConfigruationService
1.2.2 容器实例化
ApplicationContext
构造函数提供了一个或多个字符串路径参数,让容器去加载外部资源的配置元数据,例如:本地文件系统或者Java 类路径等等。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");//加载services.xml和daos.xml配置元数据
在我们学习了关于
Spring IoC
容器后,我们可能想知道更多关于Resource
的抽象,Resource
提供了一个便利的机制去通过URI语法定义的位置读取一个输入流。特别地,Resource
被用于构建应用程序的上下文。Application上下文和Resource路径。
以下给出services.xml
服务层的元数据配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- services -->
<bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
以下显示数据获取层dao.xml
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for data access objects go here -->
</beans>
在前面的实例中,服务层由PetStoreServiceImpl
类和数据存储层JpaAccountDao
和JpaItemDao
对象组成(以JPA的对象关系映射为标准)。属性名称元素引用JavaBean的属性名称,并且ref
元素引用其他bean定义的名称。id和ref之间的联系表达了协作对象的依赖性。更多的关于对象依赖的配置查看Dependencies。
- 组成基于XML的配置元数据
配置元数据可以跨多个XML配置文件。通常情况下,每个独立的XML配置文件代表项目架构中的逻辑层或者模块。
你可以使用ApplicationContext
构造函数从这些配置的XML
片段中加载bean的定义。这个构造函数可以使用多个Resource
来定位资源,在上面的例子中可以查看。或者,使用一个或多个标签元素从其他的XML配置文件中加载bean的定义。下面的例子展示了导入多个资源文件。
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
在上面的例子中,加载外部的这些bean从3个文件中被加载分别是:services.xml
、messageSource.xml
和themeSource.xml
。所有被导入文件路径是相对应导入文件位置。(备注:大白话就是相对于当前导入文件的路径),因此services.xml
必须在导入文件相同的目录或者类路径下,而messageSource.xml
和themeSource.xml
必须在导入文件下的resources
路径。正如你所看到/
被忽略。但是,鉴于这些路径是相对的,最好不要使用斜线。
一种可能的使用方式使用
../
去引用父目录文件,但是不是推荐的。这样做会导致当前应用对外部文件的依赖。特别是,这种引用方式在使用classpath:
和URL
组合是不被推荐(例如:classpath:../services.xml
),运行时解析过程在其中选择最近的
类路径根,然后查看其父目录。类路径配置的更改可能导致选择其他错误的目录。我们应该总是使用全限定资源路径去替换相对路径:例如:
file:C:/config/services.xml
或者classpath:/config/services.xml
。但是请注意,你正在将应用程序的配置耦合到特定的绝对位置。通常最好对这样的绝对位置保留间接使用,例如通过在运行时针对JVM系统属性解析的${…}
占位符。
名称空间本身提供了import指令特性。除了普通bean定义之外,Spring
提供的XML
名称空间选择中还提供了其他配置功能-例如:context
和util
命名空间
- Groovy的bean定义
为外部配置元数据例举更进一步的例子,bean
的定义能够使用Spring
的Groovy语言定义来表达(可以从Grails
框架去了解)。典型地,这个配置使用.groovy
命名。下面举个例子:
beans {
dataSource(BasicDataSource) {
driverClassName = "org.hsqldb.jdbcDriver"
url = "jdbc:hsqldb:mem:grailsDB"
username = "sa"
password = ""
settings = [mynew:"setting"]
}
sessionFactory(SessionFactory) {
dataSource = dataSource
}
myService(MyService) {
nestedBean = { AnotherBean bean ->
dataSource = dataSource
}
}
}
上面这个配置格式相当于XML的bean
定义方式并且支持Spring的XML命名空间配置。这种方式允许我们通过importBeans
指令去导入XML的bean
定义。
参考示例代码:
com.liyong.ioccontainer.ImportXmlIocContainer
1.2.3 容器使用
ApplicationContext
是能够维护不同bean及其依赖项的注册表的高级工厂的接口。通过使用T getBean(String name, Class<T> requiredType)
方法可以找回我们的bean实例对象。
ApplicationContext
允许我们去读取bean的定义和获取bean实例对象,举例子:
// 创建容器和加载bean定义
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// 获取bean实例
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
使用Groovy配置,引导非常的类似。但是它是不同的Context的实现。举例子:
ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");
最灵活的变体是GenericApplicationContext
结合了reader的代理-例如:通过XmlBeanDefinitionReader
读取XML文件中的配置信息。举例子:
GenericApplicationContext context = new GenericApplicationContext();
//加载xml中配置元数据
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
我们也可以使用GroovyBeanDefinitionReader
加载Groovy配置文件。举例子:
GenericApplicationContext context = new GenericApplicationContext();
//加载groovy配置元数据
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();
我们也可以在相同的ApplicationContext
中混合或者匹配一个reader的代理,去读取不同配置源的bean定义配置信息。
我们也可以使用getBean()
方法去获取我们的bean实例对象。ApplicationContext
接口同时提供了一些其他的方法获取bean实例,但是,理想情况下,我们的代码不要直接使用它们。确实,我们的应用程序代码不应该直接调用getBean()
方法,这样就不会对Spring
的API进行耦合(备注:个人观点,在目前来看Spring
已经是容器管理的规范和标准了,使用API会对应用程序耦合这个两说,个人觉得是没有问题的,除非你还想换掉底层容器)。例如:Spring
集成的Web框架(一般指SpringMVC
)提供了变体的Web框架组件的依赖注入controllers
(Controller) 和 JSF-managed
的bean对象,同时允许我们通过元数据声明一个指定bean的依赖(例如注解注入:@Autowired
、@Resource
)。