虽然有一些粗糙的地方,比如applet,Enterprise javabean(EJB),Java数据对象(JDO),和无数的日志框架,Java已经成为非常多企业软件的开发平台。Spring是java成为开发平台这个故事的重要组成部分。
早些时候,Spring仅仅是重量级企业java框架的替代品,尤其是EJB。
和EJB相比,Spring提供了一个更轻量级、更精简编程模型。它增强了POJO的能力,这样的能力曾经仅仅在EJB或者其它java规范中才具备。
如今EJB使用的思想如依赖注入(DI)和面向方面编程(AOP),能够说是来自Spring成功的灵感。
这本书是Spring的一个探索。
在本章。我们从一个较高的高度看一下Spring,让你初步感受下Spring的味道。这一章将向你介绍Spring解决类型问题的好方法。本书其余部分并将环绕这种方法进行。
One-on-One: J2EE Design and Development》一书中做了描写叙述。Spring创建的目的是解决企业应用开发的复杂性,使曾经仅仅能使用EJB解决的问题,如今能够使用普通javabean实现。可是Spring的功能并不局限于server端的开发。不论什么Java应用程序都能够受益于Spring的简单、可測试性和松耦合等特性。尽管Spring常常使用bean和JavaBean来表示应用组件,可是这并不意味着一个Spring组件必须遵循JavaBean规范。
一个Spring组件能够是不论什么类型的POJO。在本书中,我採用了一个松散的JavaBean的定义,是POJO的同义词。
但Spring提供的全部功能的根源是基于一些主要的想法,全部想法都关注于Spring的基本任务:Spring简化Java开发。
Spring是怎样简化Java开发的?
- 轻量级的、微侵入性的POJO开发
- 使用DI实现松耦合、面向接口编程
- 使用切面和约定实现声明式编程
- 使用切面和模板降低样板代码
相反,在基于Spring的应用程序中的类通常没有迹象表明他们正在使用Spring。
在最坏的情况下,一个类可能会使用Spring注解,但它仍然是一个POJO。
举例说明,看以下代码清单中的HelloWorldBean类:
代码清单1.1 Spring对HelloWorldBean并不做不论什么不合理的要求。
package com.habuma.spring;
public class HelloWorldBean {
public String sayHello() {
return "Hello World";
}
}
正如您能够看到的,这是一个简单的,普通的Java类----一个POJO。没有什么特别的地方表明它是一个Spring组件。Spring的非入侵编程模型意味着一个类在Spring应用程序中具有的功能。在非Spring应用程序相同具备。
POJO的形式很easy。可是其功能能够很强大。Spring添加POJO功能的一种方式是通过DI(译者注:依赖注入)将它们组装起来。让我们看一下DI是怎样实现应用中对象之间的松耦合的。
package com.springinaction.knights;
public class DamselRescuingKnight implements Knight {
private RescueDamselQuest quest;
public DamselRescuingKnight() {
this.quest = new RescueDamselQuest();
}
public void embarkOnQuest() {
quest.embark();
}
}
正如你所示,DamselRescuingKnight(挽救少女的骑士。译者注)类在构造函数中创建了自己的quest:RescueDamselQuest(挽救少女任务。译者注)。
这使得DamselRescuingKnight类与RescueDamselQuest类紧密耦合,严重限制了骑士所能运行的任务。假设一个少女须要挽救,此骑士能够办到。可是假设此时须要杀死一条龙或者须要一个圆桌,那么这个骑士仅仅能袖手旁观了。
为了能做一些实用的事情,类之间须要了解彼此。
耦合是必需的,但应该小心地管理。
如图1.1所看到的,依赖在须要的时候被注入到对象中。
为了说明这一点。让我们看一下以下代码清单中的BraveKnight类:骑士不仅勇敢,并且可以完毕不论什么形式的任务。
如代码所看到的。与DamselRescuingKnight类不同的是。BraveKnight类没有创建自己的quest。相反,在其构造函数中加入了一个quest參数。这样的注入类型叫:构造函数注入。更重要的是。參数类型为Quest,这是一个"任务"接口,全部的任务都实现这个接口。所以BraveKnight能够完毕RescueDamselQuest(挽救少女任务), SlayDragonQuest(杀死一条龙任务),MakeRoundTableRounderQuest(制造圆桌任务)三个任务,或者其它实现了Quest接口的任务。
关键点在于BraveKnight没有与Quest接口的详细实现耦合。
其被安排什么任务都没问题,仅仅要任务实现了Quest接口就可以。这是DI最基本的益处---解耦和。假设一个对象仅仅知道他所依赖的接口(而不是接口的详细实现)。那么依赖就能够有不同的实现。依赖对象能够不知道各个实现间的差别。
依赖替换最常见的使用方法之中的一个是在測试过程中使用模拟实现来替换真实实现。因为紧密耦合,你无法充分測试DamselRescuingKnight类,可是您能够非常easy地測试BraveKnight类,通过给它任务的模拟实现,如图所看到的。
。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGF5ZGF5bGVhcm4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
您可能还注意到,类中输出信息不是依靠system.out.println(),SlayDragonQuest採用了一种更通用的实现,通过其构造函数引用了PrintStream对象。
这里的主要问题是,怎样将SlayDragonQuest给BraveKnight?怎样将PrintStream给SlayDragonQuest?
以下的代码清单展示了一个简单的Spring配置文件:knights.xml。文件里将BraveKnight、SlayDragonQuest和PrintStream装配到了一起。
在BraveKnight中。通过构造函数注入了SlayDragonQuest Bean。同一时候,SlayDragonQuest Bean使用Spring表达式语言将System.out(PrintStream类型)注入到了SlayDragonQuest的构造函数中。
SlayDragonQuest相同。仅仅有Spring,通过其配置,知道怎样将全部的组件关联到一起。这能够实如今改变这些依赖项的时候,不须要改变依赖类。
如今,不须要对很多其它的细节过多操心,第二章我们将学习很多其它Spring配置。
我们也会看看其它Bean装配的方式,包含让Spring自己主动发现bean并创建它们之间的关系的方式。
Spring有几个应用程序上下文的实现。各个实现之间的差别在于载入配置的方式不同。
knights.xml使用XML文件来声明Bean,选择比較合适的应用程序上下文是ClassPathXmlApplicationContext(对于基于java的配置,Spring所提供AnnotationConfigApplicationContext应用程序上下文.)。这个Spring上下文实现从应用程序的类路径中的一个或多个位于XML文件里载入Spring上下文。
以下代码清单中的main( )方法使用了ClassPathXmlApplicationContext来载入knights.xml,并获取Knight对象的引用。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGF5ZGF5bGVhcm4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
以下让我们看看Spring简化java开发的还有一个策略:利用切面实现声明式编程。
虽然DI可以以松耦合的方式将软件组件组合在一起,可是面向切面编程(AOP)使你可以捕获应用中使用的可重用组件的功能。
软件系统是由多个组件组成的,每一个组件负责一个特定的功能。
但往往这些组件还承担了其核心功能之外的责任。如日志、事务管理和安全性等。
这些系统服务通常称为横切关注点,由于他们往往跨越一个系统中的多个组件。
这意味着,假设你要改动这些关注点的工作方式,您将须要改动多个组件。即使你将关注点抽取出一个单独的模块,在每一个组件中调用这个组件的一个方法。那么这种方法在多个组件中都会出现。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGF5ZGF5bGVhcm4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
这使组件,更有凝聚力,更专注于自己的详细问题,全然不用关注可能涉及的系统服务。简而言之,切面确保了pojo是简单的。
能够将切面看做毯子。覆盖应用程序的很多组件。
如图1.3所看到的。在其核心,应用程序包括了实现业务功能的模块。
利用AOP,您能够使用功能层覆盖你的核心应用程序。
这些层能够灵活声明的方式应用在您的应用程序,你的核心应用程序甚至不须要知道它们的存在。这是一个强大的概念,由于其组织了安全、事务、日志等关注点污染应用程序的核心业务逻辑。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGF5ZGF5bGVhcm4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
以下代码清单显示了把BraveKnight和Minstrel组织在一起的第一次尝试。
如果你想使用说书人这个服务来记录BraveKnight的一些事迹。
以下的代码清单列出了你可能会用到的说书人(Minstrel)类。
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGF5ZGF5bGVhcm4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
如今你须要做的就是回到你的Spring配置文件,在当中声明一个Minstrel Bean并将其注入到BraveKnight Bean的构造方法中。可是请等一下......
为什么骑士总是要提醒说书人呢?