Spring5概述
Spring是一个开源的轻量级JavaSE(Java标准版本)/JavaEE(Java企业版本)开发应用框架,其目的是用于简化企业级应用程序开发.
应用程序是由一组相互协作的对象组成.而在传统应用程序开发中,一个完整的应用是由一组相互协作的对象组成.所以开发一个应用除了要开发业务逻辑之外,最多的是关注如何使这些对象协作来完成所需功能,而且要低耦合、高内聚.业务逻辑开发是不可避免的,那如果有个框架出来帮我们来创建对象及管理这些对象之间的依赖关系.可能有人说了,比如"抽象工厂、工厂方法设计模式"不也可以帮我们创建对象,"生成器模式"帮我们处理对象间的依赖关系,不也能完成这些功能吗?可是这些又需要我们创建另一些工厂类、生成器类,我们又要而外管理这些类,增加了我们的负担,如果能有种通过配置方式来创建对象,管理对象之间依赖关系,我们不需要通过工厂和生成器来创建及管理对象之间的依赖关系,这样我们是不是减少了许多工作,加速了开发,能节省出很多时间来干其他事.Spring框架刚出来时主要就是来完成这个功能.
Spring框架除了帮我们管理对象及其依赖关系,还提供像通用日志记录、性能统计、安全控制、异常处理等面向切面的能力,还能帮我管理最头疼的数据库事务,本身提供了一套简单的JDBC访问实现,提供与第三方数据访问框架集成(如Hibernate、JPA),与各种JavaEE技术整合(如JavaMail、任务调度等等),提供一套自己的web层框架SpringMVC、而且还能非常简单的与第三方Web框架集成.
从这里我们可以认为Spring是一个超级粘合平台,除了自己提供功能外,还提供粘合其他技术和框架的能力,从而使我们可以更自由的选择到底使用什么技术进行开发.而且不管是JAVASE(C/S架构)应用程序还是JAVAEE(B/S架构)应用程序都可以使用这个平台进行开发.让我们来深入看一下Spring到底能帮我们做些什么?
1.一切从Bean开始
1996,Java还只是一个新兴的、初出茅庐的编程语言.人们之所以关注它仅仅是因为,可以使用Java的Applet来开发Web应用.但这些开发者很快就发现这个新兴的语言还能做更多的事情.与之前的所有语言不同,Java让模块化构建复杂的系统成为可能(当时的软件行业虽然在业务上突飞猛进,但当时开发用的是传统的面向过程开发思想,软件的开发效率一直踟蹰不前.伴随着业务复杂性的不断加深,开发也变得越发困难.其实,当时也是面向对象思想飞速发展的时期,它在80年代末被提出,成熟于90年代,现今大多数编程语言都是面向对象的,当然这是后话了).他们为Applet而来,为组件化而留.这便是最早的Java.
同样在这一年的12月,Sun公司发布了当时还名不见经传但后来人尽皆知的JavaBean1.00-A规范.早期的JavaBean规范针对于Java,它定义了软件组件模型.这个规范规定了一整套编码策略,使简单的Java对象不仅可以被重用,而且还可以轻松地构建更为复杂的应用.尽管JavaBean最初是为重用应用组件而设计的,但当时他们却是主要用作构建窗体控件,毕竟在PC时代那才是主流.但相比于当时正如日中天的Delphi、VB和C++,他看起来还是太简易了,以至于无法胜任任何"实际的"工作.
复杂的应用通常需要诸如事物、安全、分布式等服务的支持,但JavaBean并未直接提供.所以到了1998年3月,Sun发布了EJB1.0规范,该规范把Java组件的设计理念延伸到了服务器端,并提供了许多必须的企业级服务,但他也不再像早期的JavaBean那么简单了.实际上,除了名字,EJBBean已经和JavaBean没有任何关系了.
尽管现实中有很多系统是基于EJB构建的,但EJB从来没有实现它最初的设想: 简化开发.EJB的声明式编程模型的确简化了很多基础架构层面的开发,例如事务和安全;但另一方面EJB在部署描述符和配套代码实现等方面变得异常复杂.随着时间的推移,很多开发者对EJB已经不再抱有幻想,开始寻求更简洁的方法.
现在Java组件开发理念重新回归正轨.新的编程技术AOP和DI的不断出现,他们为JavaBean提供了之前EJB才能拥有的强大功能.这些技术为POJO提供了类似EJB的声明式编程模型,而没有引入任何EJB的复杂性.当简单的JavaBean足以胜任时,人们便不愿编写笨重的EJB组件了.
客观地讲,EJB的发展甚至促进了基于POJO的编程模型.引入新的理念,最新的EJB规范相比之前的规范有了前所未有的简化,但对很多开发者而言,这一切的一切都来得太迟了.到了EJB3规范发布时,其他基于POJO的开发架构已经成为事实的标准了,而Spring框架也是在这样的大环境下出现的.
2.Spring设计的初衷
Spring是为解决企业级应用开发的复杂性而设计,它可以做很多事.但归根到底支撑Spring的仅仅是少许的基本理念,而所有地这些的基本理念都能可以追溯到一个最根本的使命: 简化开发.这是一个郑重的承诺,其实许多框架都声称在某些方面做了简化.
而Spring则立志于全方面的简化Java开发.对此,它主要采取了4个关键策略:
- 1.基于POJO的轻量级和最小侵入性编程;
- 2.通过依赖注入和面向接口松耦合;
- 3.基于切面和惯性进行声明式编程;
- 4.通过切面和模板减少样板式代码;
而他主要是通过: 面向Bean、依赖注入以及面向切面这三种方式来达成的.
3.BOP编程伊始
Spring是面向Bean的编程(BeanOrientedProgramming,BOP),Bean在Spring中才是真正的主角.Bean在Spring中作用就像Object对OOP的意义一样,Spring中没有Bean也就没有Spring存在的意义.Spring提供了IOC容器通过配置文件或者注解的方式来管理对象之间的依赖关系.
控制反转(其中最常见的方式叫做依赖注入(DependencyInjection,DI),还有一种方式叫"依赖查找"(DependencyLookup,DL),它在C++、Java、PHP以及.NET中都运用.在最早的Spring中是包含有依赖注入方法和依赖查询的,但因为依赖查询使用频率过低,不久就被Spring移除了,所以在Spring中控制反转也被称作依赖注入).基本概念是: 不创建对象,但是描述创建它们的方式.在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务.容器(在Spring框架中是IOC容器)负责将这些联系在一起.
在典型的IOC场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法.
4.依赖注入的基本概念
Spring设计的核心org.springframework.beans包(架构核心是org.springframework.core包),它的设计目标是与JavaBean组件一起使用.这个包通常不是由用户直接使用,而是由服务器将其用作其他多数功能的底层中介.下一个最高级抽象是BeanFactory接口,它是工厂设计模式的实现,允许通过名称创建和检索对象.BeanFactory也可以管理对象之间的关系.
BeanFactory支持两个对象模型.
- 单例: 模型提供了具有特定名称的对象的共享实例,可以在查询时对其进行检索.Singleton是默认的也是最常用的对象模型.对于无状态服务对象很理想.
- 原型: 模型确保每次检索都会创建单独的对象.在每个用户都需要自己的对象时,原型模型最适合.
Bean工厂的概念是Spring作为IOC容器的基础.IOC则将处理事情的责任从应用程序代码转移到框架.
5.AOP编程理念
面向切面编程(Aspect Oriented Programming,即AOP),是一种编程思想,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化.AOP的核心构造是方面(切面),它将那些影响多个类的行为封装到可重用的模块中.
AOP和IOC是补充性的技术,它们都运用模块化方式解决企业应用程序开发中的复杂问题.在典型的面向对象开发方式中,可能要将日志记录语句放在所有方法和Java类中才能实现日志功能.在AOP方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要日志的组件上.当然,优势就是Java类不需要知道日志服务的存在,也不需要考虑相关的代码.所以,用SpringAOP编写的应用程序代码是松散耦合的.
AOP的功能完全集成到了Spring事务管理、日志和其他各种特性的上下文中.
AOP编程的常用场景
- Authentication权限认证
- Logging日志
- TransctionsManager事务
- LazyLoading懒加载
- ContextProcess上下文处理
- ErrorHandler错误跟踪(异常捕获机制)
- Cache缓存
6.Spring5系统架构
Spring总共大约有20个模块,由1300多个不同的文件构成.而这些组件被分别整合在核心容器(CoreContainer)、AOP(AspectOrientedProgramming)和设备支持(Instrmentation)、数据访问及集成(DataAccess/Integeration)、Web、报文发送(Messaging)、Test,6个模块集合中.
Spring5的模块结构图:

Spring5官网的说明:
组成Spring框架的每个模块集合或者模块都可以单独存在,也可以一个或多个模块联合实现.每个模块的组成和功能如下:
6.1 核心容器
由spring-beans、spring-core、spring-context和spring-expression(SpringExpressionLanguage,SpEL)4个模块组成.
spring-beans和spring-core模块是Spring框架的核心模块,包含了控制反转(InversionofControl,IOC)和依赖注入(DependencyInjection,DI).BeanFactory接口是Spring框架中的核心接口,它是工厂模式的具体实现.BeanFactory使用控制反转对应用程序的配置和依赖性规范与实际的应用程序代码进行了分离.但BeanFactory容器实例化后并不会自动实例化Bean,只有当Bean被使用时BeanFactory容器才会对该Bean进行实例化与依赖关系的装配.
spring-context模块构架于核心模块之上,他扩展了BeanFactory,为它添加了Bean生命周期控制、框架事件体系以及资源加载透明化等功能.此外该模块还提供了许多企业级支持,如邮件访问、远程访问、任务调度等,ApplicationContext是该模块的核心接口,它是BeanFactory的超类,与BeanFactory不同,ApplicationContext容器实例化后会自动对所有的单实例Bean进行实例化与依赖关系的装配,使之处于待用状态.
spring-expression模块是统一表达式语言(EL)的扩展模块,可以查询、管理运行中的对象,同时也方便的可以调用对象方法、操作数组、集合等.它的语法类似于传统EL,但提供了额外的功能,最出色的要数函数调用和简单字符串的模板函数.这种语言的特性是基于Spring产品的需求而设计,他可以非常方便地同Spring IOC进行交互.
6.2 AOP和设备支持
由spring-aop、spring-aspects和spring-instrument3个模块组成.
spring-aop是Spring的另一个核心模块,是AOP主要的实现模块.作为继OOP后,对程序员影响最大的编程思想之一,AOP极大地开拓了人们对于编程的思路.在Spring中,他是以JVM的动态代理技术为基础,然后设计出了一系列的AOP横切实现,比如前置通知、返回通知、异常通知等,同时,Pointcut接口来匹配切入点,可以使用现有的切入点来设计横切面,也可以扩展相关方法根据需求进行切入.
spring-aspects模块集成自AspectJ框架,主要是为SpringAOP提供多种AOP实现方法.
spring-instrument模块是基于JAVA_SE中的"java.lang.instrument"进行设计的,应该算是AOP的一个支援模块.主要作用是在JVM启用时,生成一个代理类,程序员通过代理类在运行时修改类的字节,从而改变一个类的功能,实现AOP的功能.
6.3 数据访问及集成
由spring-jdbc、spring-tx、spring-orm、spring-jms和spring-oxm5个模块组成.
spring-jdbc模块是Spring提供的JDBC抽象框架的主要实现模块,用于简化SpringJDBC.主要是提供JDBC模板方式、关系数据库对象化方式、SimpleJdbc方式、事务管理来简化JDBC编程,主要实现类是JdbcTemplate、SimpleJdbcTemplate以及NamedParameterJdbcTemplate.
spring-tx模块是SpringJDBC事务控制实现模块.使用Spring框架,它对事务做了很好的封装,通过它的AOP配置,可以灵活的配置在任何一层;但是在很多的需求和应用,直接使用JDBC事务控制还是有其优势的.其实,事务是以业务逻辑为基础的;一个完整的业务应该对应业务层里的一个方法;如果业务操作失败,则整个事务回滚;所以,事务控制是绝对应该放在业务层的;但是,持久层的设计则应该遵循一个很重要的原则: 保证操作的原子性,即持久层里的每个方法都应该是不可以分割的.所以,在使用SpringJDBC事务控制时,应该注意其特殊性.
spring-orm模块是ORM框架支持模块,主要集成Hibernate,JavaPersistenceAPI(JPA)和JavaDataObjects(JDO)用于资源管理、数据访问对象(DAO)的实现和事务策略.
spring-jms模块(JavaMessagingService)能够发送和接受信息,自SpringFramework4.1以后,他还提供了对spring-messaging模块的支撑.
spring-oxm模块主要提供一个抽象层以支撑OXM(OXM是Object-to-XML-Mapping的缩写,它是一个O/M-mapper,将java对象映射成XML数据,或者将XML数据映射成java对象),例如: JAXB,Castor,XMLBeans,JiBX和XStream等.
6.4 Web
由spring-web、spring-webmvc、spring-websocket和spring-webflux4个模块组成.
spring-web模块为Spring提供了最基础Web支持,主要建立于核心容器之上,通过Servlet或者Listeners来初始化IOC容器,也包含一些与Web相关的支持.
spring-webmvc模块众所周知是一个的Web-Servlet模块,实现了SpringMVC(model-view-Controller)的Web应用.
spring-websocket模块主要是与Web前端的全双工通讯的协议.
spring-webflux是一个新的非堵塞函数式ReactiveWeb框架,可以用来建立异步的,非阻塞,事件驱动的服务,并且扩展性非常好.
6.5 报文发送
即spring-messaging模块.
spring-messaging是从Spring4开始新加入的一个模块,主要职责是为Spring框架集成一些基础的报文传送应用.
6.6 Test
即spring-test模块.
spring-test模块主要为测试提供支持的,毕竟在不需要发布(程序)到你的应用服务器或者连接到其他企业设施的情况下能够执行一些集成测试或者其他测试对于任何企业都是非常重要的.
6.7 Spirng各模块之间的依赖关系
该图是Spring5的包结构,可以从中清楚看出Spring各个模块之间的依赖关系.

如果你想加入Spring源码的学习,笔者的建议是从spring-core入手,其次是spring-beans和spring-aop,随后是spring-context,再其次是spring-tx和spring-orm,最后是spring-web和其他部分.
7.Spring5新特性
Spring5于2017年9月发布了通用版本(GA),它标志着自2013年12月以来第一个主要Spring Framework版本.
这个版本是很长时间以来最令人兴奋的SpringFramework版本.Spring5兼容Java™8和JDK9,它集成了反应式流,以便提供一种颠覆性方法来实现端点和Web应用程序开发.
7.1 升级到Java SE 8和Java EE 7
直到现在,Spring Framework仍支持一些弃用的Java版本,但Spring5已从旧包袱中解放出来.为了充分利用Java8特性,它的代码库已进行了改进,而且该框架要求将Java8作为最低的JDK版本.
Spring 5在类路径(和模块路径)上完全兼容Java9,而且它通过了JDK9测试套件的测试.对Java9爱好者而言,这是一条好消息,因为在Java9发布后,Spring能立即使用它.
在API级别上,Spring 5兼容Java EE 8技术,满足对Servlet 4.0、Bean Validation 2.0和全新的JSON Binding API的需求.对Java EE API的最低要求为V7,该版本引入了针对Servlet、JPA和Bean Validation API的次要版本.
7.2 反应式编程模型
Spring 5最令人兴奋的新特性是它的反应式编程模型.Spring 5 Framework基于一种反应式基础而构建,而且是完全异步和非阻塞的.只需少量的线程,新的事件循环执行模型就可以垂直扩展.
该框架采用反应式流来提供在反应式组件中传播负压的机制.负压是一个确保来自多个生产者的数据不会让使用者不堪重负的概念.
Spring WebFlux是Spring5的反应式核心,它为开发人员提供了两种为Spring Web编程而设计的编程模型:一种基于注解的模型和Functional Web Framework(WebFlux.fn).
基于注解的模型是Spring WebMVC的现代替代方案,该模型基于反应式基础而构建,而Functional Web Framework是基于@Controller注解的编程模型的替代方案.这些模型都通过同一种反应式基础来运行,后者调整非阻塞HTTP来适应反应式流API.
7.3 使用注解进行编程
WebMVC程序员应该对Spring5的基于注解的编程模型非常熟悉.Spring 5调整了WebMVC的@Controller编程模型,采用了相同的注解.
@RestController
public class BookController {
@GetMapping("/book")
Flux<Book> list() {
return this.repository.findAll();
}
@GetMapping("/book/{id}")
Mono<Book> findById(@PathVariable String id) {
return this.repository.findOne(id);
}
// Plumbing code omitted for brevity
}
BookController类提供了两个方法,分别响应针对某个图书列表的HTTP请求,以及针对具有给定id的图书的HTTP请求.请注意resource方法返回的对象(Mono和Flux).这些对象是实现反应式流规范中的Publisher接口的反应式类型.它们的职责是处理数据流.
Mono对象处理一个仅含1个元素的流,而Flux表示一个包含N个元素的流.
7.4 函数式编程
Spring 5的新函数式方法将请求委托给处理函数,这些函数接受一个服务器请求实例并返回一种反应式类型.
public class BookHandler {
public Mono<ServerResponse> listBooks(ServerRequest request) {
return ServerResponse.ok()
.contentType(APPLICATION_JSON)
.body(repository.allPeople(), Book.class);
}
public Mono<ServerResponse> getBook(ServerRequest request) {
return repository.getBook(request.pathVariable("id"))
.then(book -> ServerResponse.ok()
.contentType(APPLICATION_JSON)
.body(fromObject(book)))
.otherwiseIfEmpty(ServerResponse.notFound().build());
}
// Plumbing code omitted for brevity
}
通过路由函数来匹配HTTP请求谓词与媒体类型,将客户端请求路由到处理函数.
BookHandler handler = new BookHandler();
RouterFunction<ServerResponse> personRoute =
route(
GET("/books/{id}")
.and(accept(APPLICATION_JSON)), handler::getBook)
.andRoute(
GET("/books")
.and(accept(APPLICATION_JSON)), handler::listBooks);
这些示例背后的数据存储库也支持完整的反应式体验,该体验是通过Spring Data对反应式Couchbase、Reactive MongoDB和Cassandra的支持来实现的.
7.5 使用REST端点执行反应式编程
新的编程模型脱离了传统的Spring WebMVC模型,引入了一些很不错的新特性.
举例来说,WebFlux模块为RestTemplate提供了一种完全非阻塞、反应式的替代方案,名为WebClient.
Mono<Book> book = WebClient.create("http://localhost:8080")
.get()
.url("/books/{id}", 1234)
.accept(APPLICATION_JSON)
.exchange(request)
.then(response -> response.bodyToMono(Book.class));
7.6 HTTP/2支持
Spring Framework 5.0将提供专门的HTTP/2特性支持,还支持人们期望出现在JDK9中的新HTTP客户端.尽管HTTP/2的服务器推送功能已通过Jetty servlet引擎的ServerPushFilter类向Spring开发人员公开了很长一段时间,但如果发现Spring5中开箱即用地提供了HTTP/2性能增强,Web优化者们一定会为此欢呼雀跃.
7.7 Kotlin和SpringWebFlux
Kotlin是一种来自JetBrains的面向对象的语言,它支持函数式编程.它的主要优势之一是与Java有非常高的互操作性.通过引入对Kotlin的专门支持,Spring在V5中全面吸纳了这一优势.它的函数式编程风格与Spring WebFlux模块完美匹配,它的新路由DSL利用了函数式Web框架以及干净且符合语言习惯的代码.
@Bean
fun apiRouter() = router {
(accept(APPLICATION_JSON) and "/api").nest {
"/book".nest {
GET("/", bookHandler::findAll)
GET("/{id}", bookHandler::findOne)
}
"/video".nest {
GET("/", videoHandler::findAll)
GET("/{genre}", videoHandler::findByGenre)
}
}
}
7.8 使用Lambda表达式注册bean
作为传统XML和JavaConfig的替代方案,现在可以使用lambda表达式注册Spring bean,使bean可以实际注册为提供者.
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Book.class, () -> new Book(context.getBean(Author.class)));
7.9 Spring WebMVC支持最新的API
全新的WebFlux模块提供了许多新的、令人兴奋的功能,但Spring5也迎合了愿意继续使用SpringMVC的开发人员的需求.Spring5中更新了模型-视图-控制器框架,以兼容WebFlux和最新版的Jackson 2.9和Protobuf 3.0,甚至包括对新的Java EE 8 JSON-Binding API的支持.
除了HTTP/2特性的基础服务器实现之外,Spring WebMVC还通过MVC控制器方法的一个参数来支持Servlet 4.0的PushBuilder.最后,WebMVC全面支持Reactor 3.1的Flux和Mono对象,以及RxJava 1.3和2.1,它们被视为来自MVC控制器方法的返回值.这项支持的最终目的是支持Spring Data中的新的反应式WebClient和反应式存储库.
7.10 使用JUnit5执行条件和并发测试
JUnit和Spring5:Spring5全面接纳了函数式范例,并支持JUnit5及其新的函数式测试风格.还提供了对JUnit4的向后兼容性,以确保不会破坏旧代码.
Spring5的测试套件通过多种方式得到了增强,但最明显的是它对JUnit5的支持.现在可以在您的单元测试中利用Java8中提供的函数式编程特性.
@Test
void givenStreamOfInts_SumShouldBeMoreThanFive() {
assertTrue(Stream.of(20, 40, 50)
.stream()
.mapToInt(i -> i)
.sum() > 110, () -> "Total should be more than 100");
}
Spring 5继承了JUnit 5在Spring TestContext Framework内实现多个扩展API的灵活性.举例而言,开发人员可以使用JUnit 5的条件测试执行注解@EnabledIf和@DisabledIf来自动计算一个SpEL(SpringExpressionLanguage)表达式,并适当地启用或禁用测试.借助这些注解,Spring5支持以前很难实现的复杂的条件测试方案.Spring TextContext Framework现在能够并发执行测试.
7.11 使用Spring WebFlux执行集成测试
Spring Test现在包含一个WebTestClient,后者支持对Spring WebFlux服务器端点执行集成测试.WebTestClient使用模拟请求和响应来避免耗尽服务器资源,并能直接绑定到WebFlux服务器基础架构.
WebTestClient可绑定到真实的服务器,或者使用控制器或函数.
WebTestClient testClient = WebTestClient
.bindToServer()
.baseUrl("http://localhost:8080")
.build();
将 WebTestClient 绑定到 RouterFunction
RouterFunction bookRouter = RouterFunctions.route(
RequestPredicates.GET("/books"),
request -> ServerResponse.ok().build()
);
WebTestClient
.bindToRouterFunction(bookRouter)
.build().get().uri("/books")
.exchange()
.expectStatus().isOk()
.expectBody().isEmpty();
7.12 包清理和弃用
Spring 5中止了对一些过时API的支持.遭此厄运的还有Hibernate 3和4,为了支持Hibernate 5,它们遭到了弃用.另外,对Portlet、Velocity、JasperReports、XMLBeans、JDO和Guava的支持也已中止.
包级别上的清理工作仍在继续:Spring5不再支持beans.factory.access、jdbc.support.nativejdbc、mock.staticmock(来自spring-aspects模块)或web.view.tiles2M.
Tiles3现在是Spring的最低要求.
7.13 对Spring核心和容器的一般更新
Spring Framework 5改进了扫描和识别组件的方法,使大型项目的性能得到提升.目前,扫描是在编译时执行的,而且向META-INF/spring.components文件中的索引文件添加了组件坐标.该索引是通过一个为项目定义的特定于平台的应用程序构建任务来生成的.
标有来自javax包的注解的组件会添加到索引中,任何带@Index注解的类或接口都会添加到索引中.Spring的传统类路径扫描方式没有删除,而是保留为一种后备选择.有许多针对大型代码库的明显性能优势,而托管许多Spring项目的服务器也会缩短启动时间.
Spring 5还添加了对@Nullable的支持,后者可用于指示可选的注入点.使用者现在必须准备接受null值.此外,还可以使用此注解来标记可以为null的参数、字段和返回值.@Nullable主要用于IntelliJ IDEA等IDE,但也可用于Eclipse和FindBugs,它使得在编译时处理null值变得更方便,而无需在运行时发送NullPointerExceptions.
SpringLogging还提升了性能,自带开箱即用的Commons Logging桥接器.现在已通过资源抽象支持防御性编程,为getFile访问提供了isFile指示器.