2009年5月31日星期日

使用 Spring 2.5 TestContext 测试框架

使用 Spring 2.5 TestContext 测试框架

级别: 初级

陈 雄华 (quickselect@163.com), 技术总监, 宝宝淘网络科技有限公司

2008 年 3 月 28 日

Spring 2.5 TestContext 测试框架用于测试基于 Spring 的程序,TestContext 测试框架和低版本 Spring 测试框架没有任何关系,是一个全新的基于注解的测试框架,为 Spring 推荐使用该测试框架。

概述

Spring 2.5 相比于 Spring 2.0 所新增的最重要的功能可以归结为以下 3 点:

  • 基于注解的 IoC 功能;
  • 基于注解驱动的 Spring MVC 功能;
  • 基于注解的 TestContext 测试框架。

Spring 推荐开发者使用新的基于注解的 TestContext 测试框架,本文我们将对此进行详细的讲述。

低版本的 Spring 所提供的 Spring 测试框架构在 JUnit 3.8 基础上扩展而来,它提供了若干个测试基类。而 Spring 2.5 所新增的基于注解的 TestContext 测试框架和低版本的测试框架没有任何关系。它采用全新的注解技术可以让 POJO 成为 Spring 的测试用例,除了拥有旧测试框架所有功能外,TestContext 还添加了一些新的功能,TestContext 可以运行在 JUnit 3.8、JUnit 4.4、TestNG 等测试框架下。





回页首


直接使用 JUnit 测试 Spring 程序存在的不足

在拙作《精通 Spring 2.x — 企业应用开发详解》一书中,笔者曾经指出如果直接使用 JUnit 测试基于 Spring 的程序,将存在以下 4 点明显的不足:

  • 导致 Spring 容器多次初始化问题:根据 JUnit 测试用例的调用流程,每执行一个测试方法都会重新创建一个测试用例实例并调用其 setUp() 方法。由于在一般情况下,我们都在 setUp() 方法中初始化 Spring 容器,这意味着测试用例中有多少个测试方法,Spring 容器就会被重复初始化多少次。
  • 需要使用硬编码方式手工获取 Bean:在测试用例中,我们需要通过 ApplicationContext.getBean() 的方法从 Spirng 容器中获取需要测试的目标 Bean,并且还要进行造型操作。
  • 数据库现场容易遭受破坏:测试方法可能会对数据库记录进行更改操作,破坏数据库现场。虽然是针对开发数据库进行测试工作的,但如果数据操作的影响是持久的,将会形成积累效应并影响到测试用例的再次执行。举个例子,假设在某个测试方法中往数据库插入一条 ID 为 1 的 t_user 记录,第一次运行不会有问题,第二次运行时,就会因为主键冲突而导致测试用例执行失败。所以测试用例应该既能够完成测试固件业务功能正确性的检查,又能够容易地在测试完成后恢复现场,做到踏雪无迹、雁过无痕。
  • 不容易在同一事务下访问数据库以检验业务操作的正确性:当测试固件操作数据库时,为了检测数据操作的正确性,需要通过一种方便途径在测试方法相同的事务环境下访问数据库,以检查测试固件数据操作的执行效果。如果直接使用 JUnit 进行测试,我们很难完成这项操作。

Spring 测试框架是专门为测试基于 Spring 框架应用程序而设计的,它能够让测试用例非常方便地和 Spring 框架结合起来,以上所有问题都将迎刃而解。





回页首


一个需要测试的 Spring 服务类

在具体使用 TextContext 测试框架之前,我们先来认识一下需要测试的 UserService 服务类。UserService 服务类中拥有一个处理用户登录的服务方法,其代码如下所示:


清单1. UserService.java 需要测试的服务类
	
package com.baobaotao.service;

import com.baobaotao.domain.LoginLog;
import com.baobaotao.domain.User;
import com.baobaotao.dao.UserDao;
import com.baobaotao.dao.LoginLogDao;

public class UserService{

private UserDao userDao;
private LoginLogDao loginLogDao;

public void handleUserLogin(User user) {
user.setCredits( 5 + user.getCredits());
LoginLog loginLog = new LoginLog();
loginLog.setUserId(user.getUserId());
loginLog.setIp(user.getLastIp());
loginLog.setLoginTime(user.getLastVisit());
userDao.updateLoginInfo(user);
loginLogDao.insertLoginLog(loginLog);
}
//省略get/setter方法
}

UserService 需要调用 DAO 层的 UserDao 和 LoginLogDao 以及 User 和 LoginLog 这两个 PO 完成业务逻辑,User 和 LoginLog分别对应 t_user 和 t_login_log 这两张数据库表。

在用户登录成功后调用 UserService 中的 handleUserLogin() 方法执行用户登录成功后的业务逻辑:

  1. 登录用户添加 5 个积分(t_user.credits);
  2. 登录用户的最后访问时间(t_user.last_visit)和 IP(t_user.last_ip)更新为当前值;
  3. 在日志表中(t_login_log)中为用户添加一条登录日志。

这是一个需要访问数据库并存在数据更改操作的业务方法,它工作在事务环境下。下面是装配该服务类 Bean 的 Spring 配置文件:


清单2. applicationContext.xml:Spring 配置文件,放在类路径下
	
<?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:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!-- 配置数据源 -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost/sampledb"
p:username="root"
p:password="1234"/>

<!-- 配置Jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource"/>

<!-- 配置dao -->
<bean id="loginLogDao"class="com.baobaotao.dao.LoginLogDao"
p:jdbcTemplate-ref="jdbcTemplate"/>
<bean id="userDao" class="com.baobaotao.dao.UserDao"
p:jdbcTemplate-ref="jdbcTemplate"/>

<!-- 事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>

<bean id="userService" class="com.baobaotao.service.UserService"
p:userDao-ref="userDao" p:loginLogDao-ref="loginLogDao"/>

<!-- 使用aop/tx命名空间配置事务管理,这里对service包下的服务类方法提供事务-->
<aop:config>
<aop:pointcut id="jdbcServiceMethod"
expression= "within(com.baobaotao.service..*)" />
<aop:advisor pointcut-ref="jdbcServiceMethod" advice-ref="jdbcTxAdvice" />
</aop:config>
<tx:advice id="jdbcTxAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
</beans>

UserService 所关联的 DAO 类和 PO 类都比较简单,请参看本文附件的程序代码。在着手测试 UserSerivce 之前,需要将创建数据库表,你可以在附件的 schema 目录下找到相应的 SQL 脚本文件。





回页首


编写 UserService 的测试用例

下面我们为 UserService 编写一个简单的测试用例类,此时的目标是让这个基于 TestContext 测试框架的测试类运行起来,我们将在后面逐步完善这个测试用例。


清单3.TestUserService.java: 基于注解的测试用例
	
package com.baobaotao.service;

import org.springframework.test.context.junit4.
AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.junit.Test;
import com.baobaotao.domain.User;

import java.util.Date;

@ContextConfiguration //①
public class TestUserService extends
AbstractTransactionalJUnit4SpringContextTests {

@Autowired //②
private UserService userService;

@Test //③
public void handleUserLogin(){
User user = new User();
user.setUserId(1);
user.setLastIp("127.0.0.1");
Date now = new Date();
user.setLastVisit(now.getTime());
userService.handleUserLogin(user);
}
}

这里,我们让 TestUserService 直接继承于 Spring 所提供的 AbstractTransactionalJUnit4SpringContextTests 的抽象测试类,稍后本文将对这个抽象测试类进行剖析,这里你仅须知道该抽象测试类的作用是让 TestContext 测试框架可以在 JUnit 4.4 测试框架基础上运行起来就可以了。

在 ① 处,标注了一个类级的 @ContextConfiguration 注解,这里 Spring 将按 TestContext 契约查找 classpath:/com/baobaotao/service/TestUserService-context.xml 的 Spring 配置文件,并使用该配置文件启动 Spring 容器。@ContextConfiguration 注解有以下两个常用的属性:

  • locations:可以通过该属性手工指定 Spring 配置文件所在的位置,可以指定一个或多个 Spring 配置文件。如下所示:

    @ContextConfiguration(locations={“xx/yy/beans1.xml”,” xx/yy/beans2.xml”})

  • inheritLocations:是否要继承父测试用例类中的 Spring 配置文件,默认为 true。如下面的例子:

    @ContextConfiguration(locations={"base-context.xml"})
    public class BaseTest {
    // ...
    }
    @ContextConfiguration(locations={"extended-context.xml"})
    public class ExtendedTest extends BaseTest {
    // ...
    }

如果 inheritLocations 设置为 false,则 ExtendedTest 仅会使用 extended-context.xml 配置文件,否则将使用 base-context.xml 和 extended-context.xml 这两个配置文件。

② 处的 @Autowired 注解让 Spring 容器自动注入 UserService 类型的 Bean。而在 ③ 处标注的 @Test 注解则让 handleUserLogin() 方法成为一个 JUnit 4.4 标准的测试方法, @Test 是 JUnit 4.4 所定义的注解。

在运行 TestUserService 测试类之前,让我们先看一下 TestUserService-context.xml 配置文件的内容:


清单 4.TestUserService 所引用的 Spring 配置文件
	
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<!-- ① 引入清单1定义的Spring配置文件 -->
<import resource="classpath:/applicationContext.xml"/>

</beans>

在 ① 处引入了清单 1 中定义的 Spring 配置文件,这样我们就可以将其中定义的 UserService Bean 作为测试固件注入到 TestUserService 中了。

在你的 IDE 中(Eclipse、JBuilder、Idea 等),将 JUnit 4.4 类包引入到项目工程中后,在 TestUserService 类中点击右键运行该测试类,将发现 TestUserService 已经可以成功运行了,如 图 1 所示:


图 1. 在 Eclipse 6.0 中运行 TestUserService
图 1. 在 Eclipse 6.0 中运行 TestUserService 

TestUserService 可以正确运行,说明其 userService 这个测试固件已经享受了 Spring 自动注入的功能。在运行该测试用例后,到数据库中查看 t_user 表和 t_login_log 表,你会发现表数据和测试前是一样的!这说明虽然我们在清单 3 的 handleUserLogin() 测试方法中执行了 userService.handleUserLogin(user) 的操作,但它并没有对数据库现场造成破坏:这是因为 Spring 的在测试方法返回前进行了事务回滚操作。

虽然 TestUserService.handleUserLogin() 测试方法已经可以成功运行,但是它在测试功能上是不完善的,读者朋友可以已经发现了它存在以下两个问题:

  • 我们仅仅执行了 UserService#handleUserLogin(user) 方法,但验证该方法执行结果的正确性。
  • 在测试方法中直接使用 ID 为 1 的 User 对象进行测试,这相当于要求在数据库 t_user 表必须已经存在 ID 为 1 的记录,如果 t_user 中不存在这条记录,将导致测试方法执行失败。




回页首


准备测试数据并检测运行结果

在这节里,我们将着手解决上面所提出的两个问题,在测试用例中准备测试数据并到数据库中检测业务执行结果的正确性。

准备测试数据

相比于在测试方法中直接访问预定的数据记录,在测试方法执行前通过程序准备一些测试数据,然后在此基础上运行测试方法是比较好的策略,因为后者不需要对数据库的状态做假设。在 TestContext 中,你可以通过使用 JUnit 4.4 的 @Before 注解达到这个目的,请看下面的代码:


清单5. 为测试方法准备数据
	
package com.baobaotao.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;

import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.
AbstractTransactionalJUnit4SpringContextTests;

import com.baobaotao.dao.UserDao;
import com.baobaotao.domain.User;

@ContextConfiguration
public class TestUserService
extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
private UserService userService;

@Autowired
private UserDao userDao;

private int userId;

@Before //① 准备测试数据
public void prepareTestData() {
final String sql = "insert into t_user(user_name,password) values('tom','1234')";
simpleJdbcTemplate.update(sql);
KeyHolder keyHolder = new GeneratedKeyHolder();
simpleJdbcTemplate.getJdbcOperations().update(
new PreparedStatementCreator() {
public PreparedStatement createPreparedStatement(Connection conn)
throws SQLException {
PreparedStatement ps = conn.prepareStatement(sql);
return ps;
}
}, keyHolder);
userId = keyHolder.getKey().intValue();//①-1 记录测试数据的id
}

@Test
public void handleUserLogin(){
User user = userDao.getUserById(userId); //② 获取测试数据
user.setLastIp("127.0.0.1");
Date now = new Date();
user.setLastVisit(now.getTime());
userService.handleUserLogin(user);
}
}

JUnit 4.4 允许通过注解指定某些方法在测试方法执行前后进行调用,即是 @Before 和 @After 注解。在 Spring TestContext 中,标注 @Before 和 @After 的方法会在测试用例中每个测试方法运行前后执行,并和测试方法运行于同一个事务中。在 清单 5 中 ① 处,我们给 prepareTestData() 标注上了 @Before 注解,在该方法中准备一些测试数据,以供 TestUserService 中所有测试方法使用(这里仅有一个 handleUserLogin() 测试方法)。由于测试方法运行后,整个事务会被回滚,在 prepareTestData() 中插入的测试数据也不会持久化到数据库中,因此我们无须手工删除这条记录。

标注 @Before 或 @After 注解的方法和测试方法运行在同一个事务中,但有时我们希望在测试方法的事务开始之前或完成之后执行某些方法以便获取数据库现场的一些情况。这时,可以使用 Spring TestContext 的 @BeforeTransaction 和 @AfterTransaction 注解来达到目录(这两个注解位于 org.springframework.test.context.transaction 包中)。

虽然大多数业务方法都会访问数据库,但也并非所有需要测试的业务方法都需要和数据库打交道。而在默认情况下,继承于 AbstractTransactionalJUnit4SpringContextTests 测试用例的所有测试方法都将工作于事务环境下,你可以显式地通过 @NotTransactional 注解,让测试方法不工作于事务环境下。

prepareTestData() 方法中使用到了 simpleJdbcTemplate 对象访问操作数据库,该对象在 AbstractTransactionalJUnit4SpringContextTests 抽象类中定义,只要 Spring 容器有配置数据源,simpleJdbcTemplate 就会被自动创建。同时该抽象类中还拥有一个 Spring 容器引用:applicationContext,你可以借助该成员变量访问 Spring 容器,执行获取 Bean,发布事件等操作。

此外,AbstractTransactionalJUnit4SpringContextTests 还提供了若干个访问数据库的便捷方法,说明如下:

  • protected int countRowsInTable(String tableName) :计算数据表的记录数。
  • protected int deleteFromTables(String... names):删除表中的记录,可以指定多张表。
  • protected void executeSqlScript(String sqlResourcePath, boolean continueOnError):执行 SQL 脚本文件,在脚本文件中,其格式必须一个 SQL 语句一行。

在测试方法 handleUserLogin() 的 ② 处,我们通过 userDao 获取 prepareTestData() 添加的测试数据,测试方法在测试数据的基础上执行业务逻辑。使用这种测试方式后,在任何情况下运行 TestUserService 都不会发生业务逻辑之外的问题。

检验业务逻辑的正确性

到目前为此,TestUserService 的 handleUserLogin() 测试方法仅是简单地执行 UserService#handleUserLogin() 业务方法,但并没有在业务方法执行后检查执行结果的正确性,因此这个测试是不到位的。也就是说,我们必须访问数据库以检查业务方法对数据更改是否成功:这包括积分(credits)、最后登录时间(last_visit)、最后登录 IP(last_ip)以及登录日志表中的登录日志记录(t_login_log)。下面,我们补充这项重要的检查数据正确性的工作:


清单5. 检验业务方法执行结果的正确性
	
@Test
public void handleUserLogin(){
User user = userDao.getUserById(userId);
user.setLastIp("127.0.0.1");
Date now = new Date();
user.setLastVisit(now.getTime());
userService.handleUserLogin(user);

//------------------以下为业务执行结果检查的代码---------------------
User newUser = userDao.getUserById(userId);
Assert.assertEquals(5, newUser.getCredits()); //①检测积分
//①检测最后登录时间和IP
Assert.assertEquals(now.getTime(), newUser.getLastVisit());
Assert.assertEquals("127.0.0.1",newUser.getLastIp());

// ③检测登录记录
String sql = "select count(1) from t_login_log where user_id=? "+
“ and login_datetime=? and ip=?";
int logCount =simpleJdbcTemplate.queryForInt(sql, user.getUserId(),
user.getLastVisit(),user.getLastIp());
Assert.assertEquals(1, logCount);
}

在业务方法执行后,我们查询数据库中相应记录以检查是否和期望的效果一致,如 ① 和 ② 所示。在 ③ 处,我们使用 SimpleJdbcTemplate 查询 t_login_log,以检查该表中是否已经添加了一条用户登录日志。

注意:由于我们的 DAO 层采用 Spring JDBC 框架,它没有采用服务层缓存技术,所以可以使用 DAO 类返回数据库中的数据。如果采用 Hibernate 等 ORM 框架,由于它们采用了服务层缓存的技术,为了获取数据库中的相应数据,需要在业务方法执行后调用 HibernateTemplate.flush() 方法,将缓存中的对象同步到数据库中,这时才可以通过 SimpleJdbcTemplate 在数据库中访问业务方法的执行情况。





回页首


Spring TestContext 测试框架体系结构

在前面,我们直接通过扩展 AbstractTransactionalJUnit4SpringContextTests 编写测试用例,在了解了编写基于 TestContext 测试框架的测试用例后,现在是了解 TestContext 测试框架本身的时候了。

TestContext 核心类、支持类以及注解类

TestContext 测试框架的核心由 org.springframework.test.context 包中三个类组成,分别是 TestContext 和 TestContextManager 类以及 TestExecutionListener 接口。其类图如下 图 2 所示:


图 2. Spring TestContext 测试框架核心类
图 2. Spring TestContext 测试框架核心类 
  • TestContext:它封装了运行测试用例的上下文;
  • TestContextManager:它是进入 Spring TestContext 框架的程序主入口,它管理着一个 TestContext 实例,并在适合的执行点上向所有注册在 TestContextManager 中的 TestExecutionListener 监听器发布事件:比如测试用例实例的准备,测试方法执行前后方法的调用等。
  • TestExecutionListener:该接口负责响应 TestContextManager 发布的事件。

Spring TestContext 允许在测试用例类中通过 @TestExecutionListeners 注解向 TestContextManager 注册多个监听器,如下所示:

@TestExecutionListeners( { 
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class })
public class TestXxxService{

}

Spring 提供了几个 TestExecutionListener 接口实现类,分别说明如下:

  • DependencyInjectionTestExecutionListener:该监听器提供了自动注入的功能,它负责解析测试用例中 @Autowried 注解并完成自动注入;
  • DirtiesContextTestExecutionListener:一般情况下测试方法并不会对 Spring 容器上下文造成破坏(改变 Bean 的配置信息等),如果某个测试方法确实会破坏 Spring 容器上下文,你可以显式地为该测试方法添加 @DirtiesContext 注解,以便 Spring TestContext 在测试该方法后刷新 Spring 容器的上下文,而 DirtiesContextTestExecutionListener 监听器的工作就是解析 @DirtiesContext 注解;
  • TransactionalTestExecutionListener:它负责解析 @Transaction、@NotTransactional 以及 @Rollback 等事务注解的注解。@Transaction 注解让测试方法工作于事务环境中,不过在测试方法返回前事务会被回滚。你可以使用 @Rollback(false) 让测试方法返回前提交事务。而 @NotTransactional 注解则让测试方法不工作于事务环境中。此外,你还可以使用类或方法级别的 @TransactionConfiguration 注解改变事务管理策略,如下所示:
    @TransactionConfiguration(transactionManager="txMgr", defaultRollback=false)
    @Transactional
    public class TestUserService {

    }

我们知道在 JUnit 4.4 中可以通过 @RunWith 注解指定测试用例的运行器,Spring TestContext 框架提供了扩展于 org.junit.internal.runners.JUnit4ClassRunner 的 SpringJUnit4ClassRunner 运行器,它负责总装 Spring TestContext 测试框架并将其统一到 JUnit 4.4 框架中。

TestContext 所提供的抽象测试用例

Spring TestContext 为基于 JUnit 4.4 测试框架提供了两个抽象测试用例类,分别是 AbstractJUnit4SpringContextTests 和 AbstractTransactionalJUnit4SpringContextTests,而后者扩展于前者。让我们来看一下这两个抽象测试用例类的骨架代码:

@RunWith(SpringJUnit4ClassRunner.class) //① 指定测试用例运行器
@TestExecutionListeners( //② 注册了两个TestExecutionListener监听器
{ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class })
public class AbstractJUnit4SpringContextTests implements ApplicationContextAware {

}

① 处将 SpringJUnit4ClassRunner 指定为测试用例运行器,它负责无缝地将 TestContext 测试框架移花接木到 JUnit 4.4 测试框架中,它是 Spring TestContext 可以运行起来的根本所在。② 处通过 @TestExecutionListeners 注解向测试用例类中注册了两个 TestExecutionListener 监听器,这两个监听器分别负责对 @Autowired 和 @DirtiesContext 注解进行处理,为测试用例提供自动注入和重新刷新 Spring 容器上下文的功能。

AbstractTransactionalJUnit4SpringContextTests 扩展于 AbstractJUnit4SpringContextTests,提供了事务管理的支持,其骨架代码如下所示:

//① 注册测试用例事务管理的监听器
@TestExecutionListeners( { TransactionalTestExecutionListener.class })
@Transactional //② 使测试用例的所有方法都将工作于事务环境下
public class AbstractTransactionalJUnit4SpringContextTests
extends AbstractJUnit4SpringContextTests {

}

在 ① 处,AbstractTransactionalJUnit4SpringContextTests 向测试用例类中注册了 TransactionalTestExecutionListener 监听器,这样测试用例中的 @Transaction、@NotTransaction 以及 @Rollback 等注解就可以正确地工作起来了。注意,你不需要在 Spring 配置文件通过 <tx:annotation-driven /> 和 <context:annotation-config/> 为测试用例类启用注解事务驱动和注解自动注入,这个工作完全于 TestContext 自身来解决(通过注册 DependencyInjectionTestExecutionListener 和 TransactionalTestExecutionListener 监听器),毕竟测试用例类没有注册到 Spring 容器中,没有成为 Spring 的 Bean。





回页首


小结

我们通过对一个典型的涉及数据库访问操作的 UserService 服务类的测试,讲述了使用 Spring 2.5 TestContext 测试框架进行集成测试的各项问题,这包括测试固件的自动注入、事务自动回滚、通过 SimpleJdbcTemplate 直接访问数据库以及测试数据准备等问题。

在通过一个实际例子的学习后,我们对如何使用 TestContext 测试框架有了一个具体的认识,在此基础上我们对 Spring TestContext 测试框架体系结构进行了分析,然后剖析了 Spring 为 TestContext 嫁接到 JUnit 4.4 测试框架上所提供的两个抽象测试用例类。

Spring 的 TestContext 测试框架不但可以整合到 JUnit 4.4 测试框架上,而且还可以整合到 JUnit 3.8 以及 TestNG 等测试框架上。目前已经提供了对 JUnit 3.8 以及 TestNG 的支持,你可以分别在 org.springframework.test.context.junit38 和 org.springframework.test.context.testng 包下找到整合的帮助类。



参考资料

学习

获得产品和技术


关于作者

陈雄华照片

陈雄华,2002 年毕业于厦门大学计算机与信息工程学院,获硕士学位。是宝宝淘科技有限公司的创始人之一(http://www.baobaotao.com),这是一个服务于全国母婴用户的综合性网站,作者负责网站整体框架设计以及核心代码开发的工作。技术开发之余,常将经验所得行诸于文字,作者是国内多个著名技术网站的专栏作者,在各大技术网站、报刊杂志发表过数十篇技术文章,广受读者好评。于 2005 年出版《精通 JBuilder 2005》,于2007年出版《精通 Spring 2.x--企业应用开发详解》,其新作《EXT 2.x开发详解――AJAX和Web页面布局王者至尊》即将出版。


2009年5月13日星期三

eclipse 插件安装

eclipse 插件安装

Spring IDE for Eclipse在线安装网址:





http://springide.org/updatesite/

Ibator Automatic Eclipse Install

If you've already installed a
prior version of Ibator, simply run the Eclipse Update tool and the new
version will be found automatically.

If you've not previously installed Ibator, use the built in Eclipse install support by following these steps:

  1. Take the "Help>Software Updates..." Menu Option
  2. Select the "Available Software" Tab
  3. Press the "Add Site" button
  4. Enter the following information: Location:http://ibatis.apache.org/tools/ibator
  5. Press OK
  6. Check the box next to "Apache iBATIS Ibator Feature"
  7. Press the "Install" button
  8. Follow the remainder of the install wizard

Ibator Manual Eclipse Install

The automatic install is much
preferred, but you can also install Ibator manually if you desire. To
install manually, download the file IbatorForEclipse1.2.1.zip and unzip the file to some convenient location. After unzipping the update site archive, follow these steps in Eclipse:

  1. Take the "Help>Software Updates..." Menu Option
  2. Select the "Available Software" Tab
  3. Press the "Add Site" button
  4. Press the "Local" button
  5. Navigate to the location where you unzipped the site archive
  6. Press OK
  7. Check the box next to "Apache iBATIS Ibator Feature"
  8. Press the "Install" button
  9. Follow the remainder of the install wizard

2009年5月11日星期一

电饭煲蛋糕

电饭煲蛋糕

2005-12-12

材料:低筋面粉60g,糖60g,泡打粉约4g,牛奶30g,油30g,鸡蛋3个,醋几滴(忘了拍了,后来加上的)
   我没有秤和量杯,就用了普通的一次性杯子,125ml的作为测量工具,大家可以从图中看到大约的量,面粉3/4杯多,糖
  
  半杯多一点,泡打粉就是在面粉上面的小勺子里,牛奶1/4杯,油比重小于牛奶,所以大约有1/3杯
  
  8月2日修改:真是很对不起大家,我把一次性杯子的容量搞错了,应当是205ml的,但是我当时的用量的确是这个样子的,做出来感觉稍微油了一点点。

  

点击在新窗口中查看该图片












作者:白梨儿
  回复日期:2005-12-12 21:13:00




  蛋白和蛋黄分开,放到两个较大的无油无水的容器中,我用的是两个吃面的大碗,没有分蛋器,可以鸡蛋从中间打开,左右各
  
  倒手一次就行了,一定要小心,我今天就不小心弄破了一个蛋黄,结果虽然用勺子把它从蛋清中捞出来了,但是仅剩的一点残
  
  余还是让我今天打蛋比上次多费了不少力气
  看看这个破碎的蛋黄,旁边的碗里是蛋清,不太清楚
  

点击在新窗口中查看该图片





作者:白梨儿
  回复日期:2005-12-12 21:14:00




  把蛋黄,油,牛奶,1/3的糖搅拌均匀,有的说要打发,我也不清楚什么样就是打发了,就直接搅匀完事(左图)
  面粉要筛,但是我没有筛子,也就直接放进去了,分三次加入蛋黄中,轻快的搅匀(见右图),很多方子(比如狐狸的)中说
  
  不能搅拌,但是因为面粉没筛,和到一起后会有面疙瘩,我就用打蛋器搅了,不过也不是划圈的,是上下搅得
  

点击在新窗口中查看该图片





作者:白梨儿
  回复日期:2005-12-12 21:15:00




  接下来,最艰难的过程,打蛋清,因为今天犯了严重错误,打蛋时间比上次增加了一倍,上次用了20分,这次一边打一边看阿
  
  拉蕾,看了两集才打好,不过也是因为看动画片不专心的原因
  注意,打蛋清时候的打蛋器要干净,上一步如果用过,一定要洗干净吸干水分,我偷懒,是打完蛋清才和面粉的,呵呵,大家
  
  不要学我偷懒阿
  蛋清加醋达到起大泡的时候(左上),加剩余糖的1/3,打,再加1/3,打,再加1/3,打,到最后成了硬性发泡就可以了,初学者可
  
  能不知道什么是硬性发泡,我第一次也是很疑惑呢,所以就拍了一个软性发泡的(右上)和一个硬性发泡的(左下)给大家作
  
  个比较,就是用打蛋器挑一个尖角,角直立的才是硬性发泡
  

点击在新窗口中查看该图片





作者:白梨儿
  回复日期:2005-12-12 21:48:00




  然后把打好的的蛋清分三次倒入蛋黄面糊液中,轻快的搅拌均匀,就是这个样子了,
  做这一步之前可以先预热电饭锅了,并且在上面抹一点油,因为上次没抹油,最后有一点皮沾到锅上了,我这次抹了黄油,事
  
  实证明黄油不管用,下面就可以看到了
  把上一步做好的东东倒入电饭锅,按下加热档,等跳上去之后过半小时再开盖,这时候一定要有耐心,不能着急打开看,不然
  
  会塌下去成了一张大饼的
  当然不同的电饭锅用的时间不一样,我的电饭锅用的是慢煮档,平时要使做两个人吃的饭要用一个小时呢,大家可以参照不同
  
  情况自己试试看。
  
  最后,左上,在电饭锅里的样子,右上,倒扣出来,我的电饭锅是圆底的,美中不足,这时大家可以看到此次的金黄表皮全被锅沾住了,不可理解阿,希望大家
  
  以我为戒,左下,切开的(普通刀切的,不好),右下,掰开的细节

点击在新窗口中查看该图片



 步骤:
  原料:2个大碗   
   鸡蛋4个    
   蛋糕粉(偶用的是两小包)   
  
白砂糖(越细越好,用量差不多是汤勺6勺)   
   炼奶少许   
   黄油少许(因为没有黄油所以偶用的是橄榄油)   
  
纯牛奶4勺
  1、先将鸡蛋蛋白和蛋黄分开装到两个碗中。

  2、蛋黄里加入牛奶、少许橄榄油和两勺白糖,滴入3滴左右的白醋,放少许盐,打匀,并分几次放入蛋糕粉,搅成糊状。搅拌好后放入温水泡着备用。

  3、接下来就是最关键的也是最累人的活——打蛋清~~~。打蛋清时放入少许的盐,就好打多了,打蛋清的过程中要分三次加入白糖。接下来就是不停的打!打!打!直到把蛋清打成干性发泡,把筷子立在里面不倒就行啦。

  4、将打好的蛋清分三次倒入蛋黄面糊里,偶用电饭煲的塑料勺子从下到上搅拌均匀,不要搅拌,然后就成了偶需要的蛋糕糊大。

  5、在电饭煲里涂一层橄榄油,这样蛋糕成型时就比较容易脱膜。然后将蛋糕糊倒入电饭煲中,轻轻的敦几下就可以大。按电饭煲的煮饭档,约几分钟左右就跳到保温了,等过10分钟左右再按下煮饭档,几分钟后又跳到保温档,这时不能马上开盖,必须要等30分钟左右后再打开,这时你就可以看到漂亮的蛋糕啦.

2009年4月30日星期四

iBATIS的代码生成工具-iBATOR 试用

iBATIS的代码生成工具-iBATOR
发表时间:2008-06-24

前两天在javaeye上闲逛,无意间看到iBATIS也有代码生成的工具,这两天一直没抽着时间试试,今天利用15分钟时间试用了下,感觉还是不错的,很简单也很实用。

    iBATOR下载:http://ibatis.apache.org/ibator.html

它提供了多种格式的下载,大家有兴趣可以逐一下载研究,我用的是eclipse的插件。eclipse安装插件大家应该都明白了。呵呵

装完之后,新建一个Java
Project名为:Ibatis。工程建好好在此工程中新建配置文件:abatorConfig.xml。具体为:File-New-Abator For
iBATIS ConfigurationFile.
根据需要修改此文件。我的配置为:
<?xml version="1.0"
encoding="UTF-8" ?>
<!DOCTYPE abatorConfiguration PUBLIC "-//Apache
Software Foundation//DTD Abator for iBATIS Configuration 1.0//EN"
"http://ibatis.apache.org/dtd/abator-config_1_0.dtd" >

<abatorConfiguration >
  <abatorContext >
   
<jdbcConnection driverClass="org.gjt.mm.mysql.Driver"
connectionURL="jdbc:mysql://localhost:3306/fsc" userId="root"
password="password3401" >
      <classPathEntry
location="D:\mysql-connector-java-5.0.6-bin.jar" />
   
</jdbcConnection>
    <javaModelGenerator
targetPackage="test.model" targetProject="Ibatis\src" />
   
<sqlMapGenerator targetPackage="test.xml" targetProject="Ibatis\src" />

   
    <table schema="fsc" tableName="test" >
     
<generatedKey column="id" sqlStatement="MySQL" identity="true" />

      <columnOverride column="address" property="addr" />
   
</table>
  </abatorContext>
</abatorConfiguration>

到此,一切准备工作ok
下面生成model及各种配置文件。
在abatorConfig.xml文件上鼠标右键,“Generate
iBATIS Artifacts”即可。
这样会在你指定的路径下生成model文件。
唯一遗憾的是不支持annotiation。

总的来说感觉还是不错的。

官方文档:http://ibatis.apache.org/docs/tools/abator/
-------------------------------------------------------------------

Introduction to iBATOR


iBATOR is a code generator for iBATIS. iBATOR will introspect a database table (or many tables)
and will generate iBATIS artifacts that can be used to access the table(s). This
abates some of the initial nuisance of setting up objects and configuration
files to interact with database tables. iBATOR seeks
to make a major impact on the large percentage of database operations that are
simple CRUD (Create, Retrieve, Update, Delete). You will still need to hand code
SQL and objects for custom queries, or stored procedures.


iBATOR will generate:


  • SqlMap XML Files
  • Java Classes to match the primary key and fields of the table(s)
  • DAO Classes that use the above objects (optional)

iBATOR can run as a standalone JAR file, or as an
Ant task, or as an Eclipse plugin.


iBATOR is currently under development. The legacy
version (Abator) is still available. If you have suggestions for the future of
iBATOR, please feel free to send them to the Java
user's mailing list..


About the Name


iBATOR is an iBATIS stylized version of the English word "abator". Abator
means "one who abates a nuisance".


iBATOR
News


(April 14, 2008) Due to a trade registration dispute, Abator is renamed to
iBATOR. iBATOR is currently under development. The initial source code drop can
be checked out from SVN at
http://svn.apache.org/repos/asf/ibatis/trunk/java/tools/ibator/


(March 20, 2008) Updated Abator and the Eclipse plugin to version 1.1.0. This
is an extensive update that includes quite a few minor enhancements, two major
enhancements (two new methods can be generated), and a few bug fixes. See the
What's New? section of the online documentation for full details.


(August 20, 2006) Updated Abator and the Eclipse plugin to version 1.0.0.
This is an extensive update that includes many new features including the
ability to generate code for Java 5, generate different types of domain models,
and hugely improved "by example" methods. See the "What's New?" section of the
online documentation for full details.


iBATOR Development


iBATOR is currently under development. iBATOR will not be 100% compatible
with Abator, but will be very close. Known differences include:



Legacy
Abator Software Downloads and Documentation


Download the standalone JAR if you are using an IDE other than Eclipse. The
standalone JAR includes an Ant task to run Abator, or you can run Abator from
the command line of from Java code.



Documentation for the core functions of Abator is available online. This
documentation set is also included in the downloads, and is integrated into the
Eclipse help system if you are using the Eclipse plugin.


Documentation for the Eclipse specific features is integrated into the
Eclipse help system and is not available online.



Eclipse Plugin


When run as an Eclipse plugin, Abator will persist the generated Java classes
and SqlMap files in Eclipse projects. Abator can be run iteratively multiple
time as the database design matures - and any hand coded additions to generated
Java classes or SqlMap files will remain undisturbed.


Documentation for Abator is integrated into the Eclipse help system.


Requirements



Automatic Eclipse
Install


If you've already installed a prior version of Abator, simply run the Eclipse
Install/Update tool and the new version will be found automatically.


If you've not already installed Abator, then you can use the built in Eclipse
install support by following these steps:


  1. Take the "Help>Software Updates>Find and Install" Menu Option
  2. Select the "Search for new features to install" radio button, press "Next"
  3. Press the "New Remote Site" button
  4. Enter the following information: Name:Abator for Eclipse Update
    SiteURL:
    http://ibatis.apache.org/tools/abator
  5. Press OK
  6. Check the box next to "Abator for Eclipse Update Site"
  7. Follow the remainder of the install wizard

Manual Eclipse
Install


The automatic install is much preferred, but you can also install Abator
manually if you desire. To install manually, download the file
AbatorForEclipse1.1.0.zip and unzip the file to some convenient location. After
unzipping the update site archive, follow these steps in Eclipse:


  1. Take the "Help>Software Updates>Find and Install" Menu Option
  2. Select the "Search for new features to install" radio button, press "Next"
  3. Press the "New Local Site" button
  4. Navigate to the location where you unzipped the file.
  5. Press OK
  6. Follow the remainder of the install wizard


配置文件abatorConfig.xml示例


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE
abatorConfiguration PUBLIC "-//Apache Software Foundation//DTD Abator for iBATIS
Configuration 1.0//EN"
 
"http://ibatis.apache.org/dtd/abator-config_1_0.dtd">


<abatorConfiguration>
  <abatorContext>    <!-- TODO: Add
Database Connection Information -->
    <jdbcConnection
driverClass="oracle.jdbc.driver.OracleDriver"
       
connectionURL="jdbc:oracle:thin:@192.168.0.17:1521/fcg"
       
userId="fcg"
        password="fcg">
      <classPathEntry
location="E:\Tomcat6.0\lib\ojdbc14-10.2.0.1.0.jar" />
   
</jdbcConnection>


     <javaTypeResolver >
      <property name="forceBigDecimals"
value="false" />
    </javaTypeResolver>


    <!--
    <javaModelGenerator targetPackage="abator"
targetProject="FCG\test">
      <property name="enableSubPackages"
value="true" />
      <property name="trimStrings" value="true"
/>
    </javaModelGenerator>


    <sqlMapGenerator targetPackage="abator" 
targetProject="FCG\test">
      <property name="enableSubPackages"
value="true" />
    </sqlMapGenerator>


    <daoGenerator type="SPRING" targetPackage="abator" 
targetProject="FCG\test">
      <property name="enableSubPackages"
value="true" />
    </daoGenerator>


    <table schema="fcg" tableName="users" domainObjectName="User"
>
       <property name="useActualColumnNames"
value="true"/>    
     
      <generatedKey column="ID"
sqlStatement="DB2" identity="true" />
      <columnOverride
column="DATE_FIELD" property="startDate" />
      <ignoreColumn
column="FRED" />
      <columnOverride column="LONG_VARCHAR_FIELD"
jdbcType="VARCHAR" />
    </table>-->
    <!-- 
-->
    <javaModelGenerator targetPackage="abator"
targetProject="FCG\test" />   
      <sqlMapGenerator
targetPackage="abator" targetProject="FCG\test" />   
    <daoGenerator
type="SPRING" targetPackage="abator" targetProject="FCG\test" />  
    
<!--
    <table tableName="USERS" />   
    <table
tableName="COMPONENTS" />   
    <table tableName="ROLES" /> 
   
<table tableName="RESOURCES" />-->
    <table
tableName="import_railroad_fee_check" />
 
</abatorContext>
</abatorConfiguration>


1下载插件for eclipse


2将配置文件放入src根目录


3更改数据库路径,ojbdc路径


4在test目录下建立abator目录


5 tableName修改为需要自动生成代码的表名


6 在配置文件上右键,会出现生成代码的选项


2009年4月11日星期六

基于XFire实施WS-Security

基于XFire实施WS-Security
http://www.builder.com.cn/2007/0530/405080.shtml

概述


Web Service是安全的吗?鉴于安全性涉及诸多方面(例如身份验证和授权、数据隐私和完整性等),而 SOAP 规范中根本没有提及安全性这一事实,所以我们不难理解人们为什么认为答案是否定的。


很少有不需要某种形式的安全性保证的企业级系统。在 Web Service中,处理Web Service安全的过程比处理其他领域应用的安全问题更为复杂,因为Web Service具有分布式、无状态的特性。


WS-Security是解决Web Service安全问题的规范,大多数商业的Java
EE应用服务器都实现了WS-Security规范,Apache
WSS4J是WS-Security的开源实现,WSS4J通过SOAP中WS-Security相关的信息对SOAP报文进行验证和签名,XFire通
过WSS4J对WS-Security提供了支持。你可以从http://ws.apache.org/wss4j了解更多关于WSS4J的信息。


认识WS-Security


2002 年 4 月,IBM、Microsoft 和 VeriSign 在他们的 Web 站点上提议建立 Web Services
Security
(WS-Security)规范。此规范包括安全凭证、XML签名和XML加密的安全性问题。此规范还定义了用户名凭证和已编码的二进制安全性凭证的格
式。


2002 年 6 月,OASIS 从 IBM、Microsoft 和 Verisign 接收到了提议的WS-Security安全性规范。不久之后就在 OASIS 成立了“Web Service 安全性技术委员会”(WSS TC)。


2006年2月15日,OASIS通过了WS-Security
1.1安全规范,1.1规范突出了对安全权标支持、消息附件和权限管理的增强。1.1规范包括WS-Security核心规范及用户名权标规范1.1、
X.509权标规范1.1、Kerberos权标规范1.1、SAML权标规范1.1、权限表达(REL)权标规范1.1、带附件的SOAP(SWA)规
范1.1和模式1.1。


也许读者会提出这样的问题:SSL也具有完整性和机密性,为什么还需要另外一个WS-Security规范呢?之所以要制定WS-Security规范,是SSL存在以下的问题:


  • 端对端的通信,脱离传输层就无法保证安全性;
  • 消息必须全部加密/或者全部签名,而不能针对消息的某部分,没有考虑XML处理。

SSL对应OSI的传输层,前面我们提到过Web Service是和传输层无关的,将安全问题依附在SSL上就违反了Web Serivce与传输层无关的原则。而WS-Security对应于OSI的应用层,建立消息层SOAP之上。


针对不同领域的细分问题,OASIS
在WS-Security的基础上又制定了几十个规范,其中包括WS-SecureConversation、WS-Federation、
WS-Authorisation、WS-Policy、WS-Trust、WS-Privacy等,形成了一个庞大Web
Service安全性协议家族。


基于XFire实施WS-Security(第一部分)


图1 WS-Security所在位置


有了WS-Security规范,用户就拥有在Web Service应用中实施完整性、机密性和身份验证等安全需求的规范方法。


XFire应用WS-Security的总体方案


XFire通过Apache的WSS4J对WS-Security提供支持,XFire完整发布包中包含了WSS4J的类包。我们知道XFire在
发送和接收SOAP报文前拥有多个阶段,每个阶段都可以注册Handler,对SOAP报文进行前置和后置处理的加工操作。XFire即通过
Handler实施WSS4J,当发送SOAP报文时,通过注册一系列OutHandler,对SOAP报文进行加密、签名、添加用户身份信息等后置处理
操作。而在接收SOAP报文时,则通过注册一系列的InHandler对SOAP进行解密、验证签名,用户身份认证等前置操作。


基于XFire实施WS-Security(第一部分)


图2 XFire应用WS-Security的方案


请求和响应的SOAP在发送之前可以通过注册的OutHanlder进行加工处理,让SOAP转换为WS-Security的保护格式。而服务端和
客户端的接收SOAP 报文之前,可以通过注册的InHandler,将WS-Security格式的SOAP转换正常的SOAP进行处理。


由于XFire在SOAP收发过程中定义了多个不同的生命阶段,所以可以在发送前和接收前完成SOAP报文安全处理的工作,这些操作完全独立于业务处理逻辑,实施WS-Security对于Web Service的业务操作是透明的。


使用WS-Security


在前面内容中,我们通过各种方式将BbtForum中的方法以BbtForumSerivice窄接口开放为Web
Service服务,但个Web Service是没有任何安全性可言的,任何拿到WSDL的人都可以轻松地构造客户端程序访问我们的Web
Service服务。在这节里,我们将解决这个问题,对BbtForumSerivice添加不同的安全功能。


准备工作


在使用XFire的WS-Security之前,必须做一些准备性的工作:包括搭建安全环境,创建密钥对和证书等内容。


安装Java策略文件


策略文件被JDK使用,用以控制加密的强度和算法。确认已经安装对应JDK 版本的Unlimited Strength
Jurisdiction策略文件,这是一个无限制的安全控制文件。你可以从http://java.sun.com/j2se/1.5.0
/download.jsp 或
http://java.sun.com/j2se/1.4.2/download.html页面的底部找到下载的链接。否则在使用WS-
Security时,可以会抛出java.security.InvalidKeyException: Illegal key size的错误信息。


策略文件包括local_policy.jar和US_export_policy.jar文件,将其拷贝到<JAVA_HOME>/jre/lib/security目录下。为了方便读者,我们在光盘resources/jce_policy-1_5_0拥有该策略文件的拷贝。


你也许会问:为何Sun不把它集成到JDK中去,而单独“损人不利己”地弄一个链接出来给人下载?这是因为每个国家,尤其是美国,对涉及密码的软件
产品控制非常严格,在美国国内,很多密码算法长度都作了限制,而且某些算法在某些国家没有申请专利,可以随意使用,而在某些国家却做了明确限制,不准使
用,因此Sun必须按照惯例行事。


安装SecurityProvider


WSS4J使用了BouncyCastle的SecurityProvider,所以需要事先在java.security文件中进行配置,否则运
行加密模式的XFire认证时,会抛出以下的出错信息:org.apache.ws.security.WSSecurityException:
An unsupported signature or encryption algorithm was used unsupported
key


在java.security文件中(位于<JAVA_HOME>/jre/lib/security目录中)添加BouncyCastleProvider的配置:



security.provider.1=sun.security.provider.Sun


security.provider.2=sun.security.rsa.SunRsaSign


security.provider.3=com.sun.net.ssl.internal.ssl.Provider


security.provider.4=com.sun.crypto.provider.SunJCE


security.provider.5=sun.security.jgss.SunProvider


security.provider.6=com.sun.security.sasl.Provider


security.provider.7=org.bouncycastle.jce.provider.BouncyCastleProvider



XFire发布包中包含了BouncyCastle SecurityProvider的类包,位于<XFIRE_HOME>/lib
/bcprov-jdk15-133.jar下。必须将bcprov-jdk15-133.jar加入到类路径中。你可以在http:
//docs.safehaus.org/display/PENROSE/Installing+Security+Provider中获取更多关于安
装BouncyCastle SecurityProvider的帮助。


创建密钥对和数字证书


签名和加密需要使用到数字证书和密钥对,可以使用JDK提供的KeyTool工具创建密钥对和数字证书。我们分别为服务端和客户端创建RSA密钥
对,并生成各自的X509数字证书(包含公钥和数字签名)。服务端和客户端拥有各自的密钥库JKS文件,服务端的密钥库保存服务端的密钥对和客户端的数字
证书,而客户端的密钥库保存客户端的密钥对和服务端的数字证书。


下面,我们来完成创建服务端和客户端密钥库的工作:


  • 编写一个用于创建RSA密钥对和generateKeyPair.bat批处理文件:

rem @echo off


#接受参数


echo alias %1


echo keypass %2


echo keystoreName %3


echo KeyStorePass %4


echo keyName %5


①创建RSA密钥对


keytool -genkey -alias %1 -keypass %2 -keystore %3 -storepass %4-dname "cn=%1" -keyalg RSA


keytool -selfcert -alias %1 -keystore %3 -storepass %4 -keypass %2 ②使用私钥进行自签名


keytool -export -alias %1 -file %5 -keystore %3 -storepass %4 ③导出数字证书


  • 编写一个使用generateKeyPair.bat创建服务端和客户端的密钥库的generateKeyStore.bat批处理文件:

①下面两行命名分别调用generateKeyPair.bat批处理文件为服务端和客户端生成密钥对


call generateKeyPair.bat server serverpass serverStore.jks storepass serverKey.rsa


call generateKeyPair.bat clientclientpassclientStore.jks storepass clientKey.rsa


②将服务端的数字证书导入客户端的密钥库


keytool -import -alias server -file serverKey.rsa -keystore clientStore.jks -storepass storepass -noprompt


③将客户端的数字证书导入服务端的密钥库


keytool -import -alias client -file clientKey.rsa -keystore serverStore.jks -storepass storepass -noprompt


运行该批处理文件后,将分别为服务端和客户端生成一个Java密钥库文件,它们分别拥有一个自己的密钥对和对方的数字证书。我们通过表1对两者密钥库文件的内容进行说明:


1密钥库说明






























服务端Java密钥库


客户端Java密钥库


对应密钥库文件


serverStore.jks


clientStore.jks


密钥库密码


storepass


storepass


库中包含的内容


server密钥对、client数字证书


client密钥对、server数字证书


密钥对别名


server


client


密钥对私钥的保护密码


serverpass


clientpass


  • 将serverStore.jks和clientStore.jks拷贝到<工程目录>/src/META-INF/xfire的目录下,以便后面的实例代码使用。



2009年4月9日星期四

如何正确地在Axis、Axis2和Apache CXF之间抉择?


2007-09-30


新一代的 Web Services 框架如 Axis2、CXF 都是由现有的项目中逐渐演化而来的,Axis2 是由大家熟悉的 Axis 1.x 系列演化过来,而 Apache CXF 则是由 Celtix 和 XFire 项目整合而生,并且刚刚发布了 2.0.2 的最新版本,不过仍是 Apache 的一个孵化项目。



Axis2 是对 Axis 进行了彻底的重写的一个新项目了,它使用了新的模块化架构,更方便于功能性的扩展等等。

Apache CXF 则是由 XFire 和 Celtix 两个现有的项目进行了重组。



问题:如果现有的应用程序是基于 Axis 1.x、XFire 或者 Celtix 的话,那应该怎么办?都迁移到这些新的框架上去吗?但是即使是要迁移,那应该迁移到哪个框架上去呢?

如果是编写一个新的 Web Services 应用程序的话,就不存在迁移的问题了,但是哪个框架是你应当选择进行使用的呢?哪个比哪个更好呢?



对于现在的应用程序的迁移,如果你的应用程序是稳定而成熟的,并且在可预知的未来的情况下,只要很少的一些需求变更要做的话,那么保存你的体力,不要去做“劳民伤财“的迁移工作了。

如果你的现有应用程序BUG缠身,性能,功能等等都一片糟糕的话,那就要考虑迁移了,那选哪个框架呢?先比较一下它们的不同之处:



  1、Apache CXF 支持 WS-Addressing、WS-Policy、WS-RM、WS-Security和WS-I BasicProfile

  2、Axis2 支持 WS-Addressing、WS-RM、WS-Security和WS-I BasicProfile,WS-Policy将在新版本里得到支持

  3、Apache CXF 是根据Spring哲学来进行编写的,即可以无缝地与Spring进行整合

  4、Axis2 不是

  5、Axis2 支持更多的 data bindings,包括 XMLBeans、JiBX、JaxMe 和 JaxBRI,以及它原生的 data binding(ADB)。

  6、Apache CXF 目前仅支持 JAXB 和 Aegis,并且默认是 JAXB 2.0,与 XFire 默认是支持 Aegis 不同,XMLBeans、JiBX 和 Castor 将在 CXF 2.1 版本中得到支持,目前版本是 2.0.2

  7、Axis2 支持多种语言,它有 C/C++ 版本。

  8、Apache CXF 提供方便的Spring整合方法,可以通过注解、Spring标签式配置来暴露Web Services和消费Web Services



如何抉择:

1、如果应用程序需要多语言的支持,Axis2 应当是首选了;

2、如果应用程序是遵循 Spring 哲学路线的话,Apache CXF 是一种更好的选择,特别对嵌入式的 Web Services 来说;

3、如果应用程序没有新的特性需要的话,就仍是用原来项目所用的框架,比如 Axis1,XFire,Celtrix 或 BEA 等等厂家自己的 Web Services 实现,就别劳民伤财了。

Axis和CXF的比较

Axis和CXF的比较


在SOA领域,我们认为Web Service是SOA体系的构建单元(building
block)。对于服务开发人员来说,AXIS和CXF一定都不会陌生。这两个产品都是Apache孵化器下面的Web Service开源开发工具。
Axis2的最新版本是1.3.CXF现在已经到了2.0版本。

这两个框架
都是从已有的开源项目发展起来的。Axis2是从Axis1.x系列发展而来。CXF则是XFire和Celtix项目的结合产品。Axis2是从底层全
部重新实现,使用了新的扩展性更好模块架构。 CXF也重新的深化了XFire和Celtix这两个开发工具。


新产品的退出导致了几个问题。是不是现有的使用Axis
1.x,XFire和Celix的应用需要迁移的新的版本上。如果一个开发人员确定要迁移它的应用到新的框架上,那么他应该选择哪一个呢?相反的,如果一
个开发者决定从头开发一个新的Web Service,他应该使用哪个呢? 这两个框架哪一个更好一些呢?


对于系统迁移来说,也许迁移到新的框架并不难。Axis和CXF都提供了迁移的指导。能够给开发者一些迁移的技巧和经验。但是对于这样迁移,这两个开源项
目都没有提供迁移的工具。对于这样的迁移工作,尽管很值得去寻找所有的可行方案。Axis2和CXF都有各自不同的WebService开发方法,每个方
法都有相当数量拥护者。

通过一个比较矩阵来比较Axis2和CXF变得有现实的意义。这两个项目都开发不够成熟,但是最主要的区别在以下几个方面:

1.CXF支持 WS-Addressing,WS-Policy, WS-RM, WS-Security和WS-I Basic Profile。Axis2不支持WS-Policy,但是承诺在下面的版本支持。

2. CXF可以很好支持Spring。Axis2不能

3. AXIS2支持更广泛的数据并对,如XMLBeans,JiBX,JaxMe和JaxBRI和它自定义的数据绑定ADB。注意JaxME和JaxBRI都还是试验性的。CXF只支持JAXB和Aegis。在CXF2.1

4. Axis2支持多语言-除了Java,他还支持C/C++版本。


比较这两个框架的Web Service开发方法与比较它们的特性同样重要。 从开发者的角度,两个框架的特性相当的不同。
Axis2的开发方式类似一个小型的应用服务器,Axis2的开发包要以WAR的形式部署到Servlet容器中,比如Tomcat,通过这些容器可以对
工作中的Web Service进行很好的监控和管理。Axis2 的Web
administrion模块可以让我们动态的配置Axis2.一个新的服务可以上载,激活,使之失效,修改web服务的参数。管理UI也可以管理一个或
者多个处于运行状态的服务。这种界面化管理方式的一个弊端是所有在运行时修改的参数没有办法保存,因为在重启动之后,你所做的修改就会全部失效。


Axis2允许自己作为独立的应用来发布Web Service,并提供了大量的功能和一个很好的模型,这个模型可以通过它本身的架构(modular
architecture)不断添加新的功能。有些开发人员认为这种方式对于他们的需求太过于繁琐。这些开发人员会更喜欢CXF。


CXF更注重开发人员的工效(ergonomics)和嵌入能力(embeddability)。大多数配置都可以API来完成,替代了比较繁琐的XML
配置文件, Spring的集成性经常的被提及,CXF支持Spring2.0和CXF's
API和Spring的配置文件可以非常好的对应。CXF强调代码优先的设计方式(code-first
design),使用了简单的API使得从现有的应用开发服务变得方便。


不过你选择Axis2还是CXF,你都可以从开源社区得到大量的帮助。这两个框架都有商业公司提供服务,WSO2提供AXIS2的支持,Iona提供
CXF的支持。这两公司都有很活跃的开发者社区。
Axis2出现的时间较早,CXF的追赶速度快。我的建议是:如果你需要多语言的支持,你应该选择AXIS2。如果你需要把你的实现侧重JAVA并希望和
Spring集成,CXF就是更好的选择,特别是把你的Web
Service嵌入其他的程序中。如果你觉得这两个框架的新特性对于你并没有太大的用处,你会觉得Axis1也是不错的选择,你应该继续使用它知道你有充
分的理由去更换它。

指定目录的图片2值化

```python # -*- coding: utf-8 -*- """指定目录的图片,自适应2值化 """ import os from PIL import Image import numpy as np imp...