《Spring实战》(第4版)
第一章:Spring之旅
1. 简化Java开发
为了降低Java开发的复杂性,Spring采取了以下4种关键策略:
- 基于POJO的轻量级和最小侵入性编程;
- 通过依赖注入和面向接口实现松耦合;
- 基于切面和惯例进行声明式编程;
- 通过切面和模板减少样板式代码。
1.1 激发POJO的潜能
在基于Spring构建的应用中,它的类通常没有任何痕迹表明你使用了Spring。最坏的场景是,一个类或许会使用Spring注解,但它依旧是POJO。
Spring赋予POJO魔力的方式之一就是通过DI来装配它们。
1.2 依赖注入(DI)
耦合是必须的,但应当被小心谨慎地管理。
创建应用组件之间协作的行为通常称为装配(wiring)。Spring有多种装配bean的方式,采用XML是很常见的一种装配方式。
KnightMain.java加载包含Knight的Spring上下文
这里的main() 方法基于knights.xml文件创建了Spring应用上下文。随后它调用该应用上下文获取一个ID为knight的bean。得到Knight 对象的引用后,只需简单调用embarkOnQuest() 方法就可以执行所赋予的探险任务了。注意这个类完全不知道我们的英雄骑士接受哪种探险任务,而且完全没有意识到这是由BraveKnight 来执行的。只有knights.xml文件知道哪个骑士执行哪种探险任务。
Spring还支持使用Java来描述配置。
Spring提供了基于Java的配置,可作为XML的替代方案
通过DI,对象的依赖关系将由系统中负责协调各对象的第三方组件在创建对象的时候进行设定。对象无需自行创建或管理它们的依赖关系,如图所示,依赖关系将被自动注入到需要它们的对象当中去。
依赖注入会将所依赖的关系自动交给目标对象,而不是让对象自己去获取依赖
1.3 应用切面
DI能够让相互协作的软件组件保持松散耦合,而面向切面编程(aspect-oriented programming,AOP)允许你把遍布应用各处的功能分离出来形成可重用的组件。
面向切面编程往往被定义为促使软件系统实现关注点的分离一项技术。系统由许多不同的组件组成,每一个组件各负责一块特定功能。除了实现自身核心的功能之外,这些组件还经常承担着额外的职责。诸如日志、事务管理和安全这样的系统服务经常融入到自身具有核心业务逻辑的组件中去,这些系统服务通常被称为横切关注点,因为它们会跨越系统的多个组件。
- 实现系统关注点功能的代码将会重复出现在多个组件中。这意味着如果你要改变这些关注点的逻辑,必须修改各个模块中的相关实现。即使你把这些关注点抽象为一个独立的模块,其他模块只是调用它的方法,但方法的调用还是会重复出现在各个模块中。
- 组件会因为那些与自身核心业务无关的代码而变得混乱。一个向地址簿增加地址条目的方法应该只关注如何添加地址,而不应该关注它是不是安全的或者是否需要支持事务。
在整个系统内,关注点(例如日志和安全)的调用经常散布到各个模块中,而这些关注点并不是模块的核心业务
AOP能够使这些服务模块化,并以声明的方式将它们应用到它们需要影响的组件中去。所造成的结果就是这些组件会具有更高的内聚性并且会更加关注自身的业务,完全不需要了解涉及系统服务所带来复杂性。总之,AOP能够确保POJO的简单性。
如下图所示,我们可以把切面想象为覆盖在很多组件之上的一个外壳。应用是由那些实现各自业务功能的模块组成的。借助AOP,可以使用各种功能层去包裹核心业务层。这些层以声明的方式灵活地应用到系统中,你的核心应用甚至根本不知道它们的存在。这是一个非常强大的理念,可以将安全、事务和日志关注点与核心业务逻辑相分离。
利用AOP,系统范围内的关注点覆盖在它们所影响组件之上
1.4 使用模板消除样板式代码
因为使用Java API而导致的样板式代码,样板式代码的一个常见范例是使用JDBC访问数据库查询数据。在许多编程场景中往往都会导致类似的样板式代码,JMS、JNDI和使用REST服务通常也涉及大量的重复代码。
Spring旨在通过模板封装来消除样板式代码。Spring的JdbcTemplate使得执行数据库操作时,避免传统的JDBC样板代码成为了可能。
2. 容纳你的Bean
在基于Spring的应用中,你的应用对象生存于Spring容器(container)中。Spring容器负责创建对象,装配它们,配置它们并管理它们的整个生命周期,从生存到死亡(在这里,可能就是new 到finalize())。
容器是Spring框架的核心。Spring容器使用DI管理构成应用的组件,它会创建相互协作的组件之间的关联。毫无疑问,这些对象更简单干净,更易于理解,更易于重用并且更易于进行单元测试。
Spring容器并不是只有一个。Spring自带了多个容器实现,可以归为两种不同的类型。bean工厂(由org.springframework.beans.factory.BeanFactory 接口定义)是最简单的容器,提供基本的DI支持。应用上下文(由org.springframework.context.ApplicationContext 接口定义)基于BeanFactory构建,并提供应用框架级别的服务,例如从属性文件解析文本信息以及发布应用事件给感兴趣的事件监听者。
2.1 使用应用上下文
Spring自带了多种类型的应用上下文。下面罗列的几个是你最有可能遇到的。
- AnnotationConfigApplicationContext: 从一个或多个基于Java的配置类中加载Spring应用上下文。
- AnnotationConfigWebApplicationContext: 从一个或多个基于Java的配置类中加载Spring Web应用上下文。
- ClassPathXmlApplicationContext: 从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。
- FileSystemXmlapplicationcontext: 从文件系统下的一个或多个XML配置文件中加载上下文定义。
- XmlWebApplicationContext: 从Web应用下的一个或多个XML配置文件中加载上下文定义。
无论是从文件系统中装载应用上下文还是从类路径下装载应用上下文,将bean加载到bean工厂的过程都是相似的。例如,如下代码展示了如何加载一个FileSystemXmlApplicationContext:
类似地,你可以使用ClassPathXmlApplicationContext 从应用的类路径下加载应用上下文:
使用FileSystemXmlApplicationContext 和使用ClassPathXmlApp-licationContext 的区别在于:FileSystemXmlApplicationContext 在指定的文件系统路径下查找knight.xml文件;而ClassPathXmlApplicationContext 是在所有的类路径(包含JAR文件)下查找 knight.xml文件。
应用上下文准备就绪之后,我们就可以调用上下文的getBean() 方法从Spring容器中获取bean。
2.2 bean的生命周期
在传统的Java应用中,bean的生命周期很简单。使用Java关键字new进行bean实例化,然后该bean就可以使用了。一旦该bean不再被使用,则由Java自动进行垃圾回收。
相比之下,Spring容器中的bean的生命周期就显得相对复杂多了。正确理解Spring bean的生命周期非常重要,因为你或许要利用Spring提供的扩展点来自定义bean的创建过程。下图展示了bean装载到Spring应用上下文中的一个典型的生命周期过程。
正如你所见,在bean准备就绪之前,bean工厂执行了若干启动步骤。我们对上图进行详细描述:
- 1.Spring对bean进行实例化;
- 2.Spring将值和bean的引用注入到bean对应的属性中;
- 3.如果bean实现了BeanNameAware 接口,Spring将bean的ID传递给setBean-Name() 方法;
- 4.如果bean实现了BeanFactoryAware 接口,Spring将调用setBeanFactory() 方法,将BeanFactory容器实例传入;
- 5.如果bean实现了ApplicationContextAware 接口,Spring将调用setApplicationContext() 方法,将bean所在的应用上下文的引用传入进来;
- 6.如果bean实现了BeanPostProcessor 接口,Spring将调用它们的post-ProcessBeforeInitialization() 方法;
- 7.如果bean实现了InitializingBean 接口,Spring将调用它们的after-PropertiesSet() 方法。类似地,如果bean使用init-method 声明了初始化方法,该方法也会被调用;
- 8.如果bean实现了BeanPostProcessor 接口,Spring将调用它们的post-ProcessAfterInitialization() 方法;
- 9.此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
- 10.如果bean实现了DisposableBean 接口,Spring将调用它的destroy() 接口方法。同样,如果bean使用destroy-method 声明了销毁方法,该方法也会被调用。
3. 俯瞰Spring风景线
3.1 Spring模块
在Spring 4.0中,Spring框架的发布版本包括了20个不同的模块,每个模块会有3个JAR文件(二进制类库、源码的JAR文件以及JavaDoc的JAR文件)。
这些模块依据其所属的功能可以划分为6类不同的功能:
3.2 Spring Portfolio
Spring远不是Spring框架所下载的那些。如果仅仅停留在核心的Spring框架层面,我们将错过Spring Portfolio所提供的巨额财富。整个Spring Portfolio包括多个构建于核心Spring框架之上的框架和类库。概括地讲,整个Spring Portfolio几乎为每一个领域的Java开发都提供了Spring编程模型。