不知不觉间,已经参与了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 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开发人员。

第38章 新模式

设计模式已经诞生多年,“23”这个数字也在逐渐变大,这是好事情,表明我们软件界正在积累、汇编我们的知识和经验。一个模式的提出和成熟需要一段时间,因此本章挑选了5 个大家时常使用,但又经常忽视的新模式进行讲解,即规格模式、对象池模式、雇工模式、 黑板模式、空对象模式。希望这5个新模式能够帮助大家解决更多的实际开发难题。

第33章 跨战区PK

创建类模式描述如何创建对象,行为类模式关注如何管理对象的行为,结构类模式则着重于如何建立一个软件结构,虽然三种模式的着重点不同,但是在实际应用中还是有重叠的,会出现一种模式适用、另外一种模式也适用的情况,我们到底该选用哪一个设计模式呢?本章就带领读者进入不同类设计模式PK的世界中,让你清晰地认识到各个模式的不同点以及它们的特长。

第32章 行为类模式大PK

行为类模式包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式。该组真可谓是人才济济,高手如云。行为类模式的11个模式基本上都是大家耳熟能详的,而且它们之间还有很多的相似点,特别是一些扩展部分就更加相似了,我们挑选几个比较重要的模式进行对比说明。

第30章 创建类模式大PK

创建类模式包括工厂方法模式、建造者模式、抽象工厂模式、单例模式和原型模式,它们都能够提供对象的创建和管理职责。其中的单例模式和原型模式非常容易理解,单例模式是要保持在内存中只有一个对象,原型模式是要求通过复制的方式产生一个新的对象,这两个不容易混淆。剩下的就是工厂方法模式、抽象工厂模式和建造者模式了,这三个之间有较多的相似性。

通过例子,我们可以看出代理模式和装饰模式有非常相似的地方,甚至代码实现都非常相似,特别是装饰模式中省略抽象装饰角色后,两者代码基本上相同,但是还是有细微的差别。

代理模式是把当前的行为或功能委托给其他对象执行,代理类负责接口限定:是否可以调用真实角色,以及是否对发送到真实角色的消息进行变形处理,它不对被主题角色(也就是被代理类)的功能做任何处理,保证原汁原味的调用。代理模式使用到极致开发就是AOP,这是各位采用Spring架构开发必然要使用到的技术,它就是使用了代理和反射的技术。

装饰模式是在要保证接口不变的情况下加强类的功能,它保证的是被修饰的对象功能比原始对象丰富(当然,也可以减弱),但不做准入条件判断和准入参数过滤,如是否可以执行类的功能,过滤输入参数是否合规等,这不是装饰模式关心的。

代理模式在Java的开发中俯拾皆是,是大家非常熟悉的模式,应用非常广泛,而装饰模式是一个比较拘谨的模式,在实际应用中接触比较少,但是也有不少框架项目使用了装饰模式,例如在JDK的java.io.*包中就大量使用装饰模式,类似如下的代码:

1
OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"))

这是装饰模式的一个典型应用,使用DataOutputStream封装了一个FileOutputStream,以方便进行输出流处理。

第31章 结构类模式大PK

结构类模式包括适配器模式、桥梁模式、组合模式、装饰模式、门面模式、享元模式和代理模式。为什么叫结构类模式呢?因为它们都是通过组合类或对象产生更大结构以适应更高层次的逻辑需求。我们来分析以下几个模式的相似点和不同点。

27.3 解释器模式的应用

27.3.1 解释器模式的优点

解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。

27.3.2 解释器模式的缺点

  • 解释器模式会引起类膨胀

每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。

  • 解释器模式采用递归调用方法

每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个断点一个断点地调试下去,直到最小的语法单元。

  • 效率问题

解释器模式由于使用了大量的循环和递归,效率是一个不容忽视的问题,特别是一用于解析复杂、冗长的语法时,效率是难以忍受的。

27.3.3 解释器模式使用的场景

  • 重复发生的问题可以使用解释器模式

例如,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素是相同的,按照解释器的说法就是终结符表达式都是相同的,但是非终结符表达式就需要制定了。在这种情况下,可以通过程序来一劳永逸地解决该问题。

  • 一个简单语法需要解释的场景

为什么是简单?看看非终结表达式,文法规则越多,复杂度越高,而且类间还要进行递归调用(看看我们例子中的栈)。想想看,多个类之间的调用你需要什么样的耐心和信心去排查问题。因此,解释器模式一般用来解析比较标准的字符集,例如SQL语法分析,不过该部分逐渐被专用工具所取代。

在某些特用的商业环境下也会采用解释器模式,我们刚刚的例子就是一个商业环境,而且现在模型运算的例子非常多,目前很多商业机构已经能够提供出大量的数据进行分析。

27.3.4 解释器模式的注意事项

尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,弥补Java编译型语言的不足。我们在一个银行的分析型项目中就采用JRuby进行运算处理,避免使用解释器模式的四则运算,效率和性能各方面表现良好。

27.4 最佳实践

解释器模式在实际的系统开发中使用得非常少,因为它会引起效率、性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,如一些数据分析工具、报表设计工具、 科学计算工具等,若你确实遇到“一种特定类型的问题发生的频率足够高”的情况,准备使用解释器模式时,可以考虑一下Expression4J、MESP(Math Expression String Parser)、Jep等开源的解析工具包(这三个开源产品都可以通过百度、Google搜索到,请读者自行查询), 功能都异常强大,而且非常容易使用,效率也还不错,实现大多数的数学运算完全没有问题,自己没有必要从头开始编写解释器。有人已经建立了一条康庄大道,何必再走自己的泥泞小路呢?

29.4 最佳实践

大家对类的继承有什么看法吗?继承的优点有很多,可以把公共的方法或属性抽取,父类封装共性,子类实现特性,这是继承的基本功能。缺点有没有?有!即强侵入,父类有一个方法,子类也必须有这个方法。这是不可选择的,会带来扩展性的问题。我举个简单的例子来说明:Father类有一个方法A,Son继承了这个方法,然后GrandSon也继承了这个方法, 问题是突然有一天Son要重写父类的这个方法,他敢做吗?绝对不敢!GrandSon要用从Father 继承过来的方法A,如果你修改了,那就要修改Son和GrandSon之间的关系,那这个风险就太大了!

这里讲的这个桥梁模式就是这一问题的解决方法,桥梁模式描述了类间弱关联关系,还说上面的那个例子,Father类完全可以把可能会变化的方法放出去,Son子类要拥有这个方法很简单,桥梁搭过去,获得这个方法,GrandSon也一样,即使你Son子类不想使用这个方法也没关系,对GrandSon不产生影响,它不是从Son中继承来的方法!

不能说继承不好,它非常好,但是有缺点,我们可以扬长避短,对于比较明确不发生变化的,则通过继承来完成;若不能确定是否会发生变化的,那就认为是会发生变化,则通过桥梁模式来解决,这才是一个完美的世界。