11.UserService
定义
作为基于以上开发方法的讨论,我们的第一个迭代重点得到工作的搜索屏幕。有一个这个模型的屏幕:
要支持这样一个界面,服务层需要提供下面两个功能:
1.
能获得所有用户的列表为了填充搜索下拉列表(Submitter和Approver)
2.
能获得指定搜索条件的所有的时间卡
UserVO Value Object
让我们放大我们第一个功能,例如,获得所有用户列表。让我们决定我们会创建一个叫做UserService的服务,通过下面的方法提供服务。
public interface UserService
{
public UserVO[] getAllUsers();
}
UserVO
是关联User实体的value object。在最低限度,UserVO会需要两个属性用于drop-down来正常工作:用户ID和登陆名。然而,要使UserVO更可用,让我们在上面增加两个更多的属性:用户的first name和last name。基于这个讨论,UserVO能像下面那样建模。
注意:我们标示UserVO的stereotype为ValueObject。这会告诉AndroMDAUserVO是一种ValueObject。Stereotype决定了AndroMDA代码生成模式,这由AndroMDA代码生成模板来决定。
注意我们标识其中的一个属性为Long,三个属性是String。这些类型不能与java类型java.lang.Long和java.long.String类型混淆。在模型驱动架构中,模型保持技术的无关性并且因此所有的模型元素与平台实现类型无关。因此在上下文中的Long和String是平台无关的类型。当我们运行AndroMDA来生成代码时,它会翻译平台无关的类型为平台相关的类型,例如java.lang.Long和java.long.String。如果我们相同的模型生成.Net应用,AndroMDA会翻译Long为long?并且String翻译为System.String, .NET中等同于java.long.Long和java.lang.String。正如你所看到的那样,模型驱动方法的一个关键的优势是你在业务模型上的投资被保护,甚至随着技术的发展。
在额外的UserVO,我们建模了一种类型叫做UserVO[]。这种类型被需要用来表现返回值,从我们的服务方法中。不幸的,在UML中没有标准的方法来定义对象数组,因此我们采用这种技术作为工作环境。关注一些工具,例如MagicDraw,会接受UserVO[]作为方法参数或返回值,并且存储他作为带[]标记的UserVO类型-然而这是非标准的UML,因此不能被使用标准的UML API检索。小心,不要建模你的参数和返回类型。替代的,建模这些圆熟使用上面描述的workaround。
现在,让我们进入有AndroMDA应用插件创建的UserVO的空的模型中。如果你仍然记得,这个模型被创建在timetracker/mda/src/main/uml/timetracker.xml。请使用下面的uml工具。
- ArgoUML (under construction)
- MagicDraw 9.x
- MagicDraw 11.5
- RSM 6
重点注意(译者补充)
:在RSM6中,需要导入uml2的两个文件在andromda/uml2目录下面。andromda-profile-datatype-uml2-3.2-RC1.uml2,andromda-profile-uml2-3.2-RC1.uml2。
把这andromda-profile-uml2-3.2-RC1.uml2文件加入到概要文件中,导入后还会有emx文件存在,这时需要在timetracker中导入包。
这样数据类型就准备好了。
现在,让我们要求AndroMDA来生成UserVO的代码:
1.
打开命令提示符,修改目录到C:/timetracker
2.
执行命令mvn install。确认你看到了BUILD SUCCESSFUL信息。
3.
留着命令提示符窗口以后构建使用。
在Windows Explorer中打开目录
C:/timetracker/common/target/src/org/andromda/timetracker/vo,注意UserVO类生成到了这里。打开类并且浏览他的内容。ValueObject stereotype触发在代码中只有一个产品生成,java类名表示valueobject。后来我们能看到stereotype的以便于触发多个工件的生成。
Eclipse
用户:AndroMDA也在c:/timetracker目录中生成了.project和.classpath文件。要浏览你的代码,看下面的步骤:
1.
启动Eclipse。
2.
选择File菜单的import。
3.
选择”Existing Projects into Workspace”并且点击next。
4.
在import对话框,点击browse
5.
浏览C:/timetracker点击OK。
6.
点击finish。现在timetracker项目可用了。
7.
在package explorer中右击timetracker项目,选择properties。
8.
点击java compiler,然后检查“Enable project specific properties”。
9.
因为我们要使用JDK5.0的特点,设置编译器兼容级别为5.0,点击OK。
10.
Eclipse
现在会要求“Build the project now?”,回答yes。
这个设置过程只要求一次。以后,当你再生成代码时(产生了新的.project和.classpath文件),简单的在timetracker项目上右击,选择refresh。
UserService
现在UserVO被定义了,我们能建模UserService。基于我们上面的讨论,模型如下:
使用下面的连接编辑模型。
- ArgoUML (under construction)
- MagicDraw 9.5
- MagicDraw 11.5
- RSM 6
现在,让我们要求AndroMDA生成UserService的代码:
1. 在命令提示符下面执行命令 mvn install,确保你看到BUILD SUCCESSFUL的提示信息
不像ValueObject,Service生成了三类class:接口,抽象基类和具体实现。下面就是UserService生成的三个类。
l UserService.java:UserService是服务方法指定的接口。因为这个接口被客户端应用需要,它被生成在目标common目录分支中。
l UserServiceBase.java:UserServiceBase实现UserService接口描述的方法。这些基础的方法做一些参数检查和胆力实际的业务功能到”handle”方法。Handle方法被希望在UserServiceImpl类中手工实现。UserServiceBase也管理应用的交易行为。事务在进入服务方法时启动并且在从这些方法退出时完成。UserServiceBase.java被生成在core目录中。
l UserServiceImpl.java:UserServiceImpl是UserServiceBase类的具体继承类。这里是开发人员希望编写主要服务方法的业务逻辑的地方。UserServiceImpl.java生成在源码路径的core目录下。
UserService Test
在测试驱动开发TDD的精神中,我们要在UserService被实现之前写测试。TDD的整个重点是写robust鲁棒的测试来验证系统完成的功能-如果测试通过,你有很好的信心系统会正常的工作;如果不是,你还有一些工作没有J。当然,一旦测试被定义了,你必须写实现代码来使得他们通过。好的方面是你仅仅需要写能使测试通过的代码-不多也不少!对于我们的简单的getAllUsers()方法,我们会写一个简单的测试来获取所有的用户并且验证返回值是否是我们所期望的。
TODO:下面描述的测试完全是手工的。我们使用的测试框架(TestNG和DBUnit的联合)完全有能力做到,因此我们会在这个教程的未来版本融入自动化测试。现在我们简单的检查测试结果。
我们使用TestNG测试我们的服务层。TestNG是一个很强大的测试框架,与JUnit非常相像,但是提供了一些额外的功能,例如数据驱动测试,来简化测试流程。为了使我们的测试过程生效,我们不会在JBoss上测试服务层。相反,我们会配置我们的测试直接与POJO服务对话,完全绕过EJB容器,下面是我们需要启用的:
1. 打开POM文件,C:/timetracker/pom.xml
2. 搜索单词junit。注释junit周围所有依赖的章节,像下面一样:
<!--
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>
3.8.1
</version>
<scope>test</scope>
</dependency>
-->
2. 就在注释的下面,增加下面的内容来启用TestNG。记住,如果你感到迷惑,你可以与已经完成版本比较一下你的代码。
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>4.7</version>
<scope>test</scope>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version> 1.2.12 </version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version> 3.1.13 </version>
<scope>test</scope>
</dependency>
4. 现在搜索maven-resources-plugin,在plugins段中的下面。增加下面两个插件在maven-resources-plugin后面。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.2</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
那就是所有我们需要使用TestNG需要修改的地方。现在拷贝完整的test目录从C:/timetracker-completed/core/src到你自己的timetracker目录。拷贝后,在你的TimeTracker项目目录中删掉文件
core/src/test/java/org/andromda/timetracker/service/TimeTrackingServiceTest.java-我们还没有准备编译这个文件。
浏览core/src/test目录来获得相似的内容,使用下面的步骤指导你运行测试:
3. UserServiceTest位于test/java/org/andromda/timetracker/service。这个测试非常简单。initializeTestSuite()方法得到一个UserService的饮用,这个饮用被testGetAllUsers()方法使用,用来获得所有的用户和日志。UserServiceTest像下面显示的一样:
public class UserServiceTest {
private Log logger = LogFactory.getLog(UserServiceTest.class);
private UserService userService;
@Configuration(beforeSuite=true)
public void initializeTestSuite() {
// Initialize ServiceLocator
logger.info("Initializing ServiceLocator");
ServiceLocator locator = ServiceLocator.instance();
locator.init("testBeanRefFactory.xml", "beanRefFactory");
// Initialize UserService
logger.info("Initializing UserService");
userService = locator.getUserService();
}
@Test
public void testGetAllUsers() {
logger.info("testGetAllUsers:");
UserVO[] users = userService.getAllUsers();
for (int i=0; i<users.length; i++) {
logger.info(users[i].getUsername());
}
}
}
3. 目录test/resources包含下面测试需要的文件:
l log4j.xml控制日志行为
l testBeanRefFactory.xml允许测试直接与service对话
l testng.xml控制TestNG自己的行为
4. 打开文件testng.xml,注意UserServiceTest是注释的。这机制用于临时禁用那些打断build的测试。因为我们向想再启动测试,仅仅取消测试的注释。
5. 在C:/timetracker目录执行下面的命令来运行测试
mvn –f core/pom.xml test
注意mvn install也运行这个测试,但是它会生成代码。因为我们自从最后一次代码生成后没有修改模型,我们只要简单的要求Maven运行在core子目录的测试即可。你会发现测试失败,提示下面的信息:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
org.apache.maven.surefire.booter.SurefireExecutionException: null;
nested exception is java.lang.NullPointerException: null
java.lang.NullPointerException
...
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] There are test failures.
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8 seconds
[INFO] Finished at: Sun Aug 27 21:41:48 EDT 2006
[INFO] Final Memory: 6M / 11M
[INFO] ------------------------------------------------------------------------
测试失败因为initializeTestSuite()方法不能正确初始化UserService。原因为Spring不能找到一个他所希望找到的bean叫做’sessionFactory’。当我们在中间层实现实体时,这个问题会被纠正。