关于本教程
本教程将会向你展示如何创建一个业务Facade类(和一个JUnit Test)与
DAO we created in Part I交互操作。
在AppFuse的语境下,这被称作一个Manager类,它的主要职责是持久户层(DAO)和web层之间的一个桥梁,它也很好的把展示层和数据库层(例如Swing应用)解耦,Managers必定是应用程序所有的业务逻辑所在的地方。
-
我在“真实世界”中实际操作的方式用蓝色斜体表示。
让我们从在AppFuse的框架下创建一个ManagerTest和Manager。
目录
- 创建一个新的运行JUnit测试的ManagerTest
- 创建一个新的与DAO通讯的Manager
- 为这个Manager和事务配置Spring
- 运行ManagerTest
创建一个新的运行JUnit测试的ManagerTest
在
Part I, 我们创建了一个Person对象和一个PersonDAO对象 - 所以我们继续开发这个实体,首先,我们创建PersonManager的JUnit test,在test/service/**/service目录下创建PersonManagerTest,我们会希望在DAO对象同样的基本方法 (get, save, remove) 测试。
-
这看起来是多余的(为什么全是测试!),但如果是一个6个月的过程这个测试是非常重要的。
这个类必须扩展service包下的BaseManagerTestCase,这个类(BaseManagerTestCase)的功能与BaseDAOTestCase类似。
-
我通常会修改(打开 → 另存为)存在的测试(如UserManagerTest.java),查找/替换[Uu]ser with [Pp]erson,或者其他任何我的对象的名字。
以下代码是一个基本的Manager的JUnit测试的要求,与DAOTest不同,这个测试使用jMock来吧Manager和他的依赖隔离,使它成为一个真的"单元" 测试。这可以使你只关心业务逻辑而不必担心它的依赖,以下代码简单的设置好Manager和它 的依赖。
package org.appfuse.service; import java.util.List; import java.util.ArrayList; import org.appfuse.dao.PersonDAO; import org.appfuse.model.Person; import org.appfuse.service.impl.PersonManagerImpl; import org.jmock.Mock; import org.springframework.orm.ObjectRetrievalFailureException; public class PersonManagerTest extends BaseManagerTestCase { private final String personId = "1"; private PersonManager personManager = new PersonManagerImpl(); private Mock personDAO = null; private Person person = null; protected void setUp() throws Exception { super.setUp(); personDAO = new Mock(PersonDAO.class); personManager.setPersonDAO((PersonDAO) personDAO.proxy()); } protected void tearDown() throws Exception { super.tearDown(); personManager = null; } } |
现在你已经把类的骨架搭好了,你需要添加肉了:填写确保所有测试通过的代码,以下来自DAO Tutorial的片断帮助我们理解我们将要做的事情。
-
...我们创建以"test"(全部小写)开头的方法,只要这些方法是public,返回类型是void,并且没有参数,它们就会被<junit>调用,以下是为了测试简单的CRUD操作,一件需要记住的事情是每一个方法(也可以称作测试)必须是自制的。
添加如下方法到PersonManagerTest.java:
public void testGetPerson() throws Exception { // set expected behavior on dao personDAO.expects(once()).method("getPerson") .will(returnValue(new Person())); person = personManager.getPerson(personId); assertTrue(person != null); personDAO.verify(); } public void testSavePerson() throws Exception { // set expected behavior on dao personDAO.expects(once()).method("savePerson") .with(same(person)).isVoid(); personManager.savePerson(person); personDAO.verify(); } public void testAddAndRemovePerson() throws Exception { person = new Person(); // set required fields person.setFirstName("firstName"); person.setLastName("lastName"); // set expected behavior on dao personDAO.expects(once()).method("savePerson") .with(same(person)).isVoid(); personManager.savePerson(person); personDAO.verify(); // reset expectations personDAO.reset(); personDAO.expects(once()).method("removePerson").with(eq(new Long(personId))); personManager.removePerson(personId); personDAO.verify(); // reset expectations personDAO.reset(); // remove Exception ex = new ObjectRetrievalFailureException(Person.class, person.getId()); personDAO.expects(once()).method("removePerson").isVoid(); personDAO.expects(once()).method("getPerson").will(throwException(ex)); personManager.removePerson(personId); try { personManager.getPerson(personId); fail("Person with identifier '" + personId + "' found in database"); } catch (ObjectRetrievalFailureException e) { assertNotNull(e.getMessage()); } personDAO.verify(); } |
这个类不会被编译,因为我们还没有创建PersonManager接口。
-
在AppFuse里遵从这么多规范来实现可扩展性看起来很可笑,事实上,在绝大多数我参与的项目里 - 我发现在一年里学了如此多的知识,以至于我不想扩展我的架构,我想去重写它,我希望通过采纳最佳实践来保持AppFuse的时效性,但这并不经常发生,每年都仅仅是一个道最新版本的升级,而不是一个重写,;-)
创建一个新的与DAO通讯的Manager
马上,为所有实现类在src/service/**/service目录创建一个PersonManager.java接口来指定基本的CRUD操作,为了显示的目的,我去掉了所有的JavaDocs, setPersonDAO()方法不是在所有的情况下出现,只是因为PersonManagerTest可以把DAO赋值。
-
通常,我会复制(打开 → 另存为)一个已存在的文件 (例如UserManager.java).
package org.appfuse.service; import org.appfuse.model.Person; import org.appfuse.dao.PersonDAO; public interface PersonManager { public void setPersonDAO(PersonDAO dao); public Person getPerson(String id); public void savePerson(Person person); public void removePerson(String id); } |
我们创建一个PersonManagerImpl类来实现PersonManager中的方法,为此,在 src/service/**/service/impl创建一个PersonManagerImpl.java类,他必须扩展BaseManage并且 实现PersonManager。
package org.appfuse.service.impl; import org.appfuse.model.Person; import org.appfuse.dao.PersonDAO; import org.appfuse.service.PersonManager; public class PersonManagerImpl extends BaseManager implements PersonManager { private PersonDAO dao; public void setPersonDAO(PersonDAO dao) { this.dao = dao; } public Person getPerson(String id) { return dao.getPerson(Long.valueOf(id)); } public void savePerson(Person person) { dao.savePerson(person); } public void removePerson(String id) { dao.removePerson(Long.valueOf(id)); } } |
需要注意的是setPersonDAO()方法,Spring使用它来绑定PersonDAO到Manager,这些配置在applicationContext-service.xml 文件,我们将在Step 3配置这些,现在你可以使用"ant compile-service"编译所有代码。
现在你需要为服务层配置Spring文件,它才会知道这个新的Manager。
为这个Manager和事务配置Spring
为了通知Spring我们的PersonManager接口和它的实现类,打开 src/service/**/service/applicationContext-service.xml,你会看到注释掉的关于 "personManager"的定义,去掉注释,或者直接在文件末尾添加:
<bean id="personManager" parent="txProxyTemplate"> <property name="target"> <bean class="org.appfuse.service.impl.PersonManagerImpl" autowire="byName"/> </property> </bean> |
"parent"属性会引用一个TransactionProxyFactoryBeanbean的定义,这也是所有的事物对象所要设置的。
运行ManagerTest
保存所有的文件,并且运行ant test-service -Dtestcase=PersonManager。
Yeah Baby, Yeah: BUILD SUCCESSFUL
Total time: 9 seconds
此刻所有修改后的文件可以从available for download.
下一部分: Part III: Creating Actions and JSPs- 在AppFuse架构下创建Actions和JSPs。