JUnit in Action 2nd Edition 第三章 精通Junit(1)

第三章 精通JUnit

到目前为止,我们做了JUnit综述和演示如何使用(第一章)。我们也看到了JUnit内部的核心类和方法,它们如何同其它的交互(第二章)。

我们现在通过介绍实实在在的组件和测试来深度挖掘它,在这一章中,我们使用控制器设计模式实现一个小的应用,我们然后使用JUnit测试这个应用的每一个部分,当编写和组织测试时我们也将看到JUnit的最佳实践。

3.1 介绍控制器组件

Core Java EE Patterns》中描述控制器controller作为一个组件,“同客户端交互,控制

和管理每一个请求的处理”。然后告诉我们它用于表示层和业务层模式。

通常,一个控制器有如下行为:

n 接受请求

n 在请求上执行公共的计算

n 选择一个合适的请求处理程序

n 路由请求,因此处理程序能够执行相关的业务逻辑

n 可以为错误和异常提供一个顶层的处理程序

你会发现在各种各样的应用中控制器普遍地使用。例如,在一个表示层模式,一个web控制器接受HTTP请求和提取HTTP参数,cookiesHTTP头文件,可能使用HTTP元素容易地访问应用程序的其余部分。一个web控制器基于请求元素决定调用合适的业务逻辑组件。也许在HTTP会话持久性数据的帮助下。数据库,或者一些其他资源。Apache Struts框架就是一个web控制器的例子。

控制器另一常见的运用是在业务层模式中处理应用程序。许多业务应用支持多个表示层,HTTP客户端可以处理web应用。Swing客户端可以处理desktop应用,在这些表示层下,常常有一个应用控制器或者状态机。程序员实现许多Enterprise JavaBeanEJB)应用以此方式,EJB层有自己的控制器,通过一个业务外观模式或者委派模式连接不同的表示层。

它给出了控制器的多种用途,就不会惊讶于控制器出现在大量的企业架构模式中,包括Page ControllerFront controller,Application Controller

控制器的设计是你实现这些经典模式的第一步,让我们完成一个简单控制器的代码。看如何工作,然后尝试一些测试。如果你想跟着自己的感觉与运行测试。本章所有的源代码都在SourceForge中可以找到(http://junitbook.sf.net)。附录A中有关于这些源代码设置信息。

3.1.1接口设计

仔细检查控制器的描述,有四个对象较突出:RequestResponseRequestHandlerControllerController接受Request,调度RequestHandler,然后返回Response对象。动手描述,你能编写一些简单的启动接口,就像Listing3.1所示。

Listing 3.1 RequestResponseRequestHandlerController interface

public interface Request{

String getName();

}

public interface Response{}

public interface RequestHandler{

Response process(Request request) throws Excepion;

}

public interface Controller{

Response processRequest(Request request);

void addHandler(Request request,RequestHandler requestHandler);

}

1.首先,定义一个Request接口,包含一个单独的getNmae方法,返回唯一请求名,因此你能区分每一个请求。因此你开发这个组件,你将需要其他的方法,但是你能添加他们当你进行编写时。

2.下一步,指定一个空接口,设计编码,你仅需要返回一个Response对象,Response包含的东西你能稍后处理。现在,你需要一个Response类型能插入一个签名。

3.下一步是定义一个RequestHandler接口,它能处理Request(请求)和返回你的Response(响应)。RequestHandler是一个辅助部件设计,执行大多的脏数据工作。它可能调用类抛出任何类型的异常。Exceptionprocess方法抛出的。

4.定义一个顶层的方法来处理进入的请求。接受这个请求后,控制器发送它到合适的

RequestHandler上,通知processRequest不要声明任何异常,方法在栈顶能够捕获处理所有的内部错误。假如它不抛出一个异常,错误将常常进入Java Virtual MachineJVM)或者servlet 容器中。JVM或者容器将然后出现用户使用的空白页中的一个。最好你为自己编码。

最后,控制器将是最好的设计元素,addHandler方法允许你继承Controller不用修改Java源代码。

设计模式实战:控制反转模式

一个简单的控制反转的例子是使用控制器注册一个处理程序。你可能知道这样一个模式,叫做好莱坞原则,“don't call uswe'll call you”。对象注册作为事件处理程序,当事件发生时,对已注册对象调用一个钩子方法,控制反转让架构管理事件生命周期同时,允许开发者给框架事件插入自定义的处理程序。

 

 

3.1.2实现基本类

继续采用LIsting3.1中的接口,Listing3.2展示一个简单控制器类的初稿。

Listing3.2 The generic Controller

[...]

import java.util.HashMap;

import java.util.Map;

public class DefaulController implements Controller{

private Map requestHandlers = new HashMap();

protected RequestHandler getHandler(Request request){

if(!this.requestHandler.containsKey(request.getNmae())){

String message = "Cannot find handler for request name" 

+ "[" + request.getName() + "]";

throw new RuntimeException(message);

}

return (RequestHandler) this.requestHandlers.get

(request.getName());

}

public Response processRequest(Request request){

Response response;

try{

response = getHandler(request).process(request);

}

catch (Exception exception){

response = new ErrorResponse(request,exception);

}

return response;

}

public void addHandler(Request request,RequestHander requestHandler){

if (this.requestHandlers.containsKey(request.getName())){

throw new RuntimeException("A request handler has " + 

"already been registered for request naem"

+ "[" + request. getName() + "]")

}

else{

this.requestHandlers.put(request.getName

(),requestHandler);

}

}

}

1.第一步,声明一个hashMap(java.util.HashMap)充当请求处理器的注册表。

2.下一步,添加一个protected方法,getHandler,获取给定请求的RequestHandler

3.如果一个RequestHandler没有注册,将抛出一个RuntimeException

(java.lang.RuntimeExcption)异常,这是一个偶然事件表示成一个编程错误,而不是由用户或

者外部系统产生的问题。Java不需要你去使用方法签名声明RuntimeException。但是你依旧能作为异常捕获它。一个改进是将添加一个指定的异常给控制器框架(例如

NoSuitableRequestHandlerExceprion).

4.你的有效方法然后返回合适的处理器给它的调用者

5.processRequest方法是Controller类的核心,这个方法指派合适的处理器给请求和回传处理器的Response。假如一个异常产生,它将在ErrorResponse类中捕获,如Listing3.3所示。

6.最后,检查是否该处理程序的名称已注册。如果有的话然后抛出一个异常,看这个实现,请注意请求对象签名的次数。但是你仅仅使用它的名字,这种事情常常发生,当代码编写之前一个接口已经定义。一种方法去避免过度设计一个接口最好实践测试驱动开发(TDD)。

Lisiting3.3 Special responseclass signaling an error

[...]

public class ErrorResponse implements Response{

private Request origianlRequest;

private Exception originalException;

public ErrorResponse(Request request,Exception exception){

this.originalRequest = request;

this.originalException = exception;

}

public Request getOriginalRequest(){

return this.originalRequest;

}

public Exception getOriginalException(){

return this.originalException;

}

}

 

此时,你有一个粗糙但是有效的控制器骨架,表3.1展示上面这部分源代码如何要求。

Table3.1 Resolving the base requirements form the component

Requirement

Resolution

Accept requests

public Response processRequest(Request request)

Select handler

this.requestHandlers.get(request.getName())

Route requests

response=getRequestHandler(request).process(request);

Error handling

Cubclass ErrorResponse

 

对于许多开发者下一步将使用骨架控制器粗略地制作一个桩程序。作为测试注入开发者,我们能编写一个测试套件给控制器不用小题大做使用一个桩程序。这是单元测试之美,我们能编写一个包验证它是否工作,所有外部的常规Java应用。

3.2开始测试

合适的灵感让我们编写四个接口和两个开始类,假如我们现在不编写自动化测试,极限编程将找我们的麻烦。

列表3.2和列表3.3以一个简单的实现开始,我们在单元测试将做同样的事,我们可以探讨什么是尽可能简单的测试用例。

3.2.1测试DefaultController

DefaultController类的实例化的测试用例怎么实现,第一步在做任何有用的事是控制器构造它,因此我们开始,列表3.4展示引导测试代码,它构建DefaultController对象和设置测试框架。

Listing 3.4 TestDefaultControllera bootstrap iteration

[...]

import org.junit.core.Test;

import static org.junit.Assert.*;

public class TestDefaultController{

private DefaultController controller;

@Before

public void instantiate() throws Exception{

controller = new DefaultController();

}

@Test

public void testMethod(){

throw new RuntimeException("implement me");

}

}

1.测试类的名字需要以前缀Test开始,命名规则并不是必须的,但是一般都这么做,我们将类标记成测试类后,我们更容易地识别和在构建脚本中过滤它们。相对地,依赖你的本地语言,你可以更愿意使用Test作为类的前缀。

2.下一步,使用@Before注释实例化DefaultController的方法,这是JUnit框架调用测试方法之间的一个内建的扩展点。

3.插入一个虚拟的测试方法,以使有事件去运行。只要确认测试设施是工作的,你能开始添加真正的测试方法,虽然这个测试运行了,但是它依旧会失败,下一步将修补这个测试。

4.对还没有实现的测试代码最佳实践是抛出一个异常。它将阻止测试通过,提醒你必须实现这些代码。

现在你有一个引导测试,下一步决定首先测试什么。

JUnits details

@Before@After注释方法在每一个@Test方法执行之前和之后执行,不管测试失败或通过,它帮助你提取所有的公共逻辑,像实例化你的领域对象和设置它们在一些已知的状态。

你可以有很多这些你想要的方法,但要注意你将有超过一个@Before@After方法。它们的执行顺序是不用定义的。

JUnit也提供@BeforeClass@AfterClass注释你的方法在类级别,这个方法将被执行一次,在所有你的@Test方法之前或之后。再次,就像@Before@After注释一样,你可以有很多这些你想要的方法,它们的执行顺序是不用定义的。

你需要记住@Before/@After@BeforeClass/@AfterClass注释方法必须设置成public

@BeforeClass/@AfterClass注释方法必须设置成publicstatic

3.2.2添加一个处理器

现在已有一个引导测试,下一步是决定首先测试什么。我们使用DefaultController对象开始测试用例,因为这是这个练习的重点:创建一个控制器。编写一些代码确定它编译成功,但是如何看测试是否运行呢?

控制器的目的是处理请求和返回响应。但是在处理请求之前,设计调用添加一个

RequestHandler去做处理工作,因此,第一件事首先是:你将测试是否能添加一个

RequestHandler

在第一章中运行过这个测试和返回一个已知的结果。去看测试是否成功,你将比较期望值与测试对象的返回值是否一致,addHandler的签名是:

void addHandler(Request request,RequestHandler requestHandler)

添加一个RequestHandler,你需要一个已知的Request名字,去检查是否添加成功,你能使用DefaultController类中的getHandler方法,使用如下签名:

RequestHandler getHandler(Request request)

这样是可行的,因为getHandler方法访问权限是protected,测试类是位于同样的包中。这是一个原因去定义测试在同一个包下。

测试第一步,你能做如下事情:

n 添加一个RequestHandler,引用一个Request

n 取得一个RequestHandler和通过同样的Request

n 检查看是否得到同一个RequestHandler返回

n 测试来自哪里?

现在你知道你需要哪些对象,下一个问题是,“这些对象来自哪里?”,你因该向前并编写一些你将在应用中使用的对象。例如一个登录请求?

单元测试的重点是一次测试一个对象,在面向对象环境中,像Java,你设计对象同其它对象交互,创建一个单元测试,因此,你需要两种类型的对象:你测试的领域对象(domain object)和测试对象(test objects)同测试下的对象进行交互。

定义:DEFINITION

Domain object:在单元测试上下文中,就域对象而言,用于对比和比较应用程序中使用的对象和测试程序中使用的对象。在测试中任何对象被认为是一个域对象。

假如你需要另一个域对象,像登录请求,测试失败,罪魁祸首将很难确定,你可能不能去告诉这个问题是否是一个控制器或者请求,因此,在一系列测试的第一步,你将使用唯一的类是DefaultController,其它的一切将是指定的测试类。

JUnit最佳实践:单元测试的重点是一次测试一个对象

单元测试一个重要的方面是它们是细粒度的。单元测试独立检查你创建的每一个对象,这样只要问题发生就能隔离。假如你在测试中放置多个对象,你不能预测当改变发生时对象间如何交互,你可以在可预测的测试对象下测试对象,另一种软件测试形式,集成测试,检查对象之间的相互作用,第四章有更多关于其他类型的测试。

Where do test classes live

测试类将放置在哪里?Java提供几个选择,如下其中的一个:

n 在包中将它们标记类成public

n 在测试类中将它们设置成内部类

如果类是简单的可能保持那种形式,它们很容易作为内部类去编写它们,这个类的实例很简单,Listing3.5演示内部类添加到TestDefaultController类中。

Listing3.5Test classes as inner classes

public class TestDefaultController{

private class SampleRequest implements Request{

public String getName(){

return "Test";

}

}

private class SampleHandler implements RequestHandler{

public Response process(Request request) throws Exception{

return new SampleResponse();

}

}

private class SampleResponse implements Response{

//empty

}

}

1.首先,设置一个请求对象,返回已知的名字(Test)。

2.下一步,实现一个SampleHandler,接口调用process方法,因此你不得不那样编码。现在你没有测试process方法,因此它返回一个SampleResponse对象满足签名。继续定义一个空的SampleResponse以便可以实例化。

Listing3.5中,我们看Listing3.6演示一个添加RequestHandler测试。

Listing3.6 TestDefaultController.tetAddHandler

[...]

import static org.junit.Assert.*;

public class TestDefaultController{

@Test

public void testAddHandler(){

Request request = new SampleRequest();

RequestHandler handler = new SampleHandler();

controller.addHandler(request,handler);

RequestHandler handler2 = conrtoller.getHandler(request);

assertSame("Hander we set in conrotller should be teh same 

handler we get",handler2,handler);

}

}

1.给测试方法设置一个明显的名字,使用@Test注释测试方法。

2.记住实例化测试对象

3.这段代码是测试的重点:Controller(测试下的对象)添加测试处理器,注意

DefaultController对象是被@Before注释的方法实例化的

4.在新的变量名下回读处理器

5.检查是否得到相同的对象

JUnit最佳实践:选择有意义的测试方法名

你能知道用@Test注释的方法是一个测试方法。你也必须通过阅读方法名字能理解它是一个测试方法。虽然JUnit没有要求任何特定的规则来命名测试方法,一个好的规则是使用testxxx结构开始命名测试方法,xxx是要测试域方法的名字。当你添加其它测试与相同的测试名冲突时,移动到testxxxyyy结构,yyy描述测试的不同之处。不要害怕你测试方法的名字过长或者冗长。你将在本章最后看到,有时不太明显,一个测试是测试方法通过查找它的断言方法时,命名你的测试方法使用一个描述性的方式,必要时添加注释。

虽然它简单,但是这个单元测试证实了关键的前提用于存储和检索RequestHandler的机制是灵活且好的。假如addHandler或者getRequest在将来失败了,测试将迅速地甄别问题所在。

像这样创建许多测试,你将注意以下模式:

1.将测试环境设置成一个已知的状态(创建对象,获得资源)。预测试状态参考test fixture(测试夹具)

2.调用测试下的方法

3.确认测试结果,常常调用一个或多个断言(assert)方法。

 

3.2.3处理一个请求

我们看看控制器的核心测试目标,处理请求。按照惯例,我们在Listing3.7展示这个测试并回顾它。

Listing3.7 testProceesRequest method

import static org.junit.Assert.*;

public class TestDefaultController{

[...]

@Test

public void testProcessRequest(){

Request request = new SampleRequest();

RequestHandler handler = new SampleHandler();

controller.addHandler(request,handler);

Response response = conroller.processRequest(request);

assertNotNull("Must not return a null response",response);

assertEquals("Response should be of type SampleResponse",

SampleResponse.class,response.getClass());

}

}

1.首先,使用@Test注释测试方法,然后给出测试取一个简单统一的名字

2.设置测试对象和添加测试处理器

3.Response对象代码从Listing3.6分离出来,然后调用processRequest方法

4.验证返回的Response对象是否为空。这很重要,因为你在Response对象上调用getClass方法,如果Response对象为空,它将验证失败,并抛出一个令人畏惧的空指向异常

(NullPointerException),使用assertNotNull(Sring,Object)签名假如测试失败的话,错误显示是有意义和容易去理解的。但是如果使用assertNotNull(Object)签名,JUnit运行器能够显示一个栈跟踪java.lang.AssertionError异常而没有错误信息,这将使诊断更加困难。

5.最后,比较测试结果和期望结果(SampliResponse)是否一致。

 

JUnit最佳实践:在断言调用中解释失败原因

无论什么时候使用JUnit assert*方法,确认使用这样的签名形式,使用一个字符串(String)作为第一个参数,这个参数让你提供一个有意义的描述,假如断言失败的话显示Junit测试运行器信息,不要使用这样的参数,当失效发生时它很难去理解。

 

设置逻辑分解

因为两个测试做了同样的设置,你能复制代码进入@Before注释中,同一时刻,你又不想复制它到一个新的@Before方法中,因为你不确定将首先执行哪一个方法,可以得到一个异常。你能将代码移动到同样的@Before方法中。

随着你添加更过的测试方法,你可能需要调整如何做,在@Before方法中,现在,排除重复的代买尽可能帮助你编写更多的测试。Listing3.8,显示改进的TestDefaultController类,改近的地方以粗体显示。

Listing3.8 TestDefaultController After some refactoring

[...]

public class TestDefaultController{

private DefaultController controller;

private Request request; 

private RequestHandler handler;

@Before

public void initialize()throws Exception{

controller = new DefaultController();

request = new SampleRequest();

handler = new Samplehandler();

controller.addHandler(request,handler);

}

private class SampleRequest implements Request{

//Same as in listing 3.1

}

private class SampleHandler implements RequestHandler{

//Same as in listing 3.1

}

private class SampleResponse implements Response{

//Same as in listing 3.1

}

@Test

public void testAddHandler(){

RequestHandler handler2 = controller.gethandler(request);

assertSame(handler2,handler);

}

@Test

public void testProcessRequest(){

Response response =controller.processRequest(request);

assertNotNull("Must not return a null response",response);

assertEquals("response should be of type 

 

SampleResponse",SampleResponse.class,response.getClass());

}

}

1.我们移动测试RequestRequestHandler对象的初始化到initialize()方法中。

2.initialize()保存着testAddhandlertestProcessRequest中的重复代码。

3.因此,我们编写一个新的@Before注释方法以便添加处理器到控制器,因为@Before方法在每个单独@Test方法之前执行,我们确认我们完整地设置了DefaultConroller对象。

定义(definition):代码重构Refactor,改进已存在代码的设计。

 

注意在一个测试方法中,不要试图通过测试多个操作去共享设置代码,如Listing3.9所示。

Listing3.9 Anti-example:don't conbine test methods.

public class TestDefaultController{

@Test

public void tetsADdAndProcess(){

Request request = new SampleRequest();

Requesthandler handler = new Samplehandler();

controller.addhandler(request,handler);

RequestHandler handler2 = controller.gerhandler(request);

assertEquals(handler2,handler);

// DO NOT COMBINE TEST METHODS THIS WAY

Response response = conrtoller.processRequest(request);

assertNotNull("Must not return a null response",response);

assertEquals(SampleResponse.class,response.getCalss());

}

}

 

JUnit最佳实践:一个单元测试对应一个@Test方法

不要将多个测试塞入一个方法中。这将导致测试方法复杂化,变得越来越难以阅读和理解。更糟糕的是,测试方法中会有更过的逻辑,将增加不运行和调试的风险,滑坡将结束鞭子额测试来测试你的测试。

在程序中,当出现工作或者失败的时候,单元测试将给你信心,如果将多个单元测试放入一个测试方法中,它将变得更加困难地观察哪里出错。当测试共享同样的方法时,一个失败的测试会使夹具处于可预知的状态。在方法中其它的测试嵌入可能不会运行或者正确的运行,你的测试结果的图片通常是不完整的或误导的。

因为所有的测试方法在一个测试类中共享同样的夹具,JUnit能产生一个自动的测试套件,它很容易地替换每一个单元测试用它们自己的方法。假如你需要使用同样的代码块在多个测试间,把它们提取成一个实用方法,每一个测试方法都能调用它,更好地,假如所有的方法能共享代码,将它放入到夹具中。

另外常用的陷阱是编写测试方法并不包含断言声明,当你执行这些测试时,观察JUnit标记它们成功,但这是一个成功测试的假象,总是使用断言调用。只有一种情况不使用断言是可以接受的,当一个异常抛出表示一个错误条件时。

为了更好的结果,你的测试方法应当简明地、集中你的域方法。每个测试方法必须尽可能地清晰和集中,这就是为什么JUnit提供使用@Before@After@AfterClass@BeforeClass注释:因此你能共享夹具而不用组合测试方法。

3.2.4改进testPeocessRequest

Listing3.7中编写testProcessRequest方法时,我们希望响应返回的值是期望的回应,实现确认返回的对象是我们期望的对象。但是我们希望知道的是返回的响应是否与期望的响应相等。

这个响应可能是一个不同的类,重要的是这个类识别他自己是正确的响应。

assertSame方法确认两个引用是同一个对象,assertEquals方法同equals方法一样。从基类

Object继承而来,如果两个不同的对象有同样的身份,需要提供自己定义的ID。对于像一个响应对象,你能指派每个响应它自己的令牌。

空实现的SampleResponse没有名字属性可以测试,为了得到想要的测试,不得不首先实现更多的Response类。Listing3.10展示加强的SampleResponse类。

Listing3.10 A refactored SampleResponse

public class TestDefaultController{

private class SampleResponse implements Resonse{

private static final String NAME = "Test";

public String getName(){

return NAME;

}

public boolean equals(Object object){

boolean result = false;

if (object instanceof SampleREsponse){

result = ((SampleResponse) object).getName().equals(getName());

}

return result;

}

public  int hashCode(){

return NAME.hashCode();

}

}

}

现在SampleResponse有一个身份(表现getName())和它自己equals方法,你能修改这个测试方法。

@Test

public void testPeocessRequest(){

Response response = controller.processRequest(request);

assertNotNull("Must not return a null response",response);

assertEquals(new SampleREsponse(),response);

}

我们使用SampleResponse类介绍身份的概念在测试中的目的。但是测试告诉你应该有存在合适的Response类,你需要修改Response接口如下;

public interface Response{

String getName();

}

就像你看到这样,测试能告诉或者指导你一个好的设计,但是这不是测试真正的目的,不要忘记测试常常用于保护我们在代码中介绍错误。这样做我们需要测试每一个条件,在我们应用可执行的情况下,我们开始探讨异常条件在下一章中。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本书从认识JUnit、不同的测试策略、JUnit与构建过程、JUnit扩展4个方面,由浅入深、由易到难地对JUnit展开了系统的讲解,包括探索JUnit的核心、软件测试原则、测试覆盖率与开发、使用stub进行粗粒度测试、使用mockobjects进行测试、容器内测试、从Ant中运行JUnit测试、从Maven2中运行JUnit测试、持续集成工具、表示层的测试、Ajax测试、使用Cactus进行服务器端的Java测试测试JSF应用程序、测试OSGi组件、测试数据库访问、测试基于JPA的应用程序、JUnit的其他用法等内容。 需要的朋友们可以下载试试吧! JUnit实战(第2)中文版不仅仅介绍了使用JUnit框架测试项目必须掌握的核心概念,还指导读者如何使用JUnit框架编写自己的测试用例,并针对在编写代码的过程中如何使得代码可测试给出建议。本书还介绍了基础的软件开发原则,如测试驱动开发(TDD)等,以便指导用户如何使用不同的工具来测试典型JavaEE应用程序的每一层。此外,本书也提供了几个附录,以帮助读者快速转换到最新本的JUnit,并且能够轻松地集成自己喜欢的IDE。 本书适合于已具有一定Java编程基础的读者,以及在Java平台下进行各类软件开发开发人员、测试人员。对于单元测试学习者和编程爱好者来说,本书则具有极高的学习参考价值。大家可以下载参考学习一下!
第一部分 Spring的核心 第1章 开始Spring之旅 1.1 Spring是什么 1.2 开始Spring之旅 1.3 理解依赖注入 1.3.1 依赖注入 1.3.2 DI应用 1.3.3 企业级应用中的依赖注入 1.4 应用AOP 1.4.1 AOP介绍 1.4.2 AOP使用 1.5 小结 第2章 基本Bean装配 2.1 容纳你的Bean 2.1.1 BeanFactory介绍 2.1.2 使用应用上下文 2.1.3 Bean的生命 2.2 创建Bean 2.2.1 声明一个简单的Bean 2.2.2 通过构造函数注入 2.3 注入Bean属性 2.3.1 注入简单的数值 2.3.2 使用其他的Bean 2.3.3 装配集合 2.3.4 装配空值 2.4 自动装配 2.4.1 四种自动装配类型 2.4.2 混合使用自动和手动装配 2.4.3 何时采用自动装配 2.5 控制Bean创建 2.5.1 Bean范围化 2.5.2 利用工厂方法来创建Bean 2.5.3 初始化和销毁Bean 2.6 小结 第3章 高级Bean装配 3.1 声明父Bean和子Bean 3.1.1 抽象基Bean类型 3.1.2 抽象共同属性 3.2 方法注入 3.2.1 基本的方法替换 3.2.2 获取器注入 3.3 注入非Spring Bean 3.4 注册自定义属性编辑器 3.5 使用Spring的特殊Bean 3.5.1 后处理Bean 3.5.2 Bean工厂的后处理 3.5.3 配置属性的外在化 3.5.4 提取文本消息 3.5.5 程序事件的解耦 3.5.6 让Bean了解容器 3.6 脚本化的Bean 3.6.1 给椰子上Lime 3.6.2 脚本化Bean 3.6.3 注入脚本化Bean的属性 3.6.4 刷新脚本化Bean 3.6.5 编写内嵌的脚本化Bean 3.7 小结 第4章 通知Bean 4.1 AOP简介 4.1.1 定义AOP术语 4.1.2 Spring对AOP的支持 4.2 创建典型的Spring切面 4.2.1 创建通知 4.2.2 定义切点和通知者 4.2.3 使用ProxyFactoryBean 4.3 自动代理 4.3.1 为Spring切面创建自动代理 4.3.2 自动代理@AspectJ切面 4.4 定义纯粹的POJO切面 4.5 注入AspectJ切面 4.6 小结 第二部分 企业Spring 第5章 使用数据库 5.1 Spring的数据访问哲学 5.1.1 了解Spring数据访问的异常体系 5.1.2 数据访问的模板化 5.1.3 使用DAO支持类 5.2 配置数据源 5.2.1 使用JNDI数据源 5.2.2 使用数据源连接池 5.2.3 基于JDBC驱动的数据源 5.3 在Spring里使用JDBC 5.3.1 处理失控的JDBC代码 5.3.2 使用JDBC模板 5.3.3 使用Spring对JDBC的DAO支持类 5.4 在Spring里集成Hibernate 5.4.1 选择Hibernate的本 5.4.2 使用Hibernate模板 5.4.3 建立基于Hibernate的DAO 5.4.4 使用Hibernate 3上下文会话 5.5 Spring和Java持久API 5.5.1 使用JPA模板 5.5.2 创建一个实体管理器工厂 5.5.3 建立使用JPA的DAO 5.6 Spring和iBATIS 5.6.1 配置iBATIS客户模板 5.6.2 建立基于iBATIS的DAO 5.7 缓存 5.7.1 配置缓存方案 5.7.2 缓存的代理Bean 5.7.3 注解驱动的缓存 5.8 小结 第6章 事务管理 6.1 理解事务 6.1.1 仅用4个词解释事务 6.1.2 理解Spring对事务管理的支持 6.2 选择事务管理器 6.2.1 JDBC事务 6.2.2 Hibernate事务 6.2.3 JPA事务 6.2.4 JDO事务 6.2.5 JTA事务 6.3 在Spring中编写事务 6.4 声明式事务 6.4.1 定义事务参数 6.4.2 代理事务 6.4.3 在Spring 2.0里声明事务 6.4.4 定义注释驱动事务 6.5 小结 第7章 保护Spring 7.1 Spring Security介绍 7.2 验证用户身份 7.2.1 配置Provider Manager 7.2.2 根据数据库验证身份 7.2.3 根据LDAP仓库进行身份验证 7.3 控制访问 7.3.1 访问决策投票 7.3.2 决定如何投票 7.3.3 处理投票弃权 7.4 保护Web应用程序 7.4.1 代理Spring Security的过滤器 7.4.2 处理安全上下文 7.4.3 提示用户登录 7.4.4 处理安全例外 7.4.5 强制Web安全性 7.4.6 确保一个安全的通道 7.5 视图层安全 7.5.1 有条件地渲染内容 7.5.2 显示用户身份验证信息 7.6 保护方法调用 7.6.1 创建一个安全切面 7.6.2 使用元数据保护方法 7.7 小结 第8章 Spring和基于POJO的远程服务 8.1 Spring远程调用概览 8.2 与RMI一起工作 8.2.1 连接RMI服务 8.2.2 输出RMI服务 8.3 使用Hessian和Burlap的远程调用 8.3.1 访问Hessian/Burlap服务 8.3.2 用Hessian或Burlap公开Bean的功能 8.4 使用HTTP invoker 8.4.1 通过HTTP访问服务 8.4.2 把Bean作为HTTP服务公开 8.5 Spring和Web服务 8.5.1 使用XFire将Bean输出为Web服务 8.5.2 使用JSR-181注释声明Web服务 8.5.3 消费Web服务 8.5.4 使用XFire客户端代理Web服务 8.6 小结 第9章 在Spring中建立契约优先Web服务 9.1 介绍Spring-WS 9.2 定义契约(首先!) 9.3 使用服务端点处理消息 9.3.1 建立基于JDOM消息的端点 9.3.2 序列化消息载荷 9.4 合并在一起 9.4.1 Spring-WS:全景视图 9.4.2 将消息映射到端点 9.4.3 置入服务端点 9.4.4 配置消息序列化器 9.4.5 处理端点异常 9.4.6 提供WSDL文件 9.4.7 部署服务 9.5 消费Spring-WS Web服务 9.5.1 使用Web服务模板 9.5.2 使用Web服务的网关支持 9.6 小结 第10章 Spring消息 10.1 JMS简介 10.1.1 构建JMS 10.1.2 介绍JMS的优点 10.1.3 在Spring中安装ActiveMQ 10.2 协同使用JMS和Spring 10.2.1 处理冗长失控的JMS代码 10.2.2 使用JMS模板 10.2.3 转换消息 10.2.4 将Spring的网关支持类应用于JMS 10.3 创建消息驱动POJO 10.3.1 创建消息监听器 10.3.2 编写纯POJO MDP 10.4 使用基于消息的RPC 10.4.1 引入Lingo 10.4.2 输出服务 10.4.3 代理JMS 10.5 小结 第11章 Spring和EJB 11.1 在Spring中置入EJB 11.1.1 代理会话Bean(EJB 2.x) 11.1.2 将EJB置入Spring Bean 11.2 开发Spring驱动的EJB(EJB 2.x) 11.3 Spring和EJB3 11.3.1 引入Pitchfork 11.3.2 从Pitchfork起步 11.3.3 通过注释注入资源 11.3.4 使用注释声明拦截器 11.4 小结 第12章 访问企业服务 12.1 从JNDI中获取对象 12.1.1 使用传统的JNDI 12.1.2 注入JNDI对象 12.1.3 在Spring 2中注入JNDI对象 12.2 发送电子邮件 12.2.1 配置邮件发送器 12.2.2 构建电子邮件 12.3 调度任务 12.3.1 使用Java Timer调度任务 12.3.2 使用Quartz调度器 12.3.3 按调度计划调用方法 12.4 使用JMX管理Spring Bean 12.4.1 将Spring Bean输出为MBean 12.4.2 远程访问MBean 12.4.3 处理通知 12.5 小结 第三部分 Spring客户端 第13章 处理Web请求 13.1 开始Spring MVC之旅 13.1.1 请求生命中的一天 13.1.2 配置DispatcherServlet 13.1.3 Spring MVC概述 13.2 将请求映射到控制器 13.2.1 使用SimpleUrlHandler Mapping 13.2.2 使用ControllerClassName HandlerMapping 13.2.3 使用元数据映射控制器 13.2.4 使用多映射处理器 13.3 用控制器处理请求 13.3.1 处理命令 13.3.2 处理表单提交 13.3.3 用向导处理复杂表单 13.3.4 使用一次性控制器 13.4 处理异常 13.5 小结 第14章 渲染Web视图 14.1 视图解析 14.1.1 使用模板视图 14.1.2 解析视图Bean 14.1.3 选择视图解析器 14.2 使用Spring模板 14.2.1 绑定表单数据 14.2.2 渲染被存储在外部的文本 14.2.3 显示错误 14.3 使用Tile设计页面布局 14.3.1 Tile视图 14.3.2 Tile控制器 14.4 使用JSP的替代技术 14.4.1 使用Velocity模板 14.4.2 使用FreeMarker 14.5 产生非HTML输出 14.5.1 产生Excel工作表 14.5.2 产生PDF文档 14.5.3 开发自定义视图 14.6 小结 第15章 使用Spring Web Flow 15.1 开始Spring Web Flow之旅 15.1.1 安装Spring Web Flow 15.1.2 配置流程执行程序 15.1.3 登记流程定义 15.2 布置流程的基础 15.2.1 流程变量 15.2.2 start和end状态 15.2.3 搜集顾客信息 15.2.4 绑定比萨饼订单 15.2.5 完成订单 15.2.6 几个结束工作 15.3 高级Web流程技术 15.3.1 使用decision状态 15.3.2 提炼子流程并使用子状态 15.4 集成Spring Web Flow与其他框架 15.4.1 Jakarts Struts 15.4.2 JavaServer Face 15.5 小结 第16章 集成其他Web框架 16.1 协同使用Spring和Struts 16.1.1 向Struts注册Spring插件 16.1.2 编写知晓Spring的Struts动作 16.1.3 委托Spring配置的动作 16.1.4 关于Struts 2 16.2 协同使用Spring和WebWork 2/Struts 2 16.3 集成Spring和Tapestry 16.3.1 集成Spring和Tapestry 3 16.3.2 集成Spring和Tapestry 4 16.4 协同使用Spring和JSF 16.4.1 解析JSF管理的属性 16.4.2 解析Spring Bean 16.4.3 在JSF页面中使用Spring Bean 16.4.4 在JSF中暴露应用程序环境 16.5 Spring中带有DWR的支持Ajax的应用程序 16.5.1 直接Web远程控制 16.5.2 访问Spring管理的Bean DWR 16.6 小结 附录A 装配Spring A.1 下载Spring A.1.1 研究Spring发布 A.1.2 构建自己的类路径 A.2 把Spring添加为一个Maven 2依赖项 A.3 Spring与Ant A.4 Spring与Log4j 附录B 用(和不用)Spring进行测试 B.1 测试简介 B.1.1 理解不同类型的测试 B.1.2 使用JUnit B.1.3 Spring在测试中的角色 B.2 单元测试Spring MVC控制器 B.2.1 模拟对象 B.2.2 断言ModelAndView的内容 B.3 使用Spring进行综合测试 B.3.1 测试装配后的对象 B.3.2 综合测试事务处理对象 B.3.3 测试数据库 B.3.4 使用Gienah Testing在JUnit 4中进行测试 B.4 小结
第一部分 Spring的核心 第1章 开始Spring之旅 1.1 Spring是什么 1.2 开始Spring之旅 1.3 理解依赖注入 1.3.1 依赖注入 1.3.2 DI应用 1.3.3 企业级应用中的依赖注入 1.4 应用AOP 1.4.1 AOP介绍 1.4.2 AOP使用 1.5 小结 第2章 基本Bean装配 2.1 容纳你的Bean 2.1.1 BeanFactory介绍 2.1.2 使用应用上下文 2.1.3 Bean的生命 2.2 创建Bean 2.2.1 声明一个简单的Bean 2.2.2 通过构造函数注入 2.3 注入Bean属性 2.3.1 注入简单的数值 2.3.2 使用其他的Bean 2.3.3 装配集合 2.3.4 装配空值 2.4 自动装配 2.4.1 四种自动装配类型 2.4.2 混合使用自动和手动装配 2.4.3 何时采用自动装配 2.5 控制Bean创建 2.5.1 Bean范围化 2.5.2 利用工厂方法来创建Bean 2.5.3 初始化和销毁Bean 2.6 小结 第3章 高级Bean装配 3.1 声明父Bean和子Bean 3.1.1 抽象基Bean类型 3.1.2 抽象共同属性 3.2 方法注入 3.2.1 基本的方法替换 3.2.2 获取器注入 3.3 注入非Spring Bean 3.4 注册自定义属性编辑器 3.5 使用Spring的特殊Bean 3.5.1 后处理Bean 3.5.2 Bean工厂的后处理 3.5.3 配置属性的外在化 3.5.4 提取文本消息 3.5.5 程序事件的解耦 3.5.6 让Bean了解容器 3.6 脚本化的Bean 3.6.1 给椰子上Lime 3.6.2 脚本化Bean 3.6.3 注入脚本化Bean的属性 3.6.4 刷新脚本化Bean 3.6.5 编写内嵌的脚本化Bean 3.7 小结 第4章 通知Bean 4.1 AOP简介 4.1.1 定义AOP术语 4.1.2 Spring对AOP的支持 4.2 创建典型的Spring切面 4.2.1 创建通知 4.2.2 定义切点和通知者 4.2.3 使用ProxyFactoryBean 4.3 自动代理 4.3.1 为Spring切面创建自动代理 4.3.2 自动代理@AspectJ切面 4.4 定义纯粹的POJO切面 4.5 注入AspectJ切面 4.6 小结 第二部分 企业Spring 第5章 使用数据库 5.1 Spring的数据访问哲学 5.1.1 了解Spring数据访问的异常体系 5.1.2 数据访问的模板化 5.1.3 使用DAO支持类 5.2 配置数据源 5.2.1 使用JNDI数据源 5.2.2 使用数据源连接池 5.2.3 基于JDBC驱动的数据源 5.3 在Spring里使用JDBC 5.3.1 处理失控的JDBC代码 5.3.2 使用JDBC模板 5.3.3 使用Spring对JDBC的DAO支持类 5.4 在Spring里集成Hibernate 5.4.1 选择Hibernate的本 5.4.2 使用Hibernate模板 5.4.3 建立基于Hibernate的DAO 5.4.4 使用Hibernate 3上下文会话 5.5 Spring和Java持久API 5.5.1 使用JPA模板 5.5.2 创建一个实体管理器工厂 5.5.3 建立使用JPA的DAO 5.6 Spring和iBATIS 5.6.1 配置iBATIS客户模板 5.6.2 建立基于iBATIS的DAO 5.7 缓存 5.7.1 配置缓存方案 5.7.2 缓存的代理Bean 5.7.3 注解驱动的缓存 5.8 小结 第6章 事务管理 6.1 理解事务 6.1.1 仅用4个词解释事务 6.1.2 理解Spring对事务管理的支持 6.2 选择事务管理器 6.2.1 JDBC事务 6.2.2 Hibernate事务 6.2.3 JPA事务 6.2.4 JDO事务 6.2.5 JTA事务 6.3 在Spring中编写事务 6.4 声明式事务 6.4.1 定义事务参数 6.4.2 代理事务 6.4.3 在Spring 2.0里声明事务 6.4.4 定义注释驱动事务 6.5 小结 第7章 保护Spring 7.1 Spring Security介绍 7.2 验证用户身份 7.2.1 配置Provider Manager 7.2.2 根据数据库验证身份 7.2.3 根据LDAP仓库进行身份验证 7.3 控制访问 7.3.1 访问决策投票 7.3.2 决定如何投票 7.3.3 处理投票弃权 7.4 保护Web应用程序 7.4.1 代理Spring Security的过滤器 7.4.2 处理安全上下文 7.4.3 提示用户登录 7.4.4 处理安全例外 7.4.5 强制Web安全性 7.4.6 确保一个安全的通道 7.5 视图层安全 7.5.1 有条件地渲染内容 7.5.2 显示用户身份验证信息 7.6 保护方法调用 7.6.1 创建一个安全切面 7.6.2 使用元数据保护方法 7.7 小结 第8章 Spring和基于POJO的远程服务 8.1 Spring远程调用概览 8.2 与RMI一起工作 8.2.1 连接RMI服务 8.2.2 输出RMI服务 8.3 使用Hessian和Burlap的远程调用 8.3.1 访问Hessian/Burlap服务 8.3.2 用Hessian或Burlap公开Bean的功能 8.4 使用HTTP invoker 8.4.1 通过HTTP访问服务 8.4.2 把Bean作为HTTP服务公开 8.5 Spring和Web服务 8.5.1 使用XFire将Bean输出为Web服务 8.5.2 使用JSR-181注释声明Web服务 8.5.3 消费Web服务 8.5.4 使用XFire客户端代理Web服务 8.6 小结 第9章 在Spring中建立契约优先Web服务 9.1 介绍Spring-WS 9.2 定义契约(首先!) 9.3 使用服务端点处理消息 9.3.1 建立基于JDOM消息的端点 9.3.2 序列化消息载荷 9.4 合并在一起 9.4.1 Spring-WS:全景视图 9.4.2 将消息映射到端点 9.4.3 置入服务端点 9.4.4 配置消息序列化器 9.4.5 处理端点异常 9.4.6 提供WSDL文件 9.4.7 部署服务 9.5 消费Spring-WS Web服务 9.5.1 使用Web服务模板 9.5.2 使用Web服务的网关支持 9.6 小结 第10章 Spring消息 10.1 JMS简介 10.1.1 构建JMS 10.1.2 介绍JMS的优点 10.1.3 在Spring中安装ActiveMQ 10.2 协同使用JMS和Spring 10.2.1 处理冗长失控的JMS代码 10.2.2 使用JMS模板 10.2.3 转换消息 10.2.4 将Spring的网关支持类应用于JMS 10.3 创建消息驱动POJO 10.3.1 创建消息监听器 10.3.2 编写纯POJO MDP 10.4 使用基于消息的RPC 10.4.1 引入Lingo 10.4.2 输出服务 10.4.3 代理JMS 10.5 小结 第11章 Spring和EJB 11.1 在Spring中置入EJB 11.1.1 代理会话Bean(EJB 2.x) 11.1.2 将EJB置入Spring Bean 11.2 开发Spring驱动的EJB(EJB 2.x) 11.3 Spring和EJB3 11.3.1 引入Pitchfork 11.3.2 从Pitchfork起步 11.3.3 通过注释注入资源 11.3.4 使用注释声明拦截器 11.4 小结 第12章 访问企业服务 12.1 从JNDI中获取对象 12.1.1 使用传统的JNDI 12.1.2 注入JNDI对象 12.1.3 在Spring 2中注入JNDI对象 12.2 发送电子邮件 12.2.1 配置邮件发送器 12.2.2 构建电子邮件 12.3 调度任务 12.3.1 使用Java Timer调度任务 12.3.2 使用Quartz调度器 12.3.3 按调度计划调用方法 12.4 使用JMX管理Spring Bean 12.4.1 将Spring Bean输出为MBean 12.4.2 远程访问MBean 12.4.3 处理通知 12.5 小结 第三部分 Spring客户端 第13章 处理Web请求 13.1 开始Spring MVC之旅 13.1.1 请求生命中的一天 13.1.2 配置DispatcherServlet 13.1.3 Spring MVC概述 13.2 将请求映射到控制器 13.2.1 使用SimpleUrlHandler Mapping 13.2.2 使用ControllerClassName HandlerMapping 13.2.3 使用元数据映射控制器 13.2.4 使用多映射处理器 13.3 用控制器处理请求 13.3.1 处理命令 13.3.2 处理表单提交 13.3.3 用向导处理复杂表单 13.3.4 使用一次性控制器 13.4 处理异常 13.5 小结 第14章 渲染Web视图 14.1 视图解析 14.1.1 使用模板视图 14.1.2 解析视图Bean 14.1.3 选择视图解析器 14.2 使用Spring模板 14.2.1 绑定表单数据 14.2.2 渲染被存储在外部的文本 14.2.3 显示错误 14.3 使用Tile设计页面布局 14.3.1 Tile视图 14.3.2 Tile控制器 14.4 使用JSP的替代技术 14.4.1 使用Velocity模板 14.4.2 使用FreeMarker 14.5 产生非HTML输出 14.5.1 产生Excel工作表 14.5.2 产生PDF文档 14.5.3 开发自定义视图 14.6 小结 第15章 使用Spring Web Flow 15.1 开始Spring Web Flow之旅 15.1.1 安装Spring Web Flow 15.1.2 配置流程执行程序 15.1.3 登记流程定义 15.2 布置流程的基础 15.2.1 流程变量 15.2.2 start和end状态 15.2.3 搜集顾客信息 15.2.4 绑定比萨饼订单 15.2.5 完成订单 15.2.6 几个结束工作 15.3 高级Web流程技术 15.3.1 使用decision状态 15.3.2 提炼子流程并使用子状态 15.4 集成Spring Web Flow与其他框架 15.4.1 Jakarts Struts 15.4.2 JavaServer Face 15.5 小结 第16章 集成其他Web框架 16.1 协同使用Spring和Struts 16.1.1 向Struts注册Spring插件 16.1.2 编写知晓Spring的Struts动作 16.1.3 委托Spring配置的动作 16.1.4 关于Struts 2 16.2 协同使用Spring和WebWork 2/Struts 2 16.3 集成Spring和Tapestry 16.3.1 集成Spring和Tapestry 3 16.3.2 集成Spring和Tapestry 4 16.4 协同使用Spring和JSF 16.4.1 解析JSF管理的属性 16.4.2 解析Spring Bean 16.4.3 在JSF页面中使用Spring Bean 16.4.4 在JSF中暴露应用程序环境 16.5 Spring中带有DWR的支持Ajax的应用程序 16.5.1 直接Web远程控制 16.5.2 访问Spring管理的Bean DWR 16.6 小结 附录A 装配Spring A.1 下载Spring A.1.1 研究Spring发布 A.1.2 构建自己的类路径 A.2 把Spring添加为一个Maven 2依赖项 A.3 Spring与Ant A.4 Spring与Log4j 附录B 用(和不用)Spring进行测试 B.1 测试简介 B.1.1 理解不同类型的测试 B.1.2 使用JUnit B.1.3 Spring在测试中的角色 B.2 单元测试Spring MVC控制器 B.2.1 模拟对象 B.2.2 断言ModelAndView的内容 B.3 使用Spring进行综合测试 B.3.1 测试装配后的对象 B.3.2 综合测试事务处理对象 B.3.3 测试数据库 B.3.4 使用Gienah Testing在JUnit 4中进行测试 B.4 小结
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值