书名:Spring实战(第5版)
ISBN:978-7-115-52792-9
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 [美]克雷格•沃斯(Craig Walls)
译 张卫滨
责任编辑 陈冀康
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
网址 http://www.ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
Original English language edition, entitled Spring in Action, Fifth Edition by Craig Walls Bibeault published by Manning Publications Co., 209 Bruce Park Avenue, Greenwich, CT 06830. Copyright © 2019 by Manning Publications Co.
Simplified Chinese-language edition copyright © 2020 by Posts & Telecom Press. All rights reserved.
本书中文简体字版由Manning Publications Co.授权人民邮电出版社独家出版。未经出版者书面许可,不得以任何方式复制本书内容。
版权所有,侵权必究。
本书是一本经典而实用的畅销Spring学习指南。
第5版涵盖了Spring 5.0和Spring Boot 2.0里程碑式的更新。全书分为5个部分,共19章。第1部分(第1~5章)涵盖了构建Spring应用的基础话题。第2部分(第6~9章)讨论如何将Spring应用与其他应用进行集成。第3部分(第10~12章)探讨Spring对反应式编程提供的全新支持。第4部分(第13~15章)拆分单体应用模型,介绍Spring Cloud和微服务开发。第5部分(第16~19章)讨论如何为应用投入生产环境做准备以及如何进行部署。
本书既适合刚开始学习Spring Boot和Spring框架的Java开发人员快速上手,也适合经验丰富的Spring开发人员学习Spring的新特性,尤其适用于企业级Java开发人员。
不知不觉间,已经参与了3个版本的《Spring实战》翻译。2007年春天,当时J2EE Without EJB的风潮刚刚兴起,还在读研的我在天津大学图书馆借到第 1 版的《Spring实战》,当时忙于毕业,没有把这本书完整地读完,但是依赖注入、面向切面编程等理念还是深深印在了脑海中,书中经典有趣的圆桌骑士样例更是驱使我读了很多相关的历史背景材料。
屈指算来,已经过去了10多年,Spring早已经成为企业级Java开发的事实标准,Spring Boot和Spring Cloud相关的技术引领潮流,更是成为Java工程师的必备技能。《Spring实战》的作者Craig Walls不断推陈出新,将这本经典图书更新到第5版。只是,10多年以前,我恐怕做梦也想不到会与这本书有这么深的缘分。
Spring之所以能够在技术不断更新换代的IT领域长盛不衰,并且引领技术架构发展的潮流,我想这是因为它一直没有偏离Rod Johnson最初的目标。那就是,根据技术的发展,不断优化和革新,让Java应用的开发更加便利和高效。从XML配置、注解配置,再到Spring Boot的自动化配置,Spring在不断简化,开发人员需要做的额外工作越来越少。虽然Rod Johnson早已离开Spring去开创新的事业了,但是我相信Spring的这种基因还是一直在的。在可以预见的未来,Spring及其家族产品依然是值得花时间投资学习的技术。
有时候,我也会思考,真正的技术到底是什么,是某一项生僻的配置还是某个新的API?我想,这都是技术,却不是最关键的。因为这些东西都是不稳定的、易变的,想要在新知识层出不穷的领域中不被淘汰,我们更应该去追求一些内在稳定不变的知识,比如技术规范、设计原理等。所以,希望本书的读者能够通过这本入门的读物,去更多地探究一些Spring底层的设计和实现原理。
本书第10章关于反应式编程的初稿由何品翻译,我负责统稿修改;另外,对于反应式编程的术语和规范,何品和他的团队都做了很多的工作,在此向他表示感谢。
再次感谢我的爱人和儿子,又容忍我把这几个月的业余时间都耗在了笔记本电脑的前面。
希望这本书对读者有所帮助,如果读者在阅读中遇到问题,可以通过levinzhang1981 @126.com或者微信levinzhang1981与我联系,祝阅读愉快。
张卫滨
2019年7月29日于大连
编写《Spring实战(第5版)》的目的是让读者学会使用Spring框架、Spring Boot以及Spring生态系统中各种辅助部分构建令人赞叹的应用程序。本书首先介绍如何使用Spring和Spring Boot开发基于Web、以数据库作为后端的Java应用;随后进行必要的扩展,展现如何与其他应用进行集成、使用反应式类型进行编程,以及将应用拆分为离散的微服务;最后讨论如何准备应用的部署。
尽管Spring生态系统中的每个项目都提供了完善的文档,但是本书所做的是所有参考文档都无法做到的事情:提供一个实用的、项目驱动的指南,将Spring的各种元素组合起来形成一个真正的应用。
《Spring实战(第5版)》适用于刚刚开始学习Spring Boot和Spring框架的Java开发人员,也适用于想要超越基础知识并学习Spring新特性的经验丰富的Spring开发者。
本书分成了5个部分,共计19章。
第1部分涵盖构建Spring应用的基础话题。
第2部分讨论如何将Spring应用与其他应用进行集成。
第3部分探讨Spring对反应式编程提供的全新支持。
第4部分将会拆分单体应用模型,介绍Spring Cloud和微服务开发。
在第5部分中,我们将会讨论如何做好将应用投入生产环境的准备,并看一下如何进行部署。
通常来讲,刚刚接触Spring的开发人员应该从第1章开始,并按顺序阅读每一章;经验丰富的Spring开发人员可能更愿意在任何感兴趣的时候参与进来。即便如此,每一章都是建立在前一章内容的基础上的,所以如果从中间开始阅读,那么可能会漏掉一些上下文信息。
本书包含许多源代码的样例,有的是编号的程序清单,有的是普通文本内嵌的源码。在这两种情况下,源代码都使用固定宽度的字体排版,以便将其与普通文本分开。有时代码也会使用粗体显示,以便于强调此处代码与本章前面步骤的变更,比如为已有的代码行添加新的特性。
在许多情况下,原始源代码会重新格式化;我们添加了换行符和重新缩进,以适应书中可用的页面空间。在极少数情况下,这样做依然是不够的,在这种情况下程序清单会包括换行符(➥)。此外,当在文中描述代码的时候,源码中的注释通常会被移除。许多程序清单会有代码标注,用来突出强调重要的概念。
本书中样例的源码可以通过异步社区的本书页面下载。
还需要其他帮助吗?
克雷格·沃斯(Craig Walls)是Pivotal的首席工程师。他是Spring框架的热心推动者,经常在本地用户组和会议上发言,撰写关于Spring的文章。在不琢磨代码的时候,Craig正在计划去迪士尼世界或迪士尼乐园的下一次旅行,他希望尽可能多地陪伴他的妻子和两个女儿。
《Spring实战(第5版)》的封面人物是“Le Caraco”,也就是约旦西南部卡拉克(Karak)省的居民。该省的首府是Al-Karak,那里的山顶有座城堡,对死海和周边的平原有着极佳的视野。这幅图出自1796年出版的法国旅游图书,Encyclopédiedes Voyages,由J.G.St.Sauveur编写。在那时,为了娱乐而去旅游还是相对新鲜的做法,而像这样的旅游指南是很流行的,它能够让旅行家和足不出户的人们了解法国其他地区和国外的居民。
Encyclopédiedes Voyages中多种多样的图画生动描绘了200年前世界上各个城镇和地区的独特魅力。在那时,相隔几十千米的两个地区着装就不相同,可以通过着装判断人们究竟属于哪个地区。这本旅行指南展现了那个时代和其他历史时代的隔离感和距离感,这与我们这个运动过度的时代是截然不同的。
从那以后,服装风格发生了改变,富有地方特色的多样性开始淡化。现在,有时很难说一个洲的居民和其他洲的居民有什么不同。可能,从积极的方面来看,我们用原来文化和视觉上的多样性换来了个人风格的多变性,或者可以说是更为多样化和有趣的知识科技生活。这本旅行指南中的图片反映了两个世纪前各个地区生活的多样性,我们现在用图书封面的方式对其进行了再现。Manning出版社的员工都认为这是计算机行业中一个很有意思的创意。
在使用了Spring 15年并编写了这本书的5个版本(暂时不算《Spring Boot实战》了)之后,你可能会认为,在为这本书撰写前言时,我很难想出一些关于Spring令人兴奋的新内容,但事实远非如此!
在Spring生态系统中,Spring、Spring Boot和所有其他项目的每个版本都发布了令人兴奋的新功能,重新点燃了开发应用程序的乐趣。Spring 5.0和Spring Boot 2.0的发布达到了一个重要的里程碑。Spring有了更多的乐趣,所以编写新版《Spring实战》是很容易的。
Spring 5的主要功能是对反应式编程的支持,包括Spring WebFlux。这是一个全新的反应式Web框架,借鉴了Spring MVC的编程模型,允许开发人员创建伸缩性更好且耗用更少线程的Web应用程序。至于Spring应用的后端,最新版本的Spring Data支持创建反应式、非阻塞的数据repository。所有这些都构建在Reactor项目之上,Reactor是一个用于处理反应式类型的Java库。
除了Spring 5新的反应式编程特性之外,Spring Boot 2提供了比以前更多的自动配置支持,以及一个完全重新设计的Actuator,用于探查和操作正在运行的应用。
更重要的是,当开发人员希望将单体应用拆分为分散的微服务时,Spring Cloud提供了一些工具,使配置和发现微服务变得容易,并增强了微服务的功能,使它们更能抵御失败。
我很高兴地说,《Spring实战(第5版)》涵盖了所有的这些功能,甚至更多!如果你是经验丰富的老手,《Spring实战(第5版)》可以作为指南,指导你去学习Spring提供的新功能;如果你是Spring新手,那么现在是行动起来的最佳时机,本书的前几章会让你快速上手!
与Spring合作的15年是令人兴奋的。现在我已经写了5个版本的《Spring实战》,我很想和你们分享这份兴奋!
Spring和Spring Boot所做的最令人惊奇的事情之一就是自动为应用程序提供所有的基础功能,让开发人员专注于应用程序特有的逻辑。不幸的是,对于写书这件事来说,并没有这样的魔法。是这样的吗?
在Manning,有很多人在施展“魔法”,确保这本书是最好的。特别要感谢我的项目编辑Jenny Stout以及制作团队,其中包括项目主管Janet Vail、文字编辑Andy Carroll和Frances Buran,以及校对Katie Tennant和Melody Dolab。同时也要感谢技术校对Joshua White,他的工作很全面,很有帮助。
在此过程中,我们得到了几位同行评论的反馈,他们确保了这本书没有偏离目标,涵盖了正确的内容。为此,我要感谢Andrea Barisone、Arnaldo Ayala、Bill Fly、Colin Joyce、Daniel Vaughan、David Witherspoon、Eddu Melendez、Iain Campbell、Jettro Coenradie、John Gunvaldson、Markus Matzker、Nick Rakochy、Nusry Firdousi、Piotr Kafel、Raphael Villela、Riccardo Noviello、Sergio Fernandez Gonzalez、Sergiy Pylypets、Thiago Presa、Thorsten Weber、Waldemar Modzelewski、Yagiz Erkan和Željko Trogrlić。
和往常一样,如果不是Spring工程团队成员所做的出色工作,写这本书是没有任何意义的。我惊叹于你们所创造的成果,并期待未来继续改变软件开发的方式。
非常感谢我的同行们在No Fluff/Just Stuff巡回演讲上的发言。我从你们每个人身上学到很多。我特别要感谢Brian Sletten、Nate Schutta和Ken Kousen关于Spring的对话和邮件,这些内容塑造了这本书。
我要再次感谢Phoenicians,你们太棒了[1]。
最后,我要感谢我美丽的妻子Raymie。她是我生命中的挚爱,是我最甜蜜的梦想,也是我的灵感来源:谢谢你的鼓励,也谢谢你为这本新书做的努力。致我可爱的女儿Maisy和Madi:我为你们感到骄傲,为你们即将成为了不起的年轻女士感到骄傲。我对你们的爱超出了你们的想象,也超出了我语言所能表达的程度。
[1] Phoenicians指的是远古时代的腓尼基人,他们被认为是字母系统的创建者,基于字母的所有现代语言都是由此衍生而来的。在迪士尼世界的Epcot,有名为Spaceship Earth的时光穿梭体验,我们可以了解到人类交流的历史,甚至能够回到腓尼基人的时代,在这段旅程的旁白中这样说道:如果你觉得学习字母语言很容易,就感谢腓尼基人吧,是他们发明了它。这是作者的一种幽默说法。——译者注
本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。
本书提供如下资源:
本书源代码;
书中彩图文件。
要获得以上配套资源,请在异步社区本书页面中点击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。
如果您是教师,希望获得教学配套资源,请在社区本书页面中直接联系本书的责任编辑。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入勘误信息,点击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。
我们的联系邮箱是contact@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问www.epubit.com/ selfpublish/submission即可)。
如果学校、培训机构或企业想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。
“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。
异步社区
微信服务号
本书的第1部分将会介绍如何开始编写Spring应用,并在这个过程中学习Spring的基础知识。
在第 1 章中,我将简要介绍Spring和Spring Boot的核心知识,并展示在构建第一个Spring应用Taco Cloud的过程中如何初始化Spring项目。在第2章中,我们将深入研究Spring MVC,了解如何在浏览器中显示模型数据,以及如何处理和验证表单输入。我们还会介绍选择视图模板库的技巧。在第3章中,我们将向Taco Cloud应用程序添加数据持久化功能。到时候,我们将介绍如何使用Spring的JDBC模板来插入数据,以及如何使用Spring Data声明JPA repository。第4章将介绍Spring应用程序的安全性,包括自动配置Spring安全性、声明自定义用户存储、自定义登录页面以及防止跨站请求伪造(CSRF)攻击。作为第1部分的结尾,我们将在第5章中学习配置属性。我们将了解如何细粒度调整自动配置bean、让应用组件使用配置属性,以及如何使用Spring profile。
本章内容:
Spring和Spring Boot的必备知识
初始化Spring项目
Spring生态系统概览
尽管希腊哲学家赫拉克利特(Heraclitus)并不作为一名软件开发人员而闻名,但他似乎深谙此道。他的一句话经常被引用:“唯一不变的就是变化”,这句话抓住了软件开发的真谛。
我们现在开发应用的方式和1年前、5年前、10年前都是不同的,更别提15年前了,当时Rod Johnson的图书Expert One-on-One J2EE Design and Development介绍了Spring框架的初始形态。
当时,最常见的应用形式是基于浏览器的Web应用,后端由关系型数据库作为支撑。尽管这种形式的开发依然有它的价值,Spring也为这种应用提供了良好的支持,但是我们现在感兴趣的还包括如何开发面向云的由微服务组成的应用,这些应用会将数据保存到各种类型的数据库中。另外一个崭新的关注点是反应式编程,它致力于通过非阻塞操作提供更好的扩展性并提升性能。
随着软件开发的发展,Spring框架也在不断变化,以解决现代应用开发中的问题,其中就包括微服务和反应式编程。Spring还通过引入Spring Boot简化自己的开发模型。
不管你是开发以数据库作为支撑的简单Web应用,还是围绕微服务构建一个现代应用,Spring框架都能帮助你达成目标。本章是使用Spring进行现代应用开发的第一步。
我知道你现在可能迫不及待地想要开始编写Spring应用了,我可以向你保证,在本章结束之前,你肯定能够开发一个简单的Spring应用。首先,我将使用Spring的一些基础概念为你搭建一个舞台,帮助你理解Spring是如何运行起来的。
任何实际的应用程序都是由很多组件组成的,每个组件负责整个应用功能的一部分,这些组件需要与其他的应用元素进行协调以完成自己的任务。当应用程序运行时,需要以某种方式创建并引入这些组件。
Spring的核心是提供了一个容器(container),通常称为Spring应用上下文(Spring application context),它们会创建和管理应用组件。这些组件也可以称为bean,会在Spring应用上下文中装配在一起,从而形成一个完整的应用程序。这就像砖块、砂浆、木材、管道和电线组合在一起,形成一栋房子似的。
将bean装配在一起的行为是通过一种基于依赖注入(dependency injection,DI)的模式实现的。此时,组件不会再去创建它所依赖的组件并管理它们的生命周期,使用依赖注入的应用依赖于单独的实体(容器)来创建和维护所有的组件,并将其注入到需要它们的bean中。通常,这是通过构造器参数和属性访问方法来实现的。
举例来说,假设在应用的众多组件中,有两个是我们需要处理的:库存服务(用来获取库存水平)和商品服务(用来提供基本的商品信息)。商品服务需要依赖于库存服务,这样它才能提供商品的完整信息。图1.1阐述这些bean和Spring应用上下文之间的关系。
图1.1 应用组件通过Spring的应用上下文来进行管理并实现互相注入
在核心容器之上,Spring及其一系列的相关库提供了Web框架、各种持久化可选方案、安全框架、与其他系统集成、运行时监控、微服务支持、反应式编程以及众多现代应用开发所需的特性。
在历史上,指导Spring应用上下文将bean装配在一起的方式是使用一个或多个XML文件(描述各个组件以及它们与其他组件的关联关系)。例如,如下的XML描述了两个bean,也就是InventoryService bean和ProductService bean,并且通过构造器参数将InventoryService装配到了ProductService中:
<bean id="inventoryService"
class="com.example.InventoryService" />
<bean id="productService"
class="com.example.ProductService" />
<constructor-arg ref="inventoryService" />
</bean>
但是,在最近的Spring版本中,基于Java的配置更为常见。如下基于Java的配置类是与XML配置等价的:
@Configuration
public class ServiceConfiguration {
@Bean
public InventoryService inventoryService() {
return new InventoryService();
}
@Bean
public ProductService productService() {
return new ProductService(inventoryService());
}
}
@Configuration注解会告知Spring这是一个配置类,会为Spring应用上下文提供bean。这个配置类的方法使用@Bean注解进行了标注,表明这些方法所返回的对象会以bean的形式添加到Spring的应用上下文中(默认情况下,这些bean所对应的bean ID与定义它们的方法名称是相同的)。
相对于基于XML的配置方式,基于Java的配置会带来多项额外的收益,包括更强的类型安全性以及更好的重构能力。即便如此,不管是使用Java还是使用XML的显式配置,只有当Spring不能进行自动配置的时候才是必要的。
在Spring技术中,自动配置起源于所谓的自动装配(autowiring)和组件扫描(component scanning)。借助组件扫描技术,Spring能够自动发现应用类路径下的组件,并将它们创建成Spring应用上下文中的bean。借助自动装配技术,Spring能够自动为组件注入它们所依赖的其他bean。
最近,随着Spring Boot的引入,自动配置的能力已经远远超出了组件扫描和自动装配。Spring Boot是Spring框架的扩展,提供了很多增强生产效率的方法。最为大家所熟知的增强方法就是自动配置(autoconfiguration),Spring Boot能够基于类路径中的条目、环境变量和其他因素合理猜测需要配置的组件并将它们装配在一起。
我非常愿意为你展现一些关于自动配置的示例代码,但是我做不到。自动配置就像风一样,你可以看到它的效果,但是我找不到代码指给你说,“看!这就是自动配置的样例!”事情发生了,组件启用了,功能也提供了,但是不用编写任何代码。没有代码就是自动装配的本质,也是它如此美妙的原因所在。
Spring Boot大幅度减少了构建应用所需的显式配置的数量(不管是XML配置还是Java配置)。实际上,当完成本章的样例时,我们会有一个可运行的Spring应用,该应用只有一行Spring配置代码。
Spring Boot极大地改善了Spring的开发,因此很难想象在没有它的情况下如何开发Spring应用。因此,本书会将Spring和Spring Boot当成一回事。我们会尽可能多地使用Spring Boot,只有在必要的时候才使用显式配置。因为Spring XML配置是一种过时的方式,所以我们主要关注Spring基于Java的配置。
闲言少叙,既然本书的名称中包含“实战”这个词,那么就开始动手吧!下面我们将会编写使用Spring的第一个应用。
在本书中,我们将会创建一个名为Taco Cloud的在线应用,它能够订购人类所发明的一种美味,也就是墨西哥煎玉米卷(taco)[1]。当然,在这个过程中,为了达成我们的目标,我们将会用到Spring、Spring Boot以及各种相关的库和框架。
我们有多种初始化Spring应用的可选方案。尽管我可以教你手动创建项目目录结构和定义构建规范的各个步骤,但这无疑是浪费时间,我们最好将时间花在编写应用代码上。因此,我们将会学习如何使用Spring Initializr初始化应用。
Spring Initializr是一个基于浏览器的Web应用,同时也是一个REST API,能够生成一个Spring项目结构的骨架,我们还可以使用各种想要的功能来填充它。使用Spring Initializr的几种方式如下:
我将这些细节放到了附录中,这样就不用在这里花费很多页的篇幅介绍每种方案了。在本章和本书中,我都会向你展示如何使用我最钟爱的方式创建新项目:在Spring Tool Suite中使用Spring Initializr。
顾名思义,Spring Tool Suite是一个非常棒的Spring开发环境。它同时还提供了便利的Spring Boot Dashboard特性,这个特性是其他IDE都不具备的(至少在我编写本书的时候如此)。
如果你不是Spring Tool Suite用户,那也没有关系,我们依然可以做朋友。你可以跳转到附录中,查看最适合你的Initializr方案,以此来替换后面小节中的内容。但是,在本书中,我偶尔会提到Spring Tool Suite特有的特性,比如Spring Boot Dashboard。如果你不使用Spring Tool Suite,那么需要调整这些指令以适配你的IDE。
要在Spring Tool Suite中初始化一个新的Spring项目,我们首先要点击File菜单,选择New,接下来选择Spring Starter Project。图1.2展现了要查找的菜单结构。
图1.2 在Spring Tool Suite中使用Initializr初始化一个新项目
在选择Spring Starter Project之后,将会出现一个新的向导对话框(见图1.3)。向导的第一页会询问一些项目的通用信息,比如项目名称、描述和其他必要的信息。如果你熟悉Maven pom.xml文件的内容,就可以识别出大多数的输入域条目最终都会成为Maven的构建规范。对于Taco Cloud应用来说,我们可以按照图1.3的样子来填充对话框。
图1.3 为Taco Cloud应用指定通用的项目信息
向导的下一页会让我们选择要添加到项目中的依赖(见图1.4)。注意,在对话框的顶部,我们可以选择项目要基于哪个Spring Boot版本。它的默认值是最新的可用版本。一般情况下,最好使用这个默认的值,除非你需要使用不同的版本。
至于依赖项本身,你可以打开各个区域并查找所需的依赖项,也可以在Available顶部的搜索框中对依赖进行搜索。对于Taco Cloud应用来说,我们最初的依赖项如图1.4所示。
图1.4 选择Starter依赖
现在,你可以点击Finish来生成项目并将其添加到工作空间中。但是,如果你还想多体验一些,那么可以再次点击Next,看一下新Starter项目向导的最后一页,如图1.5所示。
图1.5 指定备用的Initializr地址
默认情况下,新项目的向导会调用Spring Initializr来生成项目。通常情况下,没有必要覆盖默认值,这也是我们可以在向导的第二页直接点击Finish的原因。但是,如果你基于某种原因托管了自己的Initializr克隆版本(可能是本地机器上的副本或者公司防火墙内部运行的自定义克隆版本),那么你可能需要在点击Finish之前修改Base Url输入域,使其指向自己的Initializr实例。
在点击Finish之后,项目会从Initializr下载并加载到工作空间中。此时,要等待它加载和构建,然后你就可以开始开发应用功能了。下面我们看一下Initializr都为我们提供了什么。
项目加载到IDE中之后,我们将其展开,看一下其中都包含什么内容。图1.6展现了Spring Tool Suite中已展开的Taco Cloud项目。
图1.6 Spring Tool Suite中所展现的初始Spring项目结构
你可能已经看出来了,这就是一个典型的Maven或Gradle项目结构,其中应用的源码放到了“src/main/java”中,测试代码放到了“src/test/java”中,而非Java的资源放到了“src/main/resources”。在这个项目结构中,我们需要注意以下几点。
随着Taco Cloud应用功能的增长,我们会不断使用Java代码、图片、样式表、测试以及其他附属内容来充实这个项目结构。不过,在此之前,我们先看一下Spring Initializr提供的几个条目。
在填充Initializr表单的时候,我们声明项目要使用Maven来进行构建。因此,Spring Initializr所生成的pom.xml文件已经包含了我们所选择的依赖。程序清单 1.1 展示了Initializr为我们提供的完整pom.xml。
程序清单1.1 初始的Maven构建规范
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>sia</groupId>
<artifactId>taco-cloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> ⇽--- 打包为JAR
<name>taco-cloud</name>
<description>Taco Cloud Example</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version> ⇽--- Spring Boot的版本
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>
UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>
UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency> ⇽--- Starter依赖
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>htmlunit-driver</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin> ⇽--- Spring Boot插件
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在pom.xml文件中,我们第一个需要注意的地方就是<packaging>。我们选择了将应用构建成一个可执行的JAR文件,而不是WAR文件。这可能是你所做出的最奇怪的选择之一,对Web应用来说尤为如此。毕竟,传统的Java Web应用都是打包成WAR文件,JAR只是用来打包库和较为少见的桌面UI应用的。
打包为JAR文件是基于云思维做出的选择。尽管WAR文件非常适合部署到传统的Java应用服务器上,但对于大多数云平台来说它们并不是理想的选择。有些云平台(比如Cloud Foundry)也能够部署和运行WAR文件,但是所有的Java云平台都能够运行可执行的JAR文件。因此,Spring Initializr默认会使用基于JAR的打包方式,除非我们明确告诉它采用其他的方式。
如果你想要将应用部署到传统的Java应用服务器上,那么需要选择使用基于WAR的打包方式并要包含一个Web初始化类。在第2章中,我们将会更详细地了解如何构建WAR文件。
接下来,请留意<parent>元素,更具体来说是它的<version>子元素。这表明我们的项目要以spring-boot-starter-parent作为其父POM。除了其他的一些功能之外,这个父POM为Spring项目常用的一些库提供了依赖管理,现在你不需要指定它们的版本,因为这是通过父POM来管理的。这里的2.0.4.RELEASE表明要使用Spring Boot 2.0.4,所以会根据这个版本的Spring Boot定义来继承依赖管理。
既然我们谈到了依赖的话题,那么需要注意在<dependencies>元素下声明了 3 个依赖。在某种程度上,你可能会对前两个更熟悉一些。它们直接对应我们在Spring Tool Suite新项目向导中点击Finish之前所选择的Web和Thymeleaf依赖。第三个依赖提供了很多有用的测试功能。我们没有必要在专门的复选框中选择它,因为Spring Initializr假定你将会编写测试(希望你会正确地开展这项工作)。
你可能也会注意到这 3 个依赖的artifact ID上都有starter这个单词。Spring Boot starter依赖的特别之处在于它们本身并不包含库代码,而是传递性地拉取其他的库。这种starter依赖主要有3个好处。
最后,构建规范还包含一个Spring Boot插件。这个插件提供了一些重要的功能。
谈到了主类,我们打开它看一下。
因为我们将会通过可执行JAR文件的形式来运行应用,所以很重要的一点就是要有一个主类,它将会在JAR运行的时候被执行。我们同时还需要一个最小化的Spring配置,以引导该应用。这就是TacoCloudApplication类所做的事情,如程序清单1.2所示。
程序清单1.2 Taco Cloud的引导类
package tacos;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication ⇽--- Spring Boot应用
public class TacoCloudApplication {
public static void main(String[] args) {
SpringApplication.run(TacoCloudApplication.class, args); ⇽--- 运行应用
}
}
尽管在TacoCloudApplication中只有很少的代码,但是它包含了很多的内容。其中,最强大的一行代码也是最短的。@SpringBootApplication注解明确表明这是一个Spring Boot应用。但是,@SpringBootApplication远比看上去更强大。
@SpringBootApplication是一个组合注解,它组合了3个其他的注解。
TacoCloudApplication另外一个很重要的地方是它的main()方法。这是JAR文件执行的时候要运行的方法。在大多数情况下,这个方法都是样板代码,我们编写的每个Spring Boot应用都会有一个类似或完全相同的方法(类名不同则另当别论)。
这个main()方法会调用SpringApplication中静态的run()方法,后者会真正执行应用的引导过程,也就是创建Spring的应用上下文。在传递给run()的两个参数中,一个是配置类,另一个是命令行参数。尽管传递给run()的配置类不一定要和引导类相同,但这是最便利和最典型的做法。
你可能并不需要修改引导类中的任何内容。对于简单的应用程序来说,你可能会发现在引导类中配置一两个组件是非常方便的,但是对于大多数应用来说,最好还是要为没有实现自动配置的功能创建一个单独的配置类。在本书的整个过程中,我们将会创建多个配置类,所以请继续关注后续的细节。
测试是软件开发的重要组成部分。鉴于此,Spring Initializr为我们提供了一个测试类作为起步。程序清单1.3展现了这个测试类的概况。
程序清单1.3 应用测试类的概况
package tacos;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class) ⇽--- 使用Spring的运行器
@SpringBootTest ⇽--- Spring Boot测试
public class TacoCloudApplicationTests {
@Test ⇽--- 测试方法
public void contextLoads() {
}
}
TacoCloudApplicationTests类中的内容并不多:这个类中只有一个空的测试方法。即便如此,这个测试类还是会执行必要的检查,确保Spring应用上下文能够成功加载。如果你所做的变更导致Spring应用上下文无法创建,那么这个测试将会失败,你就可以做出反应来解决相关的问题了。
另外,注意这个类带有@RunWith(SpringRunner.class)注解。@RunWith是JUnit的注解,它会提供一个测试运行器(runner)来指导JUnit如何运行测试。可以将其想象为给JUnit应用一个插件,以提供自定义的测试行为。在本例中,为JUnit提供的是SpringRunner,这是一个Spring提供的测试运行器,它会创建测试运行所需的Spring应用上下文。
如果你已经熟悉如何编写Spring测试或者见过其他一些基于Spring的测试类,那么你可能见过名为SpringJUnit4ClassRunner的测试运行器。SpringRunner是SpringJUnit4ClassRunner的别名,是在Spring 4.3中引入的,以便于移除对特定JUnit版本的关联(比如,JUnit 4)。毫无疑问,这个别名更易于阅读和输入。
@SpringBootTest会告诉JUnit在启动测试的时候要添加上Spring Boot的功能。从现在开始,我们可以将这个测试类视同为在main()方法中调用SpringApplication.run()。在这本书中,我们将会多次看到@SpringBootTest,而且会不断见识它的威力。
最后,就是测试方法本身了。尽管@RunWith(SpringRunner.class)和@SpringBootTest会为测试加载Spring应用上下文,但是如果没有任何测试方法,那么它们其实什么事情都没有做。即便没有任何断言或代码,这个空的测试方法也会提示这两个注解完成了它们的工作并成功加载Spring应用上下文。如果这个过程中有任何问题,那么测试都会失败。
此时,我们已经看完了Spring Initializr为我们提供的代码。我们看到了一些用来开发Spring应用程序的基础样板,但是还没有编写任何代码。现在是时候启动IDE、准备好键盘并向Taco Cloud应用程序添加一些自定义的代码了。
因为是刚刚开始,所以我们首先为Taco Cloud做一些小的变更,但是这些变更会展现Spring的很多优点。在刚开始的时候,比较合适的做法是为Taco Cloud应用添加一个主页。在添加主页时,我们将会创建两个代码构件:
测试是非常重要的,所以我们还会编写一个简单的测试类来测试主页。但是,要事优先,我们需要先编写控制器。
Spring自带了一个强大的Web框架,名为Spring MVC。Spring MVC的核心是控制器(controller)的理念。控制器是处理请求并以某种方式进行信息响应的类。在面向浏览器的应用中,控制器会填充可选的数据模型并将请求传递给一个视图,以便于生成返回给浏览器的HTML。
在第2章中,我们将会学习更多关于Spring MVC的知识。现在,我们会编写一个简单的控制器类以处理对根路径(比如,“/”)的请求,并将这些请求转发至主页视图,在这个过程中不会填充任何的模型数据。程序清单 1.4 展示了这个简单的控制器类。
程序清单1.4 主页控制器
package tacos;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller ⇽--- 控制器
public class HomeController {
@GetMapping("/") ⇽--- 处理对根路径“/”的请求
public String home() {
return "home"; ⇽--- 返回视图名
}
}
可以看到,这个类带有@Controller。就其本身而言,@Controller并没有做太多的事情。它的主要目的是让组件扫描将这个类识别为一个组件。因为HomeController带有@Controller,所以Spring的组件扫描功能会自动发现它,并创建一个HomeController实例作为Spring应用上下文中的bean。
实际上,有一些其他的注解与@Controller有着类似的目的(包括@Component、@Service和@Repository)。你可以为HomeController添加上述的任意其他注解,其作用是完全相同的。但是,在这里选择使用@Controller更能描述这个组件在应用中的角色。
home()是一个简单的控制器方法。它带有@GetMapping注解,表明如果针对“/”发送HTTP GET请求,那么这个方法将会处理请求。该方法所做的只是返回String类型的home值。
这个值将会被解析为视图的逻辑名。视图如何实现取决于多个因素,但是因为Thymeleaf位于类路径中,所以我们可以使用Thymeleaf来定义模板。
你可能会想为什么要选择Thymeleaf作为模板引擎呢?为何不使用JSP?为何不使用FreeMarker?为何不选择其他的几个可选方案?
简单来说,我必须要做出选择,我喜欢Thymeleaf,相对于其他的方案,我会优先使用它。即便JSP是更加显而易见的选择,但是组合使用JSP和Spring Boot需要克服一些挑战。我不想脱离第1章的内容定位,所以在这里就此打住。在第2章中,我们将会看一下其他的模板方案,其中也包括JSP。
模板名称是由逻辑视图名派生而来的,再加上“/templates/”前缀和“.html”后缀。最终形成的模板路径将是“/templates/home.html”。所以,我们需要将模板放到项目的“/src/main/resources/templates/home.html”目录中。现在,就让我们来创建这个模板。
为了让主页尽可能简单,除了欢迎用户访问站点之外,它不会做其他的任何事情。程序清单1.5展现了基本的Thymeleaf模板,它定义了Taco Cloud的主页。
程序清单1.5 Taco Cloud主页模板
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Taco Cloud</title>
</head>
<body>
<h1>Welcome to...</h1>
<img th:src="@{/images/TacoCloud.png}"/>
</body>
</html>
这个模板并没有太多需要讨论的。唯一需要注意的一行代码是用于展现Taco Cloud Logo的<img>标签。它使用了Thymeleaf的th:src属性和@{...}表达式,以便于引用相对于上下文路径的图片。除此之外,它就是一个Hello World页面。
但是,我们再讨论一下这个图片。我将定义Taco Cloud Logo的工作留给你,你需要将它放到应用的正确位置中。
图片是使用相对于上下文的“/images/TacoCloud.png”路径来进行引用的。回忆一下我们的项目结构,像图片这样的静态资源是放到“/src/main/resources/static”文件夹中的。这意味着,在项目中,Taco Cloud Logo图片必须要位于“/src/main/resources/static/ images/TacoCloud.png”。
我们已经有了一个处理主页请求的控制器并且有了渲染主页的模板,现在基本就可以启动应用来看一下它的效果了。在此之前,我们先看一下如何为控制器编写测试。
在测试Web应用时,对HTML页面的内容进行断言是比较困难的。幸好Spring对测试提供了强大的支持,这使得测试Web应用变得非常简单。
对于主页来说,我们所编写的测试在复杂性上与主页本身差不多。测试需要针对根路径“/”发送一个HTTP GET请求并期望得到成功结果,其中视图名称为home并且结果内容包含“Welcome to...”。程序清单1.6能够完成该任务。
程序清单1.6 针对主页控制器的测试
package tacos;
import static org.hamcrest.Matchers.containsString;
import static
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static
org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class) ⇽--- 针对HomeController的Web测试
public class HomeControllerTest {
@Autowired
private MockMvc mockMvc; ⇽--- 注入MockMvc
@Test
public void testHomePage() throws Exception {
mockMvc.perform(get("/")) ⇽--- 发起对“/”的GET
.andExpect(status().isOk()) ⇽--- 期望得到HTTP 200
.andExpect(view().name("home")) ⇽--- 期望得到home视图
.andExpect(content().string( ⇽--- 期望包含“Welcome to...”
containsString("Welcome to...")));
}
}
对于这个测试,我们首先注意到的可能就是它使用了与TacoCloudApplicationTests类不同的注解。HomeControllerTest没有使用@SpringBootTest标记,而是添加了@WebMvcTest注解。这是Spring Boot所提供的一个特殊测试注解,它会让这个测试在Spring MVC应用的上下文中执行。更具体来讲,在本例中,它会将HomeController注册到Spring MVC中,这样的话,我们就可以向它发送请求了。
@WebMvcTest同样会为测试Spring MVC应用提供Spring环境的支持。尽管我们可以启动一个服务器来进行测试,但是对于我们的场景来说,仿造一下Spring MVC的运行机制就可以。测试类被注入了一个MockMvc,能够让测试实现mockup。
通过testHomePage()方法,我们定义了针对主页想要执行的测试。它首先使用MockMvc对象对“/”(根路径)发起HTTP GET请求。对于这个请求,我们设置了如下的预期:
如果在MockMvc对象发送请求之后,这些期望有不满足的话,那么这个测试会失败。但是,我们的控制器和模板引擎在编写时都满足了这些预期,所以测试应该能够通过,并且带有成功的图标——至少能够看到一些绿色的背景,表明测试通过了。
控制器已经编写好了,视图模板也已经创建完毕,而且我们还通过了测试,看上去我们已经成功实现了主页。尽管测试已经通过了,但是如果能够在浏览器中看到结果那会更有成就感,毕竟这才是Taco Cloud的客户所能看到的效果。接下来,我们构建应用并运行它。
就像初始化Spring应用有多种方式一样,运行Spring应用也有多种方式。如果你愿意的话,可以翻到附录部分,以了解运行Spring Boot应用的一些通用方式。
因为我们选择了使用Spring Tool Suite来初始化和处理项目,所以可以借助名为Spring Boot Dashboard的便捷功能来帮助我们在IDE中运行应用。Spring Boot Dashboard的表现形式是一个Tab标签,通常会位于IDE窗口的左下角附近。图1.7展现了一个带有标注的Spring Boot Dashboard截屏。
图1.7 Spring Boot Dashboard的重点功能
图1.7包含了一些最有用的细节,但是我不想花太多时间介绍Spring Boot Dashboard支持的所有功能。对我们来说,现在最重要的事情是需要知道如何使用它来运行TacoCloud应用。确保taco-cloud应用程序在项目列表中能够显示出来(这是图1.7中显示的唯一应用),然后点击启动按钮(上方工具栏最左边的按钮,也就是带有绿色三角形和红色正方形的按钮),应用程序应该就能立即启动。
在应用启动的过程中,你会在控制台看到一些Spring ASCII码,随后会是描述应用启动各个步骤的日志条目。在控制台输出的最后,你将会看到一条Tomcat已经在port(s): 8080 (http)启动的日志,这意味着此时你可以打开Web浏览器并导航至主页,这样就能看到我们的劳动成果了。
稍等一下!刚才说启动Tomcat?但是我们是什么时候将应用部署到Tomcat的呢?
Spring Boot应用的习惯做法是将所有它需要的东西都放到一起,没有必要将其部署到某种应用服务器中。在这个过程中,我们根本没有将应用部署到Tomcat中……Tomcat是我们应用的一部分!(在1.3.6小节,我会介绍Tomcat是如何成为我们应用的一部分的。)
现在,应用已经启动起来了,打开Web浏览器并访问http://localhost:8080(或者在Spring Boot Dashboard中点击上方的地球样式的按钮,如图1.7所示),你将会看到如图1.8所示的界面。如果你设计了自己的Logo图片,那么显示效果可能会有所不同。但是,与图1.8相比,应该不会有太大的差异。
图1.8 Taco Cloud主页
看上去似乎并不太美观,但这不是一本关于平面设计的书。目前,略显简陋的主页外观已经足够了,它为我们学习Spring打下了一个良好的开端。
到现在为止,我一直没有提及DevTools。在初始化项目的时候,我们将其作为一个依赖添加了进来。在最终生成的pom.xml文件中,它表现为一个依赖项。甚至Spring Boot Dashboard都显示项目启用了DevTools。那么,DevTools是什么,它又能为我们做些什么呢?接下来,让我们快速浏览一下DevTools最有用的一些特性。
顾名思义,DevTools为Spring开发人员提供了一些便利的开发期工具,其中包括:
需要注意,DevTools并不是IDE插件,它也不需要你使用特定的IDE。在Spring Tool Suite、IntelliJ IDEA和NetBeans中,它都能很好地运行。另外,因为它的目的是仅仅用于开发,所以能够很智能地在生产环境中把自己禁用掉。(我们将会在第19章学习应用部署的时候再讨论它是如何做到这一点的。)现在,我们主要关注Spring Boot DevTools最有用的特性,先从应用的自动重启开始。
如果将DevTools作为项目的一部分,那么你可以看到,当对项目中的Java代码和属性文件做出修改后,这些变更稍后就能发挥作用。DevTools会监控变更,当它看到有变化的时候,将会自动重启应用。
更准确地说,当DevTools运行的时候,应用程序会被加载到Java虚拟机(Java virtual Machine,JVM)两个独立的类加载器中。其中一个类加载器会加载你的Java代码、属性文件以及项目中“src/main/”路径下几乎所有的内容。这些条目很可能会经常发生变化。另外一个类加载器会加载依赖的库,这些库不太可能经常发生变化。
当探测到变更的时候,DevTools只会重新加载包含项目代码的类加载器,并重启Spring的应用上下文,在这个过程中另外一个类加载器和JVM会原封不动。这个策略非常精细,但是它能减少应用启动的时间。
这种策略的一个不足之处就是自动重启无法反映依赖项的变化。这是因为包含依赖库的类加载器不会自动重新加载。这意味着每当我们在构建规范中添加、变更或移除依赖的时候,为了让变更生效,我们需要重新启动应用。
默认情况下,像Thymeleaf和FreeMarker这样的模板方案在配置时会缓存模板解析的结果。这样的话,在为每个请求提供服务的时候,模板就不用重新解析了。在生产环境中,这是一种很好的方式,因为它会带来一定的性能收益。
但是,在开发期,缓存模板就不太好了。在应用运行的时候,如果缓存模板,那么我们刷新浏览器就无法看到模板变更的效果了。即便我们对模板做了修改,在应用重启之前,缓存的模板依然会有效。
DevTools通过禁用所有模板缓存解决了这个问题。你可以对模板进行任意数量的修改,只需要刷新一下浏览器就能看到结果。
如果你像我这样,连浏览器的刷新按钮都懒得点,那么对代码做出变更之后,马上在浏览器中看到结果就好了。幸运的是,DevTools有一些特殊的功能可以供我们使用。
DevTools在运行的时候,它会和你的应用程序一起,同时自动启动一个LiveReload服务器。LiveReload服务器本身并没有太大的用处。但是,当它与LiveReload浏览器插件结合起来的时候,就能够在模板、图片、样式表、JavaScript等(实际上,几乎涵盖为浏览器提供服务的所有内容)发生变化的时候自动刷新浏览器。
LiveReload有针对Google Chrome、Safari和Firefox的浏览器插件(要对Internet Explorer和Edge粉丝说声抱歉)。请访问LiveReload官网,以了解如何为你的浏览器安装LiveReload。
虽然我们的项目还没有使用数据库,但是这种情况在第3章中就会发生变化。如果你使用H2数据库进行开发,DevTools将会自动启用H2。这样的话,我们可以通过Web浏览器进行访问。你只需要让浏览器访问http://localhost:8080/h2-console,就能看到应用所使用的数据。
此时,我们已经编写了一个尽管非常简单却很完整的Spring应用。在本书中,我们将会不断扩展它。现在,我们要回过头来看一下都完成了哪些工作以及Spring发挥了什么作用。
回想一下我们是怎样完成这一切的。简短来说,在构建基于Spring的Taco Cloud应用的过程中,我们执行了如下步骤:
这些步骤都非常简单直接,对吧?除了初始化应用的第一个步骤之外,我们所做的每一个操作都专注于生成主页的目标。
实际上,我们所编写的每行代码都致力于实现这个目标。除了Java import语句之外,我只能在控制器中找到两行Spring相关的代码,而在视图模板中一行Spring相关的代码都没有。尽管测试类的大部分内容都使用了Spring对测试的支持,但是它在测试的上下文中似乎没有那么具有侵入性。
这是使用Spring进行开发的一个重要收益。你可以只关注满足应用需求的代码,无须考虑如何满足框架的需求。尽管我们偶尔还是需要编写一些框架特定的代码,但是它们通常只占整个代码库很小的一部分。正如我在前文所述,Spring(以及Spring Boot)可以视为感受不到框架的框架(frameworkless framework)。
但是这又是如何运行起来的呢?Spring在幕后做了些什么来保证应用的需求能够得到满足呢?要理解Spring到底做了些什么,我们首先来看一下构建规范。
在pom.xml文件中,我们声明了对Web和Thymeleaf starter的依赖。这两项依赖会传递引入大量其他的依赖,包括:
它还引入了Spring Boot的自动配置库。当应用启动的时候,Spring Boot的自动配置将会探测到这些库,并自动完成如下功能:
简而言之,自动配置功能完成了所有的脏活累活,让我们能够集中精力编写实现应用功能的代码。如果你问我对此的观点,那么我认为这是一个很好的安排!
我们的Spring之旅才刚刚开始。Taco Cloud应用程序只涉及Spring所提供功能的一小部分。在开始下一步之前,我们先整体了解一下Spring,看看在我们的路途中都会有哪些地标。
要想了解Spring的整体状况,只需查看完整版本的Spring Initializr Web表单上的那一堆复选框列表即可。它列出了100多个可选的依赖项,所以我不会在这里列出所有选项,也不会提供截图,但我鼓励你去看一看。同时,在这里我会简单介绍一些重点的项目。
如你所料,Spring核心框架是Spring领域中一切的基础。它提供了核心容器和依赖注入框架,另外还提供了一些其他重要的特性。
其中有一项是Spring MVC,也就是Spring的Web框架。你已经看到了如何使用Spring MVC来编写控制器类以处理Web请求。但是,你还没看到的是,Spring MVC还能用来创建REST API,以生成非HTML的输出。在第2章中,我们将会更深入地介绍Spring MVC,并在第6章重新学习如何使用它来创建REST API。
Spring核心框架还提供了一些对数据持久化的基础支持,尤其是基于模板的JDBC支持。在第3章中,你将会看到如何使用JdbcTemplate。
在最新版本的Spring中,还添加了对反应式(reactive)风格编程的支持,其中包括名为Spring WebFlux的新反应式Web框架,这个框架大量借鉴了Spring MVC。在第3部分中,我们将会学习Spring反应式编程模型,并在第11章专门学习Spring WebFlux。
我们已经看到了Spring Boot带来的很多收益,包括starter依赖和自动配置。在本书中,我们会尽可能多地使用Spring Boot,并避免任何形式的显式配置,除非显式配置是绝对必要的。除了starter依赖和自动配置,Spring Boot还提供了大量其他有用的特性:
除此之外,Spring Boot还提供了一个基于Groovy脚本的编程模型,称为Spring Boot 命令行接口(Command-Line Interface,CLI)。使用Spring Boot CLI,我们可以将整个应用程序编写为Groovy脚本的集合,并通过命令行运行它们。我们不会花太多时间介绍Spring Boot CLI,但是当它匹配我们的需求时,我们会偶尔提及它。
Spring Boot已经成为Spring开发中不可或缺的一部分,很难想象如果没有它我该如何开发Spring应用程序。因此,本书采用以Spring Boot为核心的视角。当我介绍Spring Boot所做的事情时,你可能会发现我却使用了Spring这个词。
尽管Spring核心框架提供了基本的数据持久化支持,但是Spring Data提供了非常令人惊叹的功能:将应用程序的数据repository定义为简单的Java接口,在定义驱动存储和检索数据的方法时使用一种命名约定即可。
此外,Spring Data能够处理多种不同类型的数据库,包括关系型数据库(JPA)、文档数据库(Mongo)、图数据库(Neo4j)等。在第3章中,我们将使用Spring Data为Taco Cloud应用程序创建repository。
应用程序的安全性一直是一个重要的话题,而且正在变得越来越重要。幸运的是,Spring有一个健壮的安全框架,名为Spring Security。
Spring Security解决了应用程序通用的安全性需求,包括身份验证、授权和API安全性。Spring Security的范围太大,在本书中无法得到充分的介绍,但是我们将在第4章和第11章中讨论一些常见的使用场景。
从一定程度上来讲,大多数应用程序都需要与其他应用甚至本应用中的其他组件进行集成。在这方面,有一些应用程序集成模式可以解决这些需求。Spring Integration和Spring Batch为基于Spring的应用程序提供了这些模式的实现。
Spring Integration解决了实时集成问题。在实时集成中,数据在可用时马上就会得到处理。相反,Spring Batch解决的则是批处理集成的问题,在此过程中,数据可以收集一段时间,直到某个触发器(可能是一个时间触发器)发出信号,表示该处理批量数据了才会对数据进行批处理。我们将会在第9章中研究Spring Batch和Spring Integration。
在撰写本书的时候,应用程序开发领域正在进入一个新的时代,我们不再将应用程序作为单个部署单元来开发,而是使用由微服务组成的多个独立部署单元来组合形成应用程序。
微服务是一个热门话题,解决了开发期和运行期的一些实际问题。然而,在这样做的过程中,它们也面临着自己所带来的挑战。这些挑战将由Spring Cloud直面解决,Spring Cloud是使用Spring开发云原生应用程序的一组项目。
Spring Cloud覆盖了很多领域,本书不可能面面俱到,我们将在第13~15章中研究Spring Cloud的一些常见组件。要更全面地研究Spring Cloud,我建议阅读John Carnell的Spring Microservices in Action一书[2](Manning,2017)。
[1] 为了行文简洁,同时保持与示例应用中Web页面展现的一致性,我们后文不再将taco翻译为墨西哥煎玉米卷,而是直接使用taco这一叫法。——译者注
[2] 该书中文版《Spring微服务实战》已由人民邮电出版社出版(ISBN978-7-115-48118-4)。