这几天笔者阅读了一下Spring In Action 4,感受仍是有必定的收获的。在以前的项目上,只会简单地使用Spring MVC,对于不少概念并不理解。如今看看Spring的一些概念,如DI和AOP,以及Spring的各个模块,对于Spring这一总体框架能够有较深的了解。java
这篇文章将主要以翻译的形式来说解Spring,中间夹杂对于原文的理解,以及代码实现,相信对于读者会带来必定的帮助,也是对本身阅读的一个总结。若是读者有不一样的看法,还望能够共同探讨一下。顺带推荐一下Manning的In Action系列,笔者此前读了《机器学习实战》,外加上如今看的《Spring实战》,感受真心不错,国外的许多书籍,相比国内简直是良心之做。可是,中文译本的更新永远没法遇上英文本来的进度,就拿Spring来讲,第三版的中文译本还停留在Spring 3,然而Spring已经升级到了4版本,而英文本来紧跟技术进度,相比之下,第四版的中文译本,还不知要到何年何月。固然,文章的翻译除了要花费长时间的努力,还须要译者对技术有较为深刻的了解,在此,仍是要感谢那些辛勤的贡献者。笔者翻译此书的目的,是为了在读书的同时,可以总结到一些有用的知识,并指望能给读者带来必定的收获。web
(题外话:因为开通了邮箱通知功能,近期Windows10任务栏右边老是会显示邮件提示。有一些问题,因为笔者也没有去深度地探究Spring MVC这个框架,也没法作出具体的回答,但相信前面几篇文章对于入门开发仍是存在必定的帮助的。真正的项目开发确定会比教程中更加复杂,由于除了业务逻辑,还有许多重要的问题,如高并发、多线程,以及相当重要的安全问题,是须要考虑进来的。)spring
接下来,进入正题。。。(转载请说明出处:Gaussic,AI研究生)数据库
Spring能作不少事情。可是对于Spring的全部的奇妙功能,它们的主要特征是依赖注入(dependency indection, DI)和面向方面编程(aspect-oriented programming, AOP,也能够译为面向切面编程)。
express
第一章,付诸行动(Springing into action),我将带给你一个Spring 框架的快速概览,包括Spring中的DI和AOP的快速概览,并展现它们是如何帮助下降应用组件(components)的耦合度的。
编程
第二章,装配Bean(Wiring Beans),咱们将更为深刻地探讨如何将应用的各个组件拼凑在一块儿。咱们将着眼于自动配置(automatic configuration)、基于Java的配置,和XML配置,这三种Spring提供的配置方法。
设计模式
第三章,高级装配(Advanced Wiring),将展现一些小把戏和手段,它们将帮助你深度挖掘Spring的强大能力,包括有条件的配置(conditional configuration),用以处理自动装配(autowiring)、范围控制(scoping)以及Spring表达式语言(Spring Expression Language, Spring EL)。
缓存
第四章,面向方面的Spring(Aspect-oriented Spring),探究如何使用Spring的AOP特征来解除系统级的服务(例如安全和审计(auditing))以及它们所服务的对象之间的耦合度。这一章为以后的章节搭建了一个起始平台,例如第九、13和14章,它们讲解了如何在声明式安全(declarative security)和缓存。
安全
对于Java开发者来讲,如今是一个很好的时期。
服务器
在20年的历史中,Java经历过一些好的时期以及很差的时期。尽管存在着一些粗糙点(rough spots),例如applets, Enterprise JavaBeans(EJB), Java Data Objects(JDO),以及数不尽的日志框架,Java仍然拥有着一个丰富的多样化的历史,做为许多企业级软件的平台而言。并且,Spring在这一段历史上占有很重要的一笔。
在早期,Spring做为更加剧量级的Java技术(特别是EJB)的替代品被创造出来。相比EJB,Spring提供了一个更轻量更简洁的的编程模型。它给予了普通Java对象(POJO)更增强大的能力,而这些能力是EJB和一些其余Java规格才拥有的。
随着时间的过去,EJB和J2EE获得了发展。EJB开始自身提供简单的面向POJO的变成模型。如今EJB利用了诸如依赖注入和面向方面变成的思想,能够说其灵感来自于Spring的成功。
虽然J2EE(如今被称为JEE)能够遇上Spring,可是Spring从未中止前进的脚步。Spring持续的发展各个领域。而这些领域,即便是如今,JEE才刚刚开始开始探索,甚至还未引入。移动开发,社交API集成,NoSQL数据库,云计算,以及大数据只是Spring引入的一小部分领域。并且,将来的Spring依然一片光明。
正如我所说,对于开发者来讲,如今是一个很好的时期。
这本书是对Spring的一个探索。在这一章节,咱们将在高层次测试Spring,让你品尝一下Spring究竟是什么。这一章将让你明白Spring到底解决了各类什么样的问题,而且未这本书的其他部分作好准备。
Spring是由Rod Johnson创造的一个开源框架。Spring被创造出来以解决企业级应用开发的复杂问题,而且让普通的JavaBeans(plain-vanilla JavaBeans)可以完成此前只有EJB才可能完成的任务。可是Spring的用处并不只仅局限于服务器端开发。并且Java应用能够从Spring中获益,例如间接性、可测试性以及松耦合。
另外一种意义的bean...虽然在表示应用组件时,Spring大方地使用了bean和JavaBean这两个词,但这并不表示一个Spring组件必须严格地听从JavaBean的规格。一个Spring组件能够是任何形式的POJO。在本书中,我假设JavaBean是一个宽松的定义,与POJO同义。
经过本书你将了解到,Spring作了许多事情。可是Spring提供的几乎每个功能的根基,是一些很是基础的想法,所有专一于Spring的基本任务:Spring简化Java开发。
这里是粗体!大量的框架说要简化某一些事物。可是Spring旨在简化Java开发这一普遍主题。这还须要更多的解释。Spring是如何简化Java开发的呢?
为了支持对Java复杂度的攻击,Spring利用了4个关键策略:
利用POJO的轻量级与最小侵入式开发(Lightweight and minimally invasive development with POJOs)
经过DI和面向接口实现松耦合(Loose coupling through DI and interface orientation)
经过方面和普通惯例实现声明式编程(Declarative programming through aspects and common conventions)
利用方面和模板消除陈词滥调的代码(Eliminating boilerplate code with aspects and templates)
基本上Spring作的每一件事均可以追溯到这四条策略中的一条或几条上。在这一章节的其余部分,我将扩展到这些想法的每个中去,展现具体的例子以解释Spring是如何完成其简化Java开发的目标的。让咱们从Spring如何经过鼓励面向POJO的开发来保持最小化侵入式的(minimally invasive,这点很差翻译,大体意思是不怎么须要修改POJO的代码)。
若是你作过很长时间的Java开发工做,你可能会发现某些框架会紧紧地困住你,它们强制你去继承某一个类或实现某一个接口。侵略式编程模型一个简单目标例子(easy-target example)是EJB 2-era stateless session beans(不详,在此不作翻译)。可是即便早期的EJBs是这样一个简单目标,侵入式编程编程仍然能在早期版本的Struts, WebWork, Tapestry,以及无数其余的Java规格与框架中找到。
Spring(尽量地)避免其API污染你的应用代码。Spring几乎历来不强迫你去实现一个Spring接口,或集成一个Spring类。反而,一个基于Spring的应用中的类一般并无指示它们为Spring所用。最坏状况,一个类可能被Spring的一个注解标注,可是它另外一方面是一个POJO。
为了说明,考虑HelloWorldBean类。
如你所见,这很简单,就是一个普通的Java类,一个POJO。它并无什么特殊的东西以代表它是一个Spring组件。Spring的非侵入式编程模型表示,这个类在Spring中一样能够很好的工做,就像它能在其余非Spring应用中同样。
尽管样式很是简单,POJOs依然能够很强大。Spring使得POJOs更强大的一个方法是利用DI装配(assembling)它们。让咱们看看DI是如何帮助解除应用对象之间的耦合度的。
依赖注入这个词听起来可能有点高大上,让人联想出一些复杂的编程技术或设计模式的概念。可是结果证实,DI并无听起来那么复杂。经过在你的项目中利用DI,你将发现你的代码将获得极大地简化,更容易理解,更容易测试。
任何非平凡的应用(比Hello World要复杂的多)都是有相互协做的多个类组成的,以执行一些业务逻辑。传统地,每个对象负责获取对它本身的依赖(与他协做的对象)的引用。这将会带来高度耦合和难以测试的代码。
例如,考虑下面的Knight类。
package com.gaussic.knights; // 一个专门拯救少女的骑士 public class DamselRescuingKnight implements Knight { private RescueDamselQuest quest; // 拯救少女任务 // 这里出现了与RescueDamselQuest的紧密耦合 public DamselRescuingKnight() { this.quest = new RescueDamselQuest(); } // 骑士出征 public void embarkOnQuest() { quest.embark(); } }
相信你们都知道骑士与龙的故事,一个勇敢的骑士,除了可以战胜巨龙以外,还须要拯救被囚禁的少女。可是在上面的DamselRescuingKnight中,咱们发现他在构造器中建立了他本身的任务,一个RescueDamselQuest(也就是说,这个骑士构造出来时内心就只有一个任务,就是拯救少女,注意这个任务是发自心里的)。这致使了DamselRescuingKnight与RescueDamselQuest的高度耦合,这严重限制了骑士的行动。若是有一位少女须要拯救,那么这个骑士将会出现。可是若是须要屠龙呢,或者须要开个圆桌会议呢,那这个骑士将被一脚踹开。
此外,给DamselRescuingKnight写一个单元测试是很是困难的。在这个测试中,你须要可以保证,在骑士的embarkOnQuest()方法被调用时,quest的embark()方法也被调用。可是在这里并无明确的方法来完成它。不幸的是,DamselRescuingKnight将保持未测试状态。
耦合是一个双头猛兽。一方面,紧密耦合的代码难以测试,难以重用,且难以理解,而且常常表现出“打地鼠”的bug行为(修复了一个bug,又产生了一个或多个新的bug)。另外一方面,必定数量的耦合仍是有必要的,彻底不耦合的代码作不了任何事情。为了作一些有用的事情,类之间应该互相具备必定的认识。耦合是必需的,可是须要仔细管理。
利用DI,对象在构建时被给予它们的依赖,这经过一些定位系统中的每个对象的三方来实现。对象不须要建立或得到它们的依赖。如图所示,依赖被注入到须要它们的对象中。
为了说明这一点,让我么看看BraveKnight类:一个骑士不只仅要勇敢,还应该能够完成任何到来的任务。
package com.gaussic.knights; // 一个勇敢的骑士,应该能完成任何到来的任务 public class BraveKnight implements Knight { private Quest quest; // 任务,这是一个接口 // 构造器,注入Quest public BraveKnight(Quest quest) { this.quest = quest; } // 骑士出征 public void embarkOnQuest() { quest.embark(); } private BraveKnight() {} }
如你所见,BraveKnight不像DamselRescuingKnight同样建立本身的任务,而是在构造的时候被传入了一个任务做为构造参数(也就是说,这个骑士构造出来时,有人给了他一个任务,他的目标是完成这个任务,想雇佣兵同样)。这种类型的注入被称为构造注入(constructor injection)。
此外,勇敢的骑士所赋予的任务是一个Quest类型,它是一个接口,全部的具体quest都要实现这个接口。于是BraveKnight能够挑战RescueDamselQuest,SlayDragonQuest,MakeRoundTableRounderQuest,或者任何其余的Quest。
这里的关键点是,BraveKnight并不与任何一个特定的Quest的实现耦合。对于给予了什么任务,他并不介意,只要它实现了Quest接口。这就是DI的一个关键益处-松耦合。若是一个对象仅仅经过依赖的接口(而不是它们的实现或者实例化)来了解这些依赖,那么这个依赖就能够自由地更换为不一样的实现,而一方(在此为BraveKnight)不须要知道有任何的不一样。
换出依赖的一个最广泛的方法是在测试时利用一个mock实现(one of the most common ways a dependency is swapped out is with a mock implementation during test)。因为紧耦合,你没法适合地测试DamselRescuingKnight,可是你能够轻松地测试BraveKnight,经过给它一个Quest的mock实现(其实就是构建了一个虚拟的Quest),以下所示:
(注意:运行如下测试须要引入junit包和mockito-core包)
package com.gaussic.knights.test; import com.gaussic.knights.BraveKnight; import com.gaussic.knights.Quest; import org.junit.Test; import org.mockito.Mockito; // 一个勇敢的骑士,须要经历mock测试的考验 public class BraveKnightTest { @Test public void knightShouldEmbarkOnQuest() { Quest mockQuest = Mockito.mock(Quest.class); // 构建 mock Quest,其实就是虚拟的Quest BraveKnight knight = new BraveKnight(mockQuest); // 注入mockQuest knight.embarkOnQuest(); Mockito.verify(mockQuest, Mockito.times(1)).embark(); } }
在此使用了一个mock对象框架(Mockito)来建立一个Quest接口的虚拟实现。利用虚拟对象,咱们能够建立一个新的BraveKnight实例,并经过构造器注入这一虚拟Quest。在调用embarkOnQuest()方法后,利用Mockito来验证虚拟Quest的embark()方法仅仅被调用一次。
如今,BraveKnight被写成了这样的形式,你能够将任何一个任务交给骑士,那么你应该如何指定给他什么Quest呢?假设,例如,你想要BraveKnight去完成一项屠龙的任务。多是SlayDragonQuest,以下所示。
package com.gaussic.knights; import java.io.PrintStream; // 任务目标:屠杀巨龙 public class SlayDragonQuest implements Quest { private PrintStream stream; // 容许自定义打印流 public SlayDragonQuest(PrintStream stream) { this.stream = stream; } public SlayDragonQuest() {} // 任务说明 public void embark() { stream.println("Embark on quest to slay the dragon!"); } }
如你所见,SlayDragonQuest实现了Quest接口,让其能很好的适应BraveKnight。你也可能注意到,SlayDragonQuest在构造时请求了一个普通的PrintStream,而不像其余的简单Java示例同样依靠System.out.println()。这里有一个大问题,怎样才能把SlayDragonQuest交给BraveKnight呢?还有怎样才能把PrintStream交给SlayDragonQuest呢?
在应用组件之间创建联系的行为,一般被称之为装配(wiring)。在Spring中,有不少将组件装配在一块儿的方法,可是一般的方法老是经过XML的。下面展现了一个简单的Spring配置文件,knight.xml,它将BraveKnight, SlayDragonQuest和PrintStream装配在了一块儿。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- BraveKnight bean,id为knight --> <bean id="knight" class="com.gaussic.knights.BraveKnight"> <!-- 构造参数,引用 id为quest的bean --> <constructor-arg ref="quest"/> </bean> <!-- SlayDragonQuest bean,id为quest --> <bean id="quest" class="com.gaussic.knights.SlayDragonQuest"> <!-- 构造参数,值为 #{T(System).out} --> <constructor-arg value="#{T(System).out}"/> </bean> </beans>
在此,BraveKnight和SlayDragonQuest在Spring中都被定义为bean。在BraveKnight bean中,它在构造是传入了一个SlayDragonQuest的引用,做为构造参数。同时,SlayDragonQuest bean中使用了Spring表达式语言来传递 System.out(这是一个PrintStream)给SlayDragonQuest的构造器。
若是说XML配置不符合你的口味,Spring还提供了基于Java的配置方法。以下所示。
package com.gaussic.knights; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; // @Configuration注解,声明这是一个配置类 @Configuration public class KnightConfig { // 与XML中的bean目的相同,声明一个SlayDragonQuest bean,传入System.out @Bean public Quest quest() { return new SlayDragonQuest(System.out); } // 与XML中的bean目的相同,声明一个BraveKnight bean,传入quest @Bean public Knight knight() { return new BraveKnight(quest()); } }
不管是使用基于XML仍是基于Java的配置,DI的益处都是相同的。虽然BraveKnight依赖于Quest,但它并不知道将被给予什么样的Quest,或者这个Quest是从哪里来的。一样的,SlayDragonQuest依赖于PrintStream,可是它并不须要了解那个PrintStream是什么。只有Spring,经过它的配置,知道全部的组件是如何组织到一块儿的。这样,咱们就能够在不修改当前类的状况下,去修改它的依赖。
这个例子展现了Spring中装配bean的一个简单方法。先不要在乎太多的细节,咱们将会在第二章深刻探讨Spring配置。咱们也将探讨Spring中装配bean的一些其余办法,包括一种让Spring自动发现bean和建立它们之间关系的方法。
如今你已经生命了BraveKnight和Quest之间的关系,你须要载入XML配置文件而且打开应用。
在Spring应用中,一个应用上下文(application context)载入bean的定义并将它们装配在一块儿。Spring应用上下文彻底负责建立并装配全部构成应用的对象。Spring提供了多种应用上下文的实现,每个主要的不一样在于它们如何载入配置。
当bean被声明在xml文件中时,一个合适的方法是ClassPathXmlApplicationContext。这个Spring上下文实现从一个或多个(在应用的classpath目录的)XML文件载入Spring上下文。下面的main()方法使用了ClassPathXmlApplicationContext来载入knight.xml,而且得到对Knight对象的一个引用。
package com.gaussic.knights; import org.springframework.context.support.ClassPathXmlApplicationContext; public class KnightMain { public static void main(String[] args) throws Exception { // 从应用的classpath目录载入Spring配置文件 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/knight.xml"); // 获取bean,并执行 Knight knight = context.getBean(Knight.class); knight.embarkOnQuest(); context.close(); } }
此处,main()方法基于knight.xml文件建立了Spring上下文。而后它利用这个上下文做为一个工厂来检索ID为knight的bean。利用对Knight对象的一个引用,它调用了embarkOnQuest()方法来使knight执行给予他的任务。注意这个类并不知道骑士被赋予了什么样的任务。这样,它也不知道这个任务是交给BraveKnight作的。只有knight.xml知道具体的实现是什么。
运行KnightMain的main()方法,将获得下列结果:
利用这个例子,你应该对依赖注入有了必定的了解。在这本书中你将看到更多的DI。如今让咱们来看看Spring的另外一个Java简化策略:基于方面(aspects)的声明式编程。
虽然DI能够松散的绑定软件的各个组件,面向方面编程(AOP)还可让你更够捕捉那些在整个应用中可重用的组件。
AOP常常被定义为一项提高软件系统分离性(separation of concerns)的技术。系统每每由多个组件构成,每个组件负责一个特定的功能。可是一般这些组件还须要承担其核心功能之外的附加功能。一些系统服务,例如日志、事务管理和安全,每每会进入一些其余的组件中,而这些组件每每负责其余的核心功能。这些系统服务一般被称为交叉相关(cross-cutting concerns),由于它们在系统中每每交叉在多个组件内。
因为将这些相关性传播到了多个组件中,你向你的代码引入了两种层次的复杂度:
实现系统级相关性的代码在多个组件中存在重复。这代表若是你须要改变这些相关性的工做方式,你须要访问多个组件。即便你将这个相关性抽象为一个独立的模块,这样其余的组件能够经过模块调用来使用它,这个方法调用依然在多个组件里面重复。
你的组件被与核心功能无关的代码污染了。一个向地址簿添加一个地址的方法,应该仅仅关心如何去添加这个地址,而不是还得去关心它的安全性和事务性。
下图展现了这一复杂度。左边的业务对象太过亲密地与右边的系统服务相联系。每一个对象不只仅知道本身被日志记录、安全检查,以及涉及到事务中,并且还须要负责本身去执行这些服务。
AOP能够模块化这些服务,而后声明式地将它们运用到须要它们的组件中。这样可使得其余组件更加紧密地专一于负责它们本身的功能,彻底无视任何系统服务的存在。简单的说,方面(aspects)使得POJOs保持普通(plain)。
把方面想象成一个覆盖多个组件和应用的毯子能够帮助理解,以下图所示。在其核心部分,一个包含多个模块的应用实现了业务功能。利用AOP,你能够利用一层层的其余功能将你的核心应用覆盖。这些层能够声明式地运用到你的应用中,以一种更为灵活的方式,而不须要让你的核心应用知道他们的存在。这是一个很强大的概念,由于它保证安全、事务管理和日志不去污染你的应用的核心业务逻辑。
为了说明方面在Spring中是如何运用的,让咱们再来看看骑士的例子,向其中添加一个基本的Spring方面。
勇敢的骑士在屠杀巨龙和拯救少女归来以后,咱们这些百姓要如何才能知道他的丰功伟绩呢?吟游诗人(minstrel)会将勇士的故事编成歌谣在民间传颂(有网络红人就必然要有网络推手啊)。咱们假设你须要经过吟游诗人的服务来记录骑士出征和归来的整个过程。下面展现了这个Minstrel类:
package com.gaussic.knights; import java.io.PrintStream; // 吟游诗人 public class Minstrel { private PrintStream stream; public Minstrel(PrintStream stream) { this.stream = stream; } // 骑士出发前,颂唱 public void singBeforeQuest() { stream.println("Fa la la, the knight is so brave!"); } // 骑士归来,颂场 public void singAfterQuest() { stream.println("Tee hee hee, the brave knight did embark on a quest!"); } }
如你所见,Minstrel是一个有两个方法的简单类。singBeforeQuest()旨在骑士出发前调用,singAfrterQuest()旨在骑士完成任务归来调用。在这两种状况下,Minstrel都经过注入其构造器的PrintStrem来颂唱。
把这个Minstrel运用到你的代码中其实很是简单----你能够把它直接注入到BraveKnight中,对吗?让咱们对BraveKnight作一点适当的调整,以使用Minstrel。下面的代码展现了将BraveKnight和Minstrel运用到一块儿的第一次尝试:
package com.gaussic.knights; public class BraveKnight2 { private Quest quest; private Minstrel minstrel; public BraveKnight2(Quest quest, Minstrel minstrel) { this.quest = quest; this.minstrel = minstrel; } // 一个骑士应该管理本身的吟游诗人吗??? public void embarkOnQuest() throws Exception { minstrel.singBeforeQuest(); quest.embark(); minstrel.singAfterQuest(); } }
上面的代码能够完成任务。如今你只须要回到Spring配置文件中去定义Minstrel bean,并将其注入到BraveKnight bean的构造器中。可是等等。。。。
好像有什么不对劲。。。一个骑士难道应该去管理他本身的吟游诗人吗?一个伟大的骑士是不会本身去传扬自身的功绩的,有节操的吟游诗人应该主动的去传颂伟大骑士的功绩,而不接受他人的收买。那么,为何这里的骑士他时时地去提醒吟游诗人呢?
此外,因为这个骑士须要知道这个吟游诗人,你被强制将Minstrel注入到BraveKnight中。这不只仅复杂化了BraveKnight的代码,还让我想到,若是你须要一个没有吟游诗人的骑士要怎么办?若是Minstrel是null那怎么办(骑士所处的地区,压根没有吟游诗人的存在)?那你是否是还要引入一些非空检查逻辑到你的代码里面呢?
你的简单的BraveKnight类开始变得愈来愈复杂(而骑士只想作一个纯粹的骑士)。可是,使用AOP,你能够声明,吟游诗人应该主动地去颂唱骑士的功绩,而骑士只须要作好本职工做。
为了把Minstrel转变为一个方面,你要作的仅仅是在Spring配置文件中声明它。这是一个更新版的knight.xml,加入了Minstrel方面的声明:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- BraveKnight bean,id为knight --> <bean id="knight" class="com.gaussic.knights.BraveKnight"> <!-- 构造参数,引用 id为quest的bean --> <constructor-arg ref="quest"/> </bean> <!-- SlayDragonQuest bean,id为quest --> <bean id="quest" class="com.gaussic.knights.SlayDragonQuest"> <!-- 构造参数,值为 #{T(System).out} --> <constructor-arg value="#{T(System).out}"/> </bean> <!-- Minstrel bean, id为minstrel --> <bean id="minstrel" class="com.gaussic.knights.Minstrel"> <!-- 构造参数,值为 #{T(System).out} --> <constructor-arg value="#{T(System).out}" /> </bean> <!-- 方面配置 --> <aop:config> <aop:aspect ref="minstrel"> <!-- 在执行任何类的 embarkOnQuest()方法时调用 --> <aop:pointcut id="embark" expression="execution(* *.embarkOnQuest(..))"/> <!-- 在embark以前,调用 singBeforeQuest --> <aop:before pointcut-ref="embark" method="singBeforeQuest"/> <!-- 在embark以后,调用 singAfterQuest --> <aop:after pointcut-ref="embark" method="singAfterQuest"/> </aop:aspect> </aop:config> </beans>
这里你使用了Spring的 aop configuration 命名空间来声明Minstrel bean是一个方面。首先你声明Minstrel是一个bean。而后在<aop:aspect>元素引入那个bean。更进一步的定义这个aspect,你声明(使用 <aop:before>)了singBeforeQuest()方法在embarkOnQuest()方法前调用,称之为before advice。以及singAfterQuest()方法在embarkOnQuest()后调用,称之为 after advice。
在以上两种状况中,pointcut-ref属性都引用了一个名为embark的pointcut(切入点)。这个pointcut定义在了以前的<pointcut>元素中,使用了表达式属性级来选择这个advice应该做用在那个位置。表达式语法是AspectJ的pointcut表达式语言。
若是你不了解AspectJ,或者AspectJ pointcut表达式是如何编写的,不用担忧。咱们将在第4章对Spring AOP作更多的讲解。如今,知道你要请求Spring在BraveKnight在出征先后,调用Minstrel的singBeforeQuest()和singAfterQuest()方法足矣。
如今,不改动KnightMain的任何地方,运行main()方法,结果以下:
这就是它的所有了 !只须要一点点的XML,你就将Minstrel转换为了一个Spring方面。若是它如今还没彻底说明白,不用担忧,你将在第4章中看到更多的Spring AOP示例,它们将给你更明确的概念。如今,这个例子有两个重点须要强调。
第一,Minstrel仍然是一个POJO,没有任何代码代表它将被做为一个方面。Minstrel只在你在Spring context中声明时,才是一个方面。
第二,也是最重要的,Minstrel能够运用到BraveKnight中,而BraveKnight不须要明确地调用它。实际上,BraveKnight甚至一直不知道还有Minstrel的存在。
我还应该指出,虽然你使用了一些Spring的魔法来将Minstrel转化为一个方面,你仍是须要将它先声明为一个bean。重点是,你能够用Spring aspects作任何的事情,像其余的Spring beans同样,例如将依赖注入给它们。
使用aspects来颂唱骑士的功绩是很好玩的。可是Spring的AOP能够用在更多实用的地方。在后面你将看到,Spring AOP能够用来提供服务,例如声明式事务、安全,将在第9章和14章介绍。
在这以前,咱们来看看Spring简化Java开发的另外一个方法。
你是否写过一些看似以前写过的代码?这不是“似曾类似“,朋友。那是重复代码(boilerplate code)---为了执行常见或简单的任务须要一遍一遍的写类似的代码。
不幸的是,Java API的许多地方都含有重复的代码。使用JDBC作数据库查询就是一个明显的例子。若是你在使用JDBC,你可能须要写一些以下所示的代码:
public Employee getEmployeeById(long id) { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { conn = dataSource.getConnection(); // 创建链接 // 选择 employee stmt = conn.prepareStatement("select id, firstname, lastname, salary from employee where id=?"); stmt.setLong(1, id); rs = stmt.executeQuery(); Employee employee = null; if (rs.next()) { employee = new Employee(); // 建立对象 employee.setId(rs.getLong("id")); employee.setFirstName(rs.getString("firstname")); employee.setLastName(rs.getString("lastname")); employee.setSalary(rs.getBigDecimal("salary")); } return employee; } catch (SQLException e) { // 错误处理 } finally { if (rs != null) { // 收拾残局 try { rs.close(); } catch (SQLException e) { } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { } } if (conn != null) { try { conn.close(); } catch (SQLException e) { } } } return null; }
如你所见,这个JDBC代码的目的是查询一个员工的名字和工资状况。因为这项特定的查询任务被埋藏在一堆的JDBC仪式中,你首先必须建立一个链接,而后建立一个陈述(statement),最后在查询结果。并且,为了安抚JDBC的脾气,你必须catch SQLException,其实就算它抛出了错误,你也没什么可以作的。
最后,在全部的事情都作完了,你还得收拾残局,关闭链接(connection)、陈述(statement)、结果集(result set)。这仍是有可能出发JDBC的脾气,于是你还得catch SQLException。
上面代码最显著的问题是,若是你要作更多的JDBC查询,你将会写大量重复的代码。可是只有不多一部分是真正跟查询相关的,而JDBC重复代码要多得多。
在重复代码业务中,JDBC并不孤独。许多的工做都包含类似的重复代码。JMS、JNDI和大量的REST服务一般涉及到大量彻底重复的代码。
Spring经过将重复代码封装在模板中来消除它们。Spring的JdbcTemplate能够在不须要全部传统JDBC仪式的状况下执行数据库操做。
例如,使用Spring的SimpleJdbcTemplate(JdbcTemplate的一个特定形式,利用了Java 5的优势),getEmployeeById()方法能够重写,使得它仅关注于检索员工数据的任务,而不须要考虑JDBC API的需求。下面的代码展现了更新后的getEmployeeById()的样子:
public Employee getEmployeeById(long id) { return jdbcTemplate.queryForObject( "select id, firstname, lastname, salary from employee where id=?", // SQL查询 new RowMapper<Employee>() { // 映射结果到对象 public Employee mapRow(ResultSet rs, int rowNum) throws SQLException { Employee employee = new Employee(); employee.setId(rs.getLong("id")); employee.setFirstName(rs.getString("firstname")); employee.setLastName(rs.getString("lastname")); employee.setSalary(rs.getBigDecimal("salary")); return employee; } }, id); }
如你所见,新版本的getEmployeeById()更加简洁且专一于从数据库中查询员工。模板的queryForObject()方法被给予一个SQL查询,一个RowMapper(为了将结果集数据映射到领域对象),以及0个或多个查询参数。你没法在getEmployeeById()中找到任何的JDBC重复代码。这一切都交给了模板来处理。
我已经想你展现了Spring是如何使用面向POJO的开发来下降Java开发复杂性的,DI, aspects 和 templates。同时,还展现了如何在XML配置文件中配置bean和aspect。可是如何载入这些文件呢?让咱们看看Spring容器(container),存放应用的bean的地方。
在Spring应用中,你的应用对象住在Spring容器中。如图1.4所示,容器建立对象,而后将它们装配在一块儿,配置它们,而后管理它们的生命周期,从襁褓到坟墓。
在下一章节,你将了解如何配置Spring,使得它知道它须要建立、配置和装配什么对象。首先,知道对象何时hang out(闲逛?很差翻译)是很重要的。了解容器将帮助你了解对象是如何被管理的。
容器是Spring框架的核心。Spring的容器使用DI来管理组成一个应用的组件。这包括创建协调组件之间的联系。自己,这些组件更简洁切更易理解,它们支持重用,且易于单元测试。
没有单独的Spring容器。Spring包括多种的容器实现方法,能够分为两种独立的类型。Bean工厂(bean factory,由org.springframework.beans.factory.BeanFactory接口定义)是最简单的容器,提供DI的基本支持。应用上下文(application context,由org.springframework.context.ApplicationContext接口定义)创建与bean工厂的概念之上,提供应用框架服务,例如从配置文件中读取文本消息的能力和向感兴趣的事件监听器发送应用时间的能力。
虽然使用bean factory和application均可以,bean factory对于大部分应用来讲仍是过低级了。所以,相比bean factory,application context更受青睐。咱们将专一于使用application context,而不花更多的时间在bean factory上。
Spring包含多种口味的应用上下文。如下是一部分你最有可能赶上的:
AnnotationConfigApplicationContext --- 从一个Java配置类中载入Spring应用上下文
AnnotationConfigWebApplicationContext --- 从一个Java配置类中载入Spring web应用上下文
ClassPathXmlApplicationContext --- 从一个或多个在classpath下的XML文件中载入Spring上下文,将上下文定义文件做为一个classpath资源文件
FileSystemXmlApplicationContext --- 从文件系统中的XML文件中载入上下文定义
XmlWebApplicationContext --- 从包含在一个web应用中的一个或多的XML文件中载入上下文定义
咱们将在第八章讲解基于web的Spring应用时更加详细的说明AnnotationConfigWebApplicationContext和XmlWebApplicationContext。如今,让咱们用FileSystemXmlApplicationContext从文件系统载入Spring上下文,或用ClassPathXmlApplicationContext从classpath载入Spring上下文。
从文件系统或classpath载入应用上下文与从bean factory中载入bean相相似。例如,下面是如何载入FileSystemXmlApplicationContext的:
ApplicationContext context = new FileSystemXmlApplicationContext("c:/knight.xml");
相似地,可使用ClassPathXmlApplicationContext从应用的classpath载入应用上下文:
ApplicationContext context = new ClassPathXmlApplicationContext("knight.xml");
FileSystemXmlApplicationContext和ClassPathXmlApplicationContext的不一样之处在于,前者在文件系统的特定的位置寻找knight.xml,然后者在应用的classpath中寻找knight.xml。
另外,若是你更偏向从Java配置中载入你的应用上下文的话,可使用AnnotationConfigApplicationContext:
ApplicationContext context = new AnnotationConfigApplicationContext(KnightConfig.class);
取代指定一个载入Spring应用上下文的XML文件外,AnnotationConfigApplicationContext被给以一个配置类来载入bean。
如今手头上有了一个应用上下文,你能够利用上下文的getBean()方法,从Spring容器中检索bean了。
如今你知道了如何建立Spring容器的基本方法,让咱们更进一步的看看在Bean容器中一个bean的生命周期。
在传统的Java应用中,bean的生命周期很是简单。Java的关键词被用来实例化bean,而后这个bean就可使用了。一旦这个bean再也不使用,将会被垃圾收集器处理。
相比之下,在Spring容器中的bean的生命周期更加复杂。理解Spring bean的生命周期是很是重要的,由于你可能须要利用Spring提供的一些有点来定制一个bean何时被建立。图1.5展现了一个典型的bean在载入到应用上下文时的生命周期。
如你所见,一个bean工厂在bean能够被使用前,执行了一系列的设置操做。让咱们来探讨一些细节:
Spring实例化bean。
Spring将值和bean引用注入到bean的属性中。
若是一个bean实现了BeanNameAware,Spring将这个bean的id传递给setBeanName()方法。
若是一个bean实习了BeanFactoryAware,Spring调用setBeanFactory()方法,将一个引用传入一个附入(enclosing)的应用上下文。
若是一个bean实现了BeanPostProcessor接口,Spring调用其postProcessBeforeInitialization()方法。
若是一个bean实现了InitializingBean接口,Spring调用其afterPropertiesSet()方法。相似的,若是这个bean使用了init-method进行声明,那么特定的初始化方法将被调用。
若是一个bean实现了BeanPostProcessor,Spring调用其postProcessAfterInitialization()方法。
在这点,bean就能够被应用使用了,而且一直保持在应用上下文中,直到应用上下文被销毁。
若是一个bean实现了DisposableBean接口,Spring调用其destroy()方法。一样,若是这个bean被声明了destroy-method,将会调用特定的方法。
如今你已经知道了如何建立并载入一个Spring容器。可是一个空的容器自己并无什么好处,它不含任何东西,除非你将什么放进去。为了实现Spring DI的好处,你必须将Spring容器中的应用对象装配起来。咱们将在第二章节更加详细的讲解bean的装配。
首先,咱们来调查一下现代Spring的蓝图(landscape),来看看Spring框架是由什么构成的,以及后面版本的Spring提供了什么新的特性。
如你所见,Spring框架专一于经过DI、AOP和减小重复来简化企业Java开发。即便那是Spring的所有,那也是值得一用的。可是对于Spring来讲,还有不少令你难以置信的东西。
在Spring框架中,你将发现许多Spring简化Java开发的方法。可是,在Spring框架之上,是一个更大的创建在核心框架之上的项目生态系统,将Spring扩展到更多的领域,例如web services, REST, mobile和NoSQL。
让咱们先来分解一下核心Spring框架,来看看它给咱们带来了什么。而后咱们再扩展咱们的视野来看看Spring包的更多成员。
当你下载了Spring distriution以后,查看它的libs目录,你将发现多个JAR文件。在Spring 4.0中,有20个独立的模块,每一个模块有3个JAR文件(binary class, source, JavaDoc)。完整的library JAR文件如图1.6所示。
这些模块能够被排成6个功能的类,如图1.7所示。
总的来讲,这些模块给了你任何你须要的东西,来开发企业级应用。可是你不须要让你的应用彻底基于Spring框架。你能够自由地选择适合你的应用的模块,而且在Spring没法知足你的需求时,选择其余的模块。Spring甚至提供了与许多其余框架和库的集成点,于是你不用去本身写它们。
让咱们一个一个的来看看Spring的每个模块,来了解一下每个模块是如何拼凑整个Spring的版图的。
Spring框架的中心是一个容器,它负责管理Spring应用中的bean是如何建立、配置与管理的。这个模块的内部是Spring bean工厂,是Spring提供DI的一部分。创建在bean工厂之上,你将发现Spring应用上下文的多个实现,每个都提供了配置Spring的不一样方式。
除了bean工厂和应用上下文以外,这个模块还提供了许多企业级服务,例如邮件、JNDI访问、EJB集成,和调度。
Spring的全部模块都都是创建在核心容器之上的。你将在配置你的应用是隐式的使用这些类。咱们将通篇讨论核心模块,从第二章开始,咱们将更深刻的挖掘Spring DI。
Spring在AOP模块中提供了面向方面编程的丰富支持。这个模块的做用服务于------为你本身的Spring应用开发你本身的aspects。与DI相似,AOP支持应用对象的松散耦合。可是利用AOP,应用级的相关性(例如事务和安全)解除了它们与其余对象的耦合。
咱们将在第4章更加深刻的讲解Spring AOP支持。
使用JDBC时老是会形成大量的重复代码(获取链接,建立statement,处理结果集合,而后关闭链接)。Spring的JDBC和数据访问对象(DAO)模块将这些重复代码抽象化,于是你能够保持你的数据库代码干净和简单,而且阻止了由数据库资源访问失败致使的错误。这个模块也在多种数据库服务器返回的错误消息之上构建了一层很是有意义的exception。你并不须要去破解神秘而专用的SQL错误消息。
对于那些更喜欢使用对象关系映射(object-relational mapping, ORM)工具的人来讲,Spring提供了ORM模块。Spring的ORM支持创建在DAO支持之上,以多种ORM方法提供了创建DAO的方便方法。Spring并无试图去实现它本身的ORM方法,可是它提供了链接多个流行ORM框架的钩子(hook),包括Hibernate,Java Persistemce APUI,Java Data Objects,以及iBATIS SQL Maps。Spring的事务管理像支持JDBC同样支持这些ORM框架。
在第10章节,咱们将讲解Spring数据访问,你将看到Spring的基于模板的JDBC抽象是如何可以大大地简化JDBC代码的。
这个模块还包括了一个对于Java Message Service(JMS)的Spring抽象,以经过消息机制进行与其余应用的异步集成。此外,对于Spring 3.0,这个模块包含了object-to-XML映射特性,它们是Spring Web Services项目的根本部分。
此外,这个模块使用了Spring的AOP模块来提供Spring应用中对象的事务管理服务。
模型-视图-控制器(MVC)样式已经很是普遍地被接受,以用来构建web应用,使得用户接口与应用逻辑向分离。Java在MVC框架中并没有缺陷,Apache Struts, JSF, WebWork和Tapestry已经成为了很是流行的MVC选择。
即便Spring能够集成多种流行MVC框架,它的web和远程模块来自于一个可靠的MVC模块,它在应用web层充分运用了Spring的松散耦合技术。咱们将在第5-7章讲解Spring mvc框架。
除了面向用户的web应用之外,为了创建能够与其余应用交互的应用,这个模块还提供了多个远程选项。Spring的远程能力包括远程方法调用(Remote Method Invocation, RMI), Hessian, Burlap, JAX-WS,以及Spring本身的HTTP invoker。Spring还提供了使用REST API的一级支持。
在地第15章,我将讲解Spring远程。而且你将在第16章学习到如何建立与使用REST API。
Spring的仪器(instrumentation,是这么翻吗)模块包括了向JVM添加助理(agent)的支持。特别地,它向Tomcat提供了一个迂回助理以转换类文件,就像它们是被类加载器加载同样。
若是这听起来难以理解,不要太担忧。这个模块提供的仪器用途很是窄,咱们将不会在这本书中讲解。
意识到了开发者写的测试的重要性,Spring提供了一个模块以测试Spring应用。
在这个模块中,你将发现许多的mock对象实现,为了撰写做用于JNDI,servlet,和portlet的单元测试代码。对于集成级的测试,这个模块提供了载入Spring上下文的bean和操做bean的支持。
在本书中,大多数例子将用测试来表示,利用Spring提供的测试方法。
Spring总比你能看到的要多得多。事实上,Spring的模块比下载下来的要多得多。若是你仅仅停留在核心的Spring框架,你将错过Spring组合带来的一笔巨大从潜力。整个Spring组合包包括了多个创建在核心Spring框架上的框架和库。 整体上,整个Spring组合包带来了基本覆盖Java开发每一面的Spring编程模型。
要覆盖Spring组合包提供的所有内容,须要大量的篇幅,并且不少都超出了本书的范围。可是咱们将了解一下Spring组合包的一些元素;这里是基于核心Spring框架的一个品尝。
Spring Web Flow创建在核心Spring框架之上,提供了将Spring bean发布为web服务的声明式方法,这些服务基于一个可论证的架构上下级的协议最后(contract-last)模型。服务的协议是由Bean的接口决定的。Spring Web Services提供了协优先(contract-first) web服务模型,其中服务实现是为知足服务协议而编写的。
安全是许多应用的关键方面。使用Spring AOP实现,Spring安全为Spring应用提供了声明式的安全机制。你将在第9章了解如何向web层添加Spring安全的。咱们将在第14章从新回到Spring安全来检验如何保证方法调用安全性的。
许多的企业级应用必须与其余的企业级应用进行交互。Spring集成提供了多种经常使用交互模式的实现,以Spring声明式的形式。本书将不作讲解。
当须要在数据上执行大量的操做时,没有什么能比得上批处理。若是你将去开发一个批处理应用,你能够利用Spring Batch, 来使用Spring的鲁棒、面向POJO的开发方法完成它。Spring Batch超出了本书的范围。
Spring Data使得操做各类数据库变得很是简单。虽然关系型数据库多年来一直用于企业应用中,现代的应用愈来愈意识到不是全部的数据均可以用表中的行和列来表示的。一种新产生的数据库,一般被称之为NoSQL数据库,提供了新的方法来操做新型数据库,而非传统关系型数据库。
无论你是在使用文档数据库如MongoDB,图形数据库如Neo4j,或者甚至传统关系型数据库,Spring Data为persistence提供了简化的编程模型。这包括,为多种数据类型,一个自动仓库(repository)机制来为你建立仓库实现。
咱们将在第11章中讲解如何用Spring Data来简化Java Persistence API(JPA)开发,而后在第12章时扩展性地讨论一些NoSQL数据库。
社交网络在互联网上升趋势,愈来愈多的应用开始集成社交网站的接口,例如Facebook和Twitter。若是你对这方面感兴趣,你能够了解一下Spring Social,Spring的一个社交网络扩展。
可是Spring Social并不只仅是微博和朋友。尽管这样命名,Spring Social相比社交(social)这一词更加偏向于链接(connect)。它帮助你利用REST API链接你的Spring应用,其中还包括不少没有任何社交目的的应用。
因为空间的限制,咱们将不在本书中讲解Spring Social。
移动应用是软件开发另外一个热门点。智能手机以及平板已经逐渐成为备受用户青睐的客户端。Spring Mobile是Spring MVC的一个新的扩展,以支持移动web应用的开发。
与Spring Mobile相关的是Spring Android项目。这个项目旨在利用Spring框架为一些安卓设备上的本地应用的开发带来必定的便利。初始地,这个项目提供了一个版本的Spring RestTemplate,它能够运用在Android应用中。它还能够操做Spring Social来使得本地安卓app能够与REST API相链接。本书将不讨论Spring for Andoid。
Spring大大简化了许多编程任务,减小甚至消除了大量你可能一般很是须要的重复代码。Spring Boot是一个很是激动人心的新项目,它的目的是在Spring开发时简化Spring自己。
Spring Boot大量地利用了自动配置技术,以消除大量的(在多种状况下,能够说是全部的)Spring配置。它还提供了许多的启动器(starter)项目来帮助来减小你的Spring项目构建文件,在你使用Maven或Gradle时。咱们将在第21章讲解Spring Boot。
当这本书的第三版出版时,最晚的Spring版本是3.0.5。这大概是3年前,而且Spring自那起发生了许多的改变。Spring框架迎来了3个重大的发布---3.1,3.2,以及如今的4.0---每一次发布都带来了许多新的特性与改善,以减轻应用开发。而且多个模块成员经历了巨大的改变。
这一版本的Spring in Action已经进行了更新,覆盖了大多数的这些版本的特性。可是如今,咱们简单地来看看Spring更新了什么。
Spring 3.1有许多有用的新特性与改进,大多都专一于简化和改进配置方法。此外,Spring 3.1提供了声明式的缓存支持,以及对于Spring MVC的许多改进。这里是Spring 3.1的一些亮点:
为了解决从多种环境(例如开发,测试,与发布)选择独立的配置的问题,Spring 3.1引入了环境资料(profile)。资料使得一些事情成为可能,例如,依赖于应用的环境选择不一样的数据源bean。
创建在Spring 3.0的基于Java的配置的基础上,Spring 3.1添加了多个enable注解以容许Spring用一个注解来进行特定特性的切换。
声明式缓存支持被引入了Spring,使得咱们能够利用简单的注解来声明缓存边界与规则,这与咱们如何声明事务边界相相似。
一个新的c命名空间带来了构造器注入,与Spring 2.0的p命名空间相似(用于属性注入),简化了配置方法。
Spring开始支持Servlet 3.0,包括利用基于Java的配置方法声明servlet和filter,来取代原来的web.xml。
Spring JPA的改进使得咱们能够彻底的配置JPA,而不须要persistence.xml。
Spring 3.1还包括了多个对Spring MVC的加强:
path variables到model attributes的自动绑定
@RequestMappingproduces和consumes属性,为了匹配请求的Accept和Content-Type头
@RequestPart注解,容许绑定部分的multipart请求到handler 方法参数
flush属性支持(重定向的属性),和一个RedirectAttributes类型来在请求见传递flash属性
与Spring 3.1更新了什么同等重要的是,Spring 3.1中哪些已经再也不使用了。特别地,Spring的JpaTemplate和JpaDapSupport类以不被建议使用,取而代之的是EntityManager。即便它们已经不建议使用,它们依然在Spring 3.2中。可是你不该该使用它们,由于它们没有升级到支持JPA 2.0,且在Spring 4中已经被移除。
如今咱们来看看Spring 3.2更新了什么。
Spring 3.1专一于利用一些其余的改进来改善配置方法,包括Spring MVC的改进。Spring 3.2主要是一个专一于Spring MVC的版本。Spring 3.2有以下的改进:
Spring 3.2 controllers能够充分利用Servlet 3的异步请求,将请求的处理传给多个分离的线程,释放servlet线程以处理更多的请求。
虽然Spring MVC controllers自Spring 2.5起就能够简单地做为POJO进行测试,Spring 3.2包含了一个Spring MVC测试框架,以针对controllers编写更加丰富的测试,做为controller来预警它们的行为,而不须要servler container。
除了改进controller测试,Spring 3.2包含了对测试基于RestTemplate的客户端的支持,而不须要发送请求到真实的REST终端。
一个@ControllerAdvice注解容许普通的@ExceptionHandler,@InitBinder,@ModelAttributes方法能够出如今单一的类中,而且运用到全部的controllers。
在Spring 3.2以前,全内容协议(full content negotiation)仅仅能够经过ContentNegotiationViewResolver来支持。可是在Spring 3.2中,全内容协议在整个Spring MVC中都是可用的,甚至在一个依赖于消息转换的controller方法。
Spring MVC 3.2包含了一个新的@MatrixVariable注解,已绑定一个请求的矩阵变量来处理方法参数。
抽象基类AbstractDispatcherServletInitializer能够用来方便的配置DispatcherServlet,而不使用web.xml。一样,它的子类AbstractAnnotationConfigDispatcherServletInitializer能够在配置基于Java的Spring配置方法时使用。
添加了ResponseEntityExceptionHandler类做为DefaultHandlerExceptionResolver的备用而使用。ResponseEntityExceptionHandler方法返回ResponseEntity<Object>而不是ModelAndView。
RestTemplate和@RequestBody参数支持通用类型。
RestTemplate和@RequestMapping方法支持HTTP PATCH方法。
映射拦截器支持URL模式,以排除在拦截处理以外。
虽然Spring MVC是Spring 3.2的主要部分,它还加入了一些非MVC的改进。这里是一些最有趣的新特性:
@Autowired,@Value和@Bean注解能够做为meta-annotations使用,以建立定制化注入和bean声明注解。
@DateTimeFormat注解再也不依赖于JodaTime。若是出现了JodaTime,它将被使用。不然,使用SimpleDateFormat。
Spring的声明式缓存支持开始初始化支持JCache 0.5。
你能够定义全局formats以转换和处理日期和时间。
集成测试能够配置和载入WebApplicationContext。
集成测试能够测试request和session范围的beans。
你将看到大量的Sping 3.2特性,在本书的多个章节中,特别是在web和REST章节。
Spring 4.0是目前最新的Spring版本。在其中有许多的新特性,包含以下:
Spring如今包含了对WebSocket编程的支持,包括JSR-356: Java API for WebSocket。
意识到WebSocket提供了一个低级的API,特别须要高级的抽象。Spring 4.0包含了一个高级的面向消息的编程模型,在WebSocket之上,基于SockJS,而且包含了STOMP子协议支持。
一个来自Spring集成项目的带有多种类型的消息模块。这个消息模块支持Sprig的SockJS/STOMP支持。它还包含发布消息的基于模板的支持。
Spring 4.0是最先支持Java 8特性(包含 lambdas)的框架之一。这使得利用回调接口(例如使用JdbcTemplate的RowMapper)更加的干净易读。
为基于Groovy开发的应用带来了更加平滑的编程体验,最重要的是让Spring应用能够彻底用Groovy来轻松开发。利用来自Grails的BeanBuilder,使Spring应用可以使用Groovy来配置。
为有条件的bean的建立提供了更加通用的支持,其中,bean只能在条件容许的状况下被建立。
Spring 4.0还包含了Spring的RestTemplate的异步实现,直接返回,可是在操做完成以后回调。
添加了许多JEE规范的支持,包括JMS 2.0,JTA 1.2,JPA 2.1和Bean Validation 1.1。
如你所见,许多新的成员都在最新的Spring框架中出现。经过本书,咱们将了解大多数的新特性,以及长期支持的特性。
如今你应该知道Spring到底带来了什么吧。Spring旨在使得Java开发更加简单化,且提倡松散耦合的代码。这其中最重要的是依赖注入和面向方面编程。
在本章,你稍微品尝了一下Spring的DI。DI是一种关联应用对象的方法,使得对象不须要知道它们的依赖来自哪里,或者它们是如何实现的。相比它们本身索取依赖,须要依赖的对象被它人给予了其所依赖的对象。由于依赖对象常常知道他们的注入对象(经过接口),使得耦合度很低。
除了DI,你稍微了解了一点Spring的AOP支持。AOP使你可以专一于一个地方----一个方面----逻辑能够从应用中分散开来。当Spring将你的bean装配在一块儿时,这些方面能够在一个运行时中运行,高效的给予bean以新的行为。
DI和AOP是Spring的一切的中心。于是你必须理解如何使用这些重要功能,以可以使用框架的其余部分。在本章,咱们仅仅看到了Spring的DI和AOP的表面。在接下来的几章,咱们将深刻探讨。
没有其余闲事,让咱们移动到第二章,来学习如何利用Spring DI将对象装配在一块儿。