??xml version="1.0" encoding="utf-8" standalone="yes"?> 1. 创徏POJO Node.java 2. 使用AppGen生成代码 3. 单元试 4. Form Bean 5. Action 6. JSP 7. 问题 用AppFuse有一D|间了Q也发现了它的一些问题,或者说不适合我们开发的地方?
2. Manager试 3. Action试 4. JSP试 1. E序描述 2. JSP面 3. ActionForm Bean 4. Validator验证框架 <arg0 key="userForm.username"/> 3.3 模型层结?br> Model层ؓ(f)整个pȝ的核心部分,完成应用的业务逻辑?qing)与数据库的通信。AppFuse中将Model分ؓ(f)两层Q持久层和业务层。采用SpringQHibernate框架实现Q这里以对用户User数据的操作ؓ(f)例详l阐q其实现方式?
Ҏ(gu)久化数据的访问基于DAOQData Access ObjectQ模式实现。DAO模式提供了访问关pd数据库系l所需的所有接口操作的接口。DAO模式底层数据访问操作与高层业务逻辑分离开Q对上层提供面向对象的数据访问接口?
Model层与User相关的类有:(x) 3.3.2 Spring的IoC 在UserManagercM有一句:(x) 在控制层调用业务层方法时Q用服务定位器q回lSpring contextQSpring的BeanFactory提供了getBeanҎ(gu)。BeanFactory是一个通用的FactoryQ它使对象能够按名称获取Qƈ且能理对象之间的关pR?br> 在applicationContext-service.xml中配|?br> 在控制层BaseAction定义通用Ҏ(gu)Q?nbsp; 在UserAction中创建UserManager的实例:(x) 3.3.3 Spring的事务管?br> 在数据持久层的杰A(ch)献,可能是Spring最为闪亮的优点?br> Spring提供了通过容器的集U式参数化事务机Ӟ实现事务的外部管理。容器管理的参数化事务ؓ(f)E序开发提供了相当的灵zL,同时因ؓ(f)事务委托给容器q行理Q应用逻辑中无需再编写事务代码,大大节省了代码量Q特别是针对需要同时操作多个事务资源的应用Q,从而提高了生率?br> AppFuse在applicationContext-service.xml文g中进行了对事务的配置 q里定义了一个名为txProxyTemplate的TransactionProxyFactoryBean服务。它对包含实际数据逻辑的持久层对象q行了事务的装。在q里Q通过transactionAttributes属性,我们指定了事务的理{略Q即所有的名称以save和remove开头的Ҏ(gu)U_事务理范围。如果此Ҏ(gu)中抛出异常,则Spring当前事务回滚,如果Ҏ(gu)正常l束Q则提交事务?br> 而对所有其它方法则以只ȝ事务处理机制q行处理。设为只d事务Q可以持久层尝试对数据操作q行优化Q如对于只读事务Hibernate不执行flush操作Q而某些数据库q接池和JDBC 驱动也对只读型操作进行了特别优化?br> 如果有其他的Ҏ(gu)需要进行写数据库操作,可以在相应的Manager配置中声明。如在UserManager中,添加了属?br> q样Q以LoginCookiel尾的方法也可以写数据库了?br> Spring可以Q意Java Class U_事务理Q而无需对其q行M修改Q因此我们的cd能完全不知道它正在被q行事务理?
Spring 提供了一?HibernateTransactionManagerQ采用面向Hibernate的TransactionManager实现Qorg.springframework.orm.hibernate.HibernateTransactionManager。他用线E捆l了一个Hibernate SessionQ用它来支持transactions?br> sessionFactory Bean引用了HibernateSessionFactoryQ而transactionManager Bean引用了HibernateTransactionManage?transactionManager Bean中有个sessionFactory属性?HibernateTransactionManager有个sessionFactory setter ?getterҎ(gu)Q用来在Spring启动的时候实?#8220;依赖注入” Qdependency injectionQ的?在sessionFactory 属性里引用sessionFactory Bean。这两个对象在Spring容器初始化后pl装了v来了?br> pȝ持久层中所有的c都l承自Spring提供的HibernateDaoSupportc,HibernateSupport实现了HibernateTemplate和SessionFactory实例的关联。HibernateTemplate对Hibernate Session操作q行了封装,提供了一个简单的方式实现了Hibernate-based DAO对象。借助HibernateTemplate我们可以q每次数据操作必须首先获得Session实例、启动事务、提?回滚事务以及(qing)?ch)杂的try/catch/finally的繁琐操作。一个简单的Hibernate讉KҎ(gu)完全解决了些麻? 无论是在多个DAO接口q是在多方事务的情况下,Spring使得多种DAO对象无缝地协同工作?br> 对于单的单步的动作,象find, load, saveOrUpdate或者delete的调用,HibernateTemplate提供更ؓ(f)便利的选择以代替象一行的回调的执行。此外HibernateDaoSupportcL供了setSessionFactoryҎ(gu)来接受一个SessionFactoryQ同时提供了getSessionFactory和getHibernateTemplateҎ(gu)供其l承cM用。将q些l合hQ允许对于典型的需求给Z非常单的DAO实现Q如获得所有用L(fng)Ҏ(gu)Q?br> AppFuse实现的最主要的功能是对用L(fng)理。下面就以对用户的管理来说明一下AppFuse控制器的实现?
与用户信息表单数据相关的FormBean是UserForm。UserForm用来存放用户的基本信息?br> UserForm的控制器是org.appfuse.webapp.action.UserAction。UserActionl承了BaseAction。BaseAction也是pȝ中几乎所有Actioncȝ父类Q它l承自Struts提供的DispatchActionQƈ提供了控制层通用的方法?
2. Action 3. DispatchAction 4. BaseAction 5. UserAction AppFuseq给我们提供或介l了很多好用的工P能大大加快我们的开发速度。如果能够很好的使用q些工具Q我们可以只l出一个表的POJOQ它们就能够自动生成所有的操作该POJO对应的数据表通常需要的增、删、改、查功能的模型层、控制层、视囑ֱ文gQ几乎不用再修改可以完成对q张表的所有基本操作?br>1. AppGen 2. xdoclet注释 下面分别介绍各层的功能:(x)
最基本也是最重要的工作首先是建立q个菜单的POJOQ源文g?a >q里下蝲吧,?多说了?
q一步偶?惛_说了Q只要用过AppFuse的都应该知道啦,8知道的看偶前面的文章?
接下来当然就是要修改生成的代码了Q首先是单元试cNodeDAOTest.java。我们要l它加几个测试方?br> - testChildrenQ测试添加和删除子节?br> - testParentQ测试两U的父子关系
- testDeleteMiddleNodeQ测试中间节点的删除
具体怎么做的?a >q里吧,但是要注意最好不要直接下载替换原文gQ如果AppFuse的版本不一P试Ҏ(gu)也是不一hQ最好把q几个方法单独copyq来?
AppGen生的NodeForm不能满我们的要求了Q所以把q个文gxdoclet-NodeForm.java攑ֈmetadata\web下?
Action是这里最重要的类了,它修改的地方也很多,所以就直接下蝲吧。不q偶有一点不太明白的是作者ؓ(f)虄要把populate()Ҏ(gu)的第二个参数设ؓ(f)long而不是LongQ他传入的可是Long啊,自己手动Ҏ(gu)吧?
接下来是JSPQ自动生成的代码基本没用了。这里nodeList.jsp可以删除Q?a >nodeForm.jsp被重新写q了。如果你的AppFuse版本比较斎ͼ?.8版以上的Q这个JSP下蝲下来后还需要改一下。搜索property="method"Q这个是button里的属性,Ҏ(gu)button的功能把他们分别修改成property="method.save"、property="method.delete"、property="method.add"{,否则按下按钮可是要报错的.
修改了JSP也别忘了它的资源文g。把q个文g里关于nodeForm部分的内容copyC自己的ApplicationResources下吧Q当然可以汉化一下?br> 接下来修?a >menuExpandable.css?a >menuExpandable.jsQ直接替换原文g可以了?br> 现在可能q是不能正常使用Q因为我们的AppFuse下已l有一个菜单了Q新的菜单会(x)和原来的菜单起冲H,D我们的新菜单打不开。到web\decorators下打开default.jsp文gQ把含有<c:import url="/WEB-INF/pages/menu.jsp"/>的这一D代码挪?lt;div id="content"></div>代码D늚下面Q这样就可以正常使用了?
在用中我还发现一个问题,是q个菜单的target有点问题Q只有末节点的target有效Q其它节点的target设了也灭有效果,8知道哪位大虾有好的解军_法,?胜感Ȁ?/p>
]]>
UserDAOTest用于试UserDAOq个接口和它的实现UserDAOHibernateQ它在test/dao/**/dao/中?br> 所有的DAOTest都承自BaseDAOTestCaseQBaseDAOTestCasel承自TestCase。这个父cdlؓ(f)我们写好了从Spring加蝲ApplicationContext的方法?br> setUp()里做了在试之前的初始化工作Q创ZUserDAO和RoleDAO的实例,tearDown()里做销毁工作,q是每个Junit试c都要做的事?br> 以testUpdateUser()Ҏ(gu)ZQ该Ҏ(gu)主要用于试UserDAO的saveUser()Ҏ(gu)是否正确。首先调用getUser()获得用户“tomcat”的信息,q修改其地址Q然后调用saveUser()Ҏ(gu)保存修改的记录。重新获?#8220;tomcat”的信息,校验其地址是否为新地址Q如果地址为新地址Q测试成功。接下来?#8220;tomcat”的version属性的值置I(version为验证当前记录是否ؓ(f)新记录的标志Qnull表示新纪录)(j)Q重C?#8220;tomcat”Q此时hibernate?x)认记录为新记录Q进行insert操作Q但username字段Z键不能重复,因此应该抛出异常。如果捕获到异常Q测试成功?br> 在控制台q入目根目录,键入ant test-dao -Dtestcase=UserDAOQ如果出现BUILD SUCCESSFULQ说明测试成功。这P我们不需要写Manager、Action、JSPQ不需要运行容器也可以保我们的类正确了?/span>
接下来l看试UserManager的类UserManagerTest。它在test/service/**/service/中,l承自BaseManagerTestCaseQ这个父cv着与BaseDAOTestCasecM的作用?br> 与UserDAOTest不同的是UserManagerTest使用了jMock帮助其测试。jMock用于解决UserManager的依赖,因ؓ(f)UserManager中需要调用UserDAO的方法,而单元测试的基本规则是一ơ只试一个对象,jMock帮助你把UserManager孤立hQ它不?x)受到UserDAO的媄(jing)响,我们来看它到底怎么做?br> 在setUp()里,我们把UserDAO和RoleDAO攑ֈMock中,让Mock来做UserDAO和RoleDAO的代理,q将q两?#8220;假冒?#8221;DAO注入到UserManager中?br> q是以testSaveUser()Z来看q个testcL么工作。首先创建User对象Q设|用户名?#8220;tomcat”Q权限ؓ(f)“user”。然后我们告诉Mock当UserManager调用UserDAO的getUser()Ҏ(gu)q参数是“tomcat”Ӟ我们期待UserDAOq回我们刚刚创徏的那个对象。接下来调用UserManager的getUser()Ҏ(gu)以获?#8220;tomcat”的信息。修改电(sh)话号码的内容。然后重|我们对Mock的要求。这ơ我们要求当UserManager调用UserDAO的saveUserҎ(gu)Ӟ不返回Q何倹{然后调用UserManager的saveUser()Ҏ(gu)Q校验user是否为新的电(sh)话号码和权限是否q是一个,若是Q测试成功。verify()用于(g)查所有应该调用的Ҏ(gu)是否都被调用了。通常来说Q每对Mock对象调用了一ơexpects()Q用完后都要执行一ơverify()?br> 在控制台执行ant test-service -Dtestcase=UserManagerQ看看结果?
我们l箋看test/web/**/action下的UserActionTest。它l承自BaseStrutsTestCaseQBaseStrutsTestCasel承MockStrutsTestCaseQ这个父cM做了cMBaseManagerTestCase的工作?br> Action是一个控制器Q主要用于接收视囑ֱ的请求,调用模型层的Ҏ(gu)Q然后返回视囑ֱ。在q里我们不关心模型层或视囑ֱQ我们只要关心Action是否能够正确的得到请求和响应hQ以?qing)能够正的?gu)h转向。MockStrutsTestCasel了我们试q方面很好的支持?br> 以testSave()ZQ首先创Z个UserFormQ在里面攑օ部分数据Q将UserForm攑օ该Action所对应的范围内。用setRequestPathInfo()讄h路径?#8220;/saveUser”Q用addRequestParameter()d好请求参敎ͼactionPerform()Ҏ(gu)模拟请求的全过E。然后用verifyForward()Ҏ(gu)验证h转发路径是否正确。ƈ验证能不能在Action范围内得到UserForm?br> q行ant test-web -Dtestcase=UserActionQOK?br> 要注意一点,q里的单元测试虽然承自MockStrutsTestCaseQ但没有使用MockQ也是_(d)它会(x)真正执行到所有相关的Ҏ(gu)Q包括修Ҏ(gu)据库?
我们同样可以对JSPq行试。这里有一个工具叫做Canoo WebTestQ它使用xml配置的方式来试JSP?br> q入test/web/Q有一个web-tests.xml文gQ里面有所有struts-config.xml中存在的path的测试?br> 以SaveUserq个targetZQ我们做一个简单的说明。测试步骤包含在steps中,invoke中给q个step定一个IDP讄h的urlQeditProfile.html。接下来验证JSP面的title是否与预期的一致。其中{webapp.prefix}和{userProfile.title}的内容在WEB-INF\classes中的ApplicationResources中定义。接下来l表单中的文本域填写内容Q用clickbutton点击保存按钮Q验证保存后的页面标题是否ؓ(f)预期的标题?br> q一试需要运行容器,因此首先q行TomcatQ再在控制台键入ant test-canoo -Dtestcase=UserTests或ant test-jsp -Dtestcase=PersonTests。用ant run-all-tests无需q行TomcatQAntZ做这件事?/p>
视图层主要由JSP|页构成Q此外还包括Resource Bundle资源文g?qing)ActionForm Bean、Validation{,q些lg提供对国际化、接收用戯入的表单数据、表单验证和错误处理{的支持?br> 与User的视囑ֱ有关的文件有Q?br> userList.jspQ用户列表页面,用于h理权限的用户对其他用户的管理?br> userForm.jspQ用户信息页面,用于对用户信息的增、删、改
UserForm.javaQ与用户信息表单对应的ActionForm Bean
validation.xmlQ对用户信息表单q行验证的配|文?br> Resource Bundle资源文gQ中文的是ApplicationResource_Zh_cn.properties
default.jspQ用于复合网늚sitemesh文g
JavaScript、CSS文g
与用L(fng)关的JSP面包括用户列表和用户信息。head?qing)foot{通用信息攄在head.jsp、foot.jsp中,因此userList.jsp?qing)userForm.jsp只包含与业务相关的标{。下面介l主要的标签功能Q?br> <logic:messagesPresent>Q判断指定的消息是否存在。若指定message属性ؓ(f)trueQ则从request范围内检索属性key为Globals.MESSAGE_KEY的ActionMessages对象。若不指定属性,则默认检索属性key为Globals.ERROR_KEY的ActionMessages对象。在本系l中一个检索Success消息Q一个检索Error消息?br> <fmt:message>QJSTL标签。用于输出Resource Bundle中的一条消息?br> <bean:struts>Q用于检索Struts框架内在的对象,如ActionFormBean、ActionForward、ActionMapping。在q里用于在JSP面上定义一个ActionForward 变量以方便其它地方的引用。forward为struts-config.xml中定义的global-forward?br> <c:set>QJSTL标签。用于定义一个变量,以方便其它地方的引用?br> <c:out>QJSTL标签。用于在面上显CZ个EL表达式的|如显C?lt;c:set>定义的变量。EL是JSTL采用的简单的表达式语aQ该语言提供一个访问和操作应用E序数据的简单方式?br> <c:if><c:when><c:forEach><c:choose>QJSTL标签。用于处理条件的标记Q根据特定的逻辑条g来控制输出网内容,或者@环遍历集合中的所有元素?br> <display>QDisplay Tag的标{,用于昄控制层返回的List。可以方便的定制表格是否分页、对列排序、导出数据等?br> <html:form>Q用来定义HTML表单。Struts的HTML标签可以和标准的HTML元素完成相同的功能,Struts框架能够把表单中的数据自动映到相应的ActionForm Bean中?br> <html:text><html:password><html:hidden>Q在表单上生成相应的HTML元素?br> <html:submit><html:cancel>Q在表单上生成提交按钮和取消按钮。当用户按下按钮Ӟ生一个提交事件或取消事gQ由ActioncL莗本pȝ?lt;html:submit><html:cancel>的属性相同(均ؓ(f)methodQ,用户按下提交按钮或取消按钮后执行哪个Ҏ(gu)由lookupMethods.properties军_?br> <html-el:multibox>Q在表单上生成HTML的CheckBox标签。html-el使用了JSTL扩展Q可以用EL表达式?br> <html:messages><html:errors>Q用于在|页中输出消息?lt;html:errors>用于输出错误消息Q本pȝ中用于显C单中字段U的错误信息Q?lt;html:messages>昄全局的消息?br> 自定义标{:(x)
在你最初运行ant newӞAppFuse׃(x)Ҏ(gu)你键入的目名自动生成一个标{文Ӟ在userForm.jsp中你?x)看到这个标{?br> <YOURAPPNAME:label>Q它用于昄表单输入域前的提C文本,Ҏ(gu)本添加了一些特别的样式Q如在必填项前自动加"*"L(fng)?br> <YOURAPPNAME:country>QuserForm中有讄国家一,该标{于显C国家的下拉列表?br> <YOURAPPNAME:constants>Q用于在面上显C常量类org.appfuse.Constants中的帔R的倹{?
ActionForm Bean是Struts提供的表单数据传输对象,用于在视囑ֱ和控制层之间传递HTML表单数据。控制层可以从ActionForm Bean中读取用戯入的表单数据Q也可以把来自模型层的数据存攑ֈActionForm Bean中,然后把它q回l视图?br> User表单的ActionForm Bean为UserForm。UserForml承BaseForm。BaseForm是ActionForm的子cd象,扩展了三个通用Ҏ(gu)QtoString()、equals(Object o) 和hashCode()。ƈ定义了一个验证,用于允许用户在点?删除"?取消"按钮时表单无验证?br> UserForm中定义了与用户信息表单中的字D对应的属性?
Validator验证框架负责数据验证Q采用基于XML的配|文件来配置验证规则。其相关的文件有Q?br> validation.xmlQ针对具体的Struts表单Qؓ(f)ActionForm配置所需的验证规则?br> validator-rules.xmlQ框架自带文Ӟ包含了一l通用的验证规则?br> validator-rules-custom.xmlQ自定义验证规则。本pȝ中定义了验证两个指定的输入域值是否相{的规则。用于验证密码和认密码是否相等。其实将q个Ҏ(gu)E微修改一下,?#8220;=”变ؓ(f)“<?gt;”可以验证一个域不能大于/于另外一个域Q非常的实用?br> Resource BundleQ定义验证失败时昄的提C文本。Resource Bundle 中以errors为前~的Keyl大部分用于Validator的错误提C?br> 下面是在validation.xml 中配|的userForm验证规则
<form name="userForm">
<field property="username"
depends="required">
</field>
……………………………
</form>
该配|通过depends="required"定义了userForm的username字段即用户名是必填项?br> <arg0 key="userForm.username"/>声明了该字段所对应的文本在Resource Bundle中的Key。Validator?x)在Resource Bundle中寻找errors.required对应的文本:(x)'{0}' 为必填项Q将userForm.username对应的文本带入{0}。如用户没有填写该项QValidator?x)提C?#8220;'用户? 为必填项”的消息?br> validation.xml中用正则表辑ּ定义了一些简单的全局的规则,如电(sh)话号码的格式{?br> 默认情况下,Validator框架在Web服务器端执行表单验证。validator-rules.xml里已l定义了客户端JavaScript的生成规则,可以在JSP|页中生成JavaScript脚本。需要进行客L(fng)验证Ӟ在JSP中包含:(x)
<html:javascript formName="userForm" cdata="false"
dynamicJavascript="true" staticJavascript="false"/>
<script type="text/javascript"
src="<c:url value="/scripts/validator.jsp"/>"></script>
q在<html:form>中定义onsubmit事gQ?br> onsubmit="return validateUserForm(this)"
在userForm表单的提交和取消按钮中包含了onclick="bCancel=false"事g。bCancel是Validator定义的是否进行验证的变量。当用户点击删除或取消按钮时Q表单无验证,此时bCancel= true
]]>
POJOQ?br> UserQ管理员表的业务对象?br> 业务层:(x)
UserManagerQ业务层接口Qؓ(f)控制层所调用?br> UserManagerImplQ业务层接口的实玎ͼ调用持久层接口?br> 持久层:(x)
UserDAOQ持久层接口Qؓ(f)业务层实现所调用?br> UserDAOHibernateQ持久层接口的实现?br> XML配置文gQ?br> applicationContext-service.xmlQ业务层接口的配|文件?br> applicationContext-hibernate.xmlQ持久层接口的配|文件?
IocQInversion of ControlQ即反{控制。Ioc模式即Dependency Injection模式是依赖注的意思,也就是将依赖先剥,然后在适当时候再注射q入?br> Spring的轻量的bean容器Z务对象(business objectsQ、DAO对象和资源(如:(x)JDBC数据源或者Hibernate SessionFactorie{)(j)对象提供了IoCcd的装配能力。Spring使用一个xml格式的应用配|文件ؓ(f)开发者提供了一U通过解析定制的属性文件来手动理单实例对象或者工厂对象的选择性。由于Spring非入R性做Z个重要的目标Q因此可以由Spring配置理的bean对象均不需要依赖Spring自有的接口和cd可以通过它们的bean属性完成配|?br> 实C来讲Spring采取了配|文件的形式来实C赖的注射Qƈ且支持Type2 IOCQSetter InjectionQ以?qing)Type3 IOCQConstructor InjectionQ?
在Model层,使用Spring提供的Setter Injection(type2)注入方式。以UserZQ下面是其用法?br> 在applicationContext- hibernate.xml中定?br> <bean id="userDAO" class="org.appfuse.dao.hibernate.UserDAOHibernate">
2 <property name="sessionFactory"><ref local="sessionFactory"/></property>
3 </bean>
4
public void setUserDAO(UserDAO dao);
q就是一个DAO Object讄Ҏ(gu)Q注器Q。UserDAO被调用Q和持久层通信。以q种方式创徏UserDAO的实例,同样辑ֈ了由UserManager创徏UserDao的目的。避免了直接实例化UserDAO的实现而业务层和持久层紧密耦合?br>
<bean id="userManager" parent="txProxyTemplate">
2 <property name="target">
3 <bean class="org.appfuse.service.impl.UserManagerImpl">
4 <property name="userDAO"><ref bean="userDAO"/></property>
5 </bean>
6 </property>
7 </bean>
8
private static ApplicationContext ctx = null;
2 public Object getBean(String name) {
3 if (ctx == null) {
4 ctx = WebApplicationContextUtils
5 .getRequiredWebApplicationContext(servlet.getServletContext());
6 }
7 return ctx.getBean(name);
8 }
9
UserManager mgr = (UserManager) getBean("userManager");
q样Q通过BeanFactory的getBeanҎ(gu)Q以?qing)xml配置文gQ避免了在UserActioncM直接实例化UserManagerQ消除了控制层与业务层及(qing)业务层与持久层之间的耦合Q实C依赖的注?br> ApplicationContext 是BeanFactory的子接口Qؓ(f)下列东西提供支持Q?
信息查找Q支持着国际?
事g机制Q允许发布应用对象以?qing)可选的注册以接收到事g
可移植的文g和资源访?
<bean id="txProxyTemplate" abstract="true"
2 class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
3 <property name="transactionManager"><ref bean="transactionManager"/></property>
4 <property name="transactionAttributes">
5 <props>
6 <prop key="save*">PROPAGATION_REQUIRED</prop>
7 <prop key="remove*">PROPAGATION_REQUIRED</prop>
8 <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
9 </props>
10 </property>
11 </bean>
12<property name="transactionAttributes">
2 <props>
3 <prop key="save*">PROPAGATION_REQUIRED</prop>
4 <prop key="remove*">PROPAGATION_REQUIRED</prop>
5 <prop key="*LoginCookie">PROPAGATION_REQUIRED</prop>
6 <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
7 </props>
8 </property>
9
3.3.3 SpringQHibernate操作持久?br> Spring对Hibernate有很好的支持?br> Hibernate中通过SessionFactory创徏和维护Session。Spring对SessionFactory的配|进行了整合Q无需再通过Hibernate.cfg.xml对SessionFactoryq行讑֮。SessionFactory节点的mappingResources属性包含了映射文g的\径,list节点下可配置多个映射文g。hibernateProperties节点则容U了所有的属性配|。可以对应传l的Hibernate.cfg.xml文gl构对这里的SessionFactory配置q行解读?br> 下面是HibernateSessionFactory ?HibernateTransactionManager的配|:(x)
在applicationContext-hibernate.xml中:(x)
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource"><ref bean="dataSource"/></property>
<property name="mappingResources">
<list>
<value>com/mycompany/model/User.hbm.xml</value>
……………………………
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">@HIBERNATE-DIALECT@</prop>
<!--prop key="hibernate.show_sql">true</prop-->
<!--prop key="hibernate.hbm2ddl.auto">update</prop-->
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>
User使用一个TransactionProxyFactoryBeanQ它定义了一个setTransactionManager()。能很方便的处理x的事物还有Service Object。TransactionProxyFactoryBean q有个setter. q会(x)被Business service objectQUserManagerQ引用, UserManager定义了业务层Qƈ且它q有个属性,由setUserDAO()引用?
public List getUsers(User user) {
return getHibernateTemplate().find("from User u order by upper(u.username)");
}
]]>
Action是用戯求和业务逻辑之间的桥梁,每个Action充当客户的一业务代理。主要完成以下Q务:(x)
1. 接收用户h?br> 2. Ҏ(gu)用户hQ调用合适的模型lg来执行相应的业务逻辑?br> 3. 获取业务逻辑执行l果
4. Ҏ(gu)当前状态以?qing)业务逻辑执行l果Q选择合适的视图lgq回l用戗?
org.apache.struts.actions.DispatchAction是org.apache.struts.action.Action的子cR?br> Action的execute()Ҏ(gu)是调用模型的业务Ҏ(gu)Q完成用戯求的业务逻辑Q然后根据执行结果把h转发l其它合适的Weblg。通常Q在一个ActioncM只能完成一U业务操作(通过execute()Ҏ(gu)Q?br> DispatchAction允许用户完成一个业务逻辑所需要的q箋动作和相兛_作集中于一个ActioncM。无覆盖execute()Ҏ(gu)Q而是可以创徏一些实现实际业务操作的Ҏ(gu)Q用户通过methodh参数指定所需要用的Ҏ(gu)?
BaseAction中实Cpȝ中Action子类需要用到的通用Ҏ(gu)Q主要有Q?br> 1. public Object getBean(String name):通过dSpring的applicationContext-service.xml配置文g来创建实例从而实?#8220;依赖注入”的方法?br> 2. public ActionMessages getMessages(HttpServletRequest request)Q初始化Struts的ActionMessagesQƈq回?br> 3. protected Object convert(Object o) QPOJO与FormBean之间的{换方法?br> 4. execute()Q置换了原始的execute()Ҏ(gu)。作用是Ҏ(gu)h的urlL相关的业务方法(没有methodh参数Ӟ(j)。例如请求的面是editUser.html,则执行edit()Ҏ(gu)?br> 5. protected ActionForm getActionForm(ActionMapping mapping, HttpServletRequest request)Q在mapping范围内得C个Action FormBean?br> 6. protected User getUser(HttpSession session) Q从Session中拿到当前登录用户信息?br> 7. protected void updateFormBean(ActionMapping mapping, HttpServletRequest request, ActionForm form)Q在指定的范围内更新当前Action 所对应的Form Bean对象?br> 8. protected void removeFormBean(ActionMapping mapping, HttpServletRequest request)Q去除无用的Form Bean?nbsp;
UserAction是用h据管理的控制器,提供了对用户数据的添加、修攏V删除、查询等操作的控Ӟ其包含的业务Ҏ(gu)有:(x)
1. add()Q进入添加用户页面?br> 2. cancel()Q当用户在表单中点击“取消”按钮Ӟ执行该方法。如果用h通过用户列表q入该表单,卛_前用于拥有管理其它用L(fng)权限Q返回到用户列表Q否则返回到主页面?br> 3. delete()Q根据请求的用户ID调用业务层的removeUser ()Ҏ(gu)删除相应的记录,q回到用户列表?br> 4. edit()Q首先校验用戯求的URL是否为editProfile.htmlQ若是,表示当前d用户要查看自q信息Q此时请求\径中不应该含有用户IDQ用户ID从Session中取得)(j)或用于标识请求是通过用户列表的from参数Q如果含有这两个参数Q发?#8220;无权?#8221;的错误信息。若不是Q表C当前用h通过用户列表q行h。根据请求的用户ID调用业务层的getUser ()Ҏ(gu)取得相应的记录。更新相应的FormBean?br> 5. save()Q当需要要d或修改用户信息时Q执行该Ҏ(gu)。首先校验请求参C是否?#8220;encryptPass”qgؓ(f)“true”Q若是,表示密码需要加密,随即密码加密。然后调用业务层的getRole ()Ҏ(gu)提交的用户权限持久化。调用业务层的saveUser ()Ҏ(gu)Q保存用户信息。如果用L(fng)辑的是自q信息Q更新Session中的当前d用户信息。如果用户在d旉择?#8220;C?#8221;Q更C存的Cookie。如果用L(fng)辑的是其它用L(fng)信息Q根据version判断是新L据还是修Ҏ(gu)据,在ActionMessages中存储不同的消息Q返回到d/修改用户信息面。若是新L据,调用sendNewUserEmail()l新用户发送一Email?br> 6. search()Q具有管理权限的用户q入用户列表时执行该Ҏ(gu)。调用业务层的getUsers ()Ҏ(gu)Q取得包含所有用户信息的List。返回到用户列表面?br> 7. unspecified()Q如果请求的url没有包含method参数Q通过BaseAction的execute()Ҏ(gu)也找不到指定的方法时Q执行该Ҏ(gu)。在该方法中转到search()Ҏ(gu)?br> 以上是很多Action需要用到的基本的方法?br> 8. sendNewUserEmail()Q根据FormBean中的内容l用户发送一Email?br> 9. checkForCookieLogin()Q如果用h通过CookiedQ用户在d旉择?#8220;C?#8221;Q,发出一个消息警告用户不能修改密码?/p>
AppFusel我们提供了一个通过xdoclet生成代码的工具——AppGenQ我们来看看它怎么用?br> POJOQPlain Old Java ObjectQ代表一个存储在持久性存储器Q如数据库)(j)中的实体的对象视图。它通过hbm.xml配置文g与存储器中的数据相映?br> 首先Q你需要有q个表的POJOQ放在src\dao\org\appfuse\model中?br> 在控制台中进入项目下的extras\appgen路径Q键入ant -Dmodel.name=XXX -Dmodel.name.lowercase=xxxQ第一个XXX处写你的POJOcdQ第二个xxx处写q个对象在小写时的名字。当控制台出现BUILD SUCCESSFULӞ代码q成了。代码在extras\appgen\build\gen文g夹下Q包括java源代码(DAO、Service、ActionQ、单元测试、JSP、资源文件、xml配置文g、源数据{,目录l构与原目一_(d)你只要把需要的目录拖到相应的项目根目录下就可以了,当然了,有的代码是需要在已有的文件上q行修改的,主要是属性文件和xml文gQ这旉要你把新的代码手动{Ud原来的文件中?br> AppGen也可以ؓ(f)你省去这个麻?ch),只要在前面的那段命o(h)中加一个installQ即ant install -Dmodel.name=XXX -Dmodel.name.lowercase=xxxQAppGen?x)直接把代码生成C的项目中去,包括修改已存在的文g。这时ؓ(f)了保险v见,最好先备䆾一下代码?
AppGen生成的文件缺了hbm.xml和Action Form BeanQ难道我们要自己写吗Q不是的。仔l察看build.xml文g׃(x)发现实际上我们下载的AppFuse最初的源代码中q没有hbm.xml、FormBeanQ甚臌web.xml、struts-config.xml、validation.xml都没有,q些文g都是在我们执行ant setup时build.xml使用xdoclet自动生成的?nbsp;甚至q数据库和表、表里的数据也可以自动生成。奥U都在POJO中?br> 查看User.javaq个POJO你会(x)发现里面除了java代码Q还有很多注释,有@hibernate开头的Q@struts开头的Q这些都是xdoctlet的标{。我们大致讲解一下:(x)
@struts.formQ用来生成Form BeanQ如User.java中写了@struts.form include-all="true" extends="BaseForm"Q意思是该POJO中每一个属性在Form Bean中都要有Qƈ且生成的Form Bean文g要承BaseForm。如果不希望生成的FormBean包含POJO的所有属性,可以在相关的属性getҎ(gu)前填写注释@struts.form-fieldQ意思是该属性要在FormBean中生成。那么如果我们要在FormBean中添加一些POJO没有的属性或Ҏ(gu)怎么做呢Q打开metadata\web文g夹,可以看到有一个xdoclet-UserForm.java文gQ里面是User.java在生成Form Bean时新加入的代码,我们同样可以把我们自己在FormBean中增加的代码新徏C个xdoclet-POJOFrom.java文g中就好了?br> @struts.validatorQ用来生成validation.xml文g。最常用的如@struts.validator type="required"Q意属性在表单中需要有必填的验证。当然前提是q个属性也在FormBean中。validationq有一w|是自定义的校验规则Q用正则表辑ּ表示。metadata\web中有一个文件validation-global.xmlQ里面就有邮~等规则的配|?br> @hibernateQ这个是比较复杂的,用来生成hbm.xml文g。其实内容与hbm.xml基本一_(d)所以如果会(x)写hbm.xmlQ这个也׃(x)写了。初学者可以先Q用工L(fng)成hbm.xmlQ再仿照生成的hbm.xml内容写这个标{。一旦标{ֆ好了Q不光可以生成hbm.xmlQbuild.xmlq可以自动在数据库中?br> @struts.action/@struts.action-forwardQPOJO不能生成struts-config.xmlQ这个标{是要写到Action中的。如果用AppGen生成ActionQ你?x)看到@struts.action已经写好了,q个标签的作用就是生成当前Action在struts-config.xml中的映射代码Q写法与struts-config.xml中一_(d)其实是把xml中的配置UL到Action中。还有一个问题,如果我们要在struts-config.xml中进行与Action无关的全局的配|,如global-forwards呢?览metadata\web文g夹你?x)发现里面有多个xml文gQ其中以global和struts开头的文g是写这些配|的。build.xml在setup时会(x)自动这些文件联合Action中的注释生成一个完整的struts-config.xml文g?br> 接下来还有web.xml文gQ你一定猜C同样需要在metadata\web中配|。除了刚才提到的那些xml文gQ剩下的基本都是用于配置web.xml的了。我们自定义的filter和listener是有具体cȝQ所以我们同样可以在q些filter和listenercMd注释Q具体例子参考AppFuse中的filter吧,很简单的?br> q行了所有配|后Q运行ant setupQbuild.xml?x)根据POJO、Action{自动生成FormBean、hbm.xml、struts-config.xml、validation.xml、web.xmlQƈ在数据库中创建相应的表。看h可能有点复杂Q但如无Ҏ(gu)要求Q对数据表的增、删、改、查Q只需要写好POJOQ一切都可以搞定?br>
3. Commonclipse
org.appfuse.model中有一个抽象类BaseObjectQ定义了三个抽象Ҏ(gu)QhashCode()、equals()和toString()Q其中前两个是Hibernate要求POJO必须实现的方法。所有的POJO都承自q个c,当然也要实现q三个方法,q个工具可以帮助你实现这三个Ҏ(gu)?br> 我们可以使用Eclipse在线获得q个工具。步骤:(x)依次点击"Help" -> "Software updates" -> "Find and Install" -> "Search for new features to install"Q点?add update site"Q输入http://commonclipse.sourceforge.netQOK?br> 接下来需要对Commonclipseq行一些配|。点?Window"->"Preferences"Q选择"Java"Q你?x)在菜单中看到Commonclipse。点击General标签Q将复选框全部钩掉Q全不选)(j)。点?OK"?br> 选中一个类Q右击,你会(x)在菜单中看到Commonclipse。选择要生成的Ҏ(gu)Q就q么单?/p>
1. ControllerQ控制器的作用是从客L(fng)接受hQƈ且选择执行相应的业务逻辑Q然后把响应l果送回到客L(fng)。Controller功能核心是一个Servletcd的对象ActionServletQ它用来接受客户端的h。ActionServlet包括一l基于配|的ActionMapping对象Q每个ActionMapping对象实现了一个请求到一个具体的Action控制器对象之间的映射。所有的Action对象都是从Struts的ActioncL生的子类。Action对象装了具体的控制逻辑Q调用Model层的业务逻辑模块Qƈ且把响应提交到合适的Viewlg以生响应。Struts提供的ActionFormlg对象为显C提供一个模型,可以通过定义属性描q客L(fng)表单数据。从它派生子cd象,利用它和Struts提供的自定义标记库结合可以实现对客户端的表单数据的良好封装和支持。通过ActionFormlg对象实现了对View和Model之间交互的支持。同Ӟ控制器会(x)负责处理异常?qing)UI验证
2. ModelQ代表的是应用的业务逻辑。Model分ؓ(f)两层Q持久层和业务层Q用q种模式底层数据访问操作与高层业务逻辑分离开。持久层使用ORM开源框Ӟ(x)Hibernate?Hibernate为java提供了OR持久化机制和查询服务。业务层框架使用Spring。Spring着手于“依赖注入”q样的概c(din)Spring把程序中所涉及(qing)到包含业务逻辑和Dao的Objects——例如transaction management handlerQ事物管理控Ӟ(j)、Object Factoris(对象工厂)、service objectsQ服务组Ӟ(j)——都通过XML来配|联pv来,降低了各个组件的耦合度?br> 3. ViewQView是应用的表示面,通过JSP技术实现。Struts提供了自定义的标记库可以使用Q通过q些自定义标记可以非常好地和pȝ的Model部分交互Q通过使用q些自定义标记创建的JSP表单Q可以实现和ActionForm的映,完成对用h据的装?br>
Action调用Model层方法时通过dSpring框架配置文g来创建实例。Spring框架控制M公共部分Q而把用户要用的部分定义了接口,然后用Spring框架调用q个接口Q实现回调。Model层分Z层:(x)业务层(ManagerQ和持久层(DAOQ,每一层又分接口和实现两部分。Action调用Manager层的接口QManager的Implement实现高层业务逻辑q调用DAO接口实现Ҏ(gu)据库的操作。这三层之间的调用均通过Spring?#8220;依赖注入”机制实现?/p>
1、JDK
2、Tomcat
3、Ant
4、Eclipse
5、MySql
6、AppFuse
前面5个这里就不废话了Q弄不到也就没必要接着往下看了。第6个的下蝲地址为:(x)https://appfuse.dev.java.net/q也是他的官方地址。E文牛的hQ可以参考里面的文档Q里面有很多指南。但是有些太|嗦Q而且弄得很?zhn)乎,觉得有些复杂?br>我下载的是appfuse-1.9.4-src.zipQ解压后Q可以从Eclipse直接导入?br>
W二步:(x)全部下蝲完成后,需要配|相关环境变量,大家可以Ҏ(gu)自己的习(fn)惯配|,我的配置情况如下Q?br>HOME
D:\AppConfig\Source
TOOLS_HOME
D:\AppConfig\Tools
MYSQL_HOME
%TOOLS_HOME%\mysql-5.0.24-win32
SDKS_HOME
D:\AppConfig\SDKs
ANT_HOME
%TOOLS_HOME%\apache-ant-1.7.0
CATALINA_HOME
%TOOLS_HOME%\apache-tomcat-5.5.17
JAVA_HOME
%SDKS_HOME%\jdk1.5.0_06
PATH
%JAVA_HOME%\bin;%ANT_HOME%\bin;%CATALINA_HOME%\bin;%MYSQL_HOME%\bin
看这个配|,应该很容易知道相关的目录l构?br>
W三步:(x)Eclipse相关配置
1、解压appfuse-1.9.4-src.zip文gQƈ复制appfuse\lib\junit3.8.1目录下的junit.jar文gQ粘贴到ant的lib目录下(也可以之后在Eclipse的Antq行环境中加入)(j)Q?br>2、打开ElipseQ依ơ找到Window——Preferences——Ant——Runtime——Classpath——Ant HomeQ点d从打开目录对话框中选定你的Ant的所在目录,我这里的目录是:(x)D:\AppConfig\Tools\apache-ant-1.7.0当然你也可以用Elipse~省的AntQ但是这里你要把appfuse\lib\junit3.8.1目录下的junit.jar文g从外部导入进来?br>3、将解压appfuse-1.9.4-src.zip后的文g从Elipse中导入进来,假设目名称为:(x)appfuse?br>4、打开Antq行H口Q导入appfuse下Buile.xmlq执行new指o(h)Q来创徏你的W一个AppFuse目Hello。要注意目名称不能使用"test"或者包?appfuse"或者以数字开头。然后按照向导提C完成相兌|就可以了?nbsp;q是在Eclipse下创Z的第一个AppFuse应用Q当?dng)如果你喜Ƣ,也可以在控制下完成同L(fng)动作Q因为已l在环境变量中加入了相关映射Q从而得在控制Cq行成ؓ(f)可能。你q需要添加支持Tomcat的jar包和属性文件。同样在Window → Preferences → Ant → Runtime中,点击Add External JARs按钮Q在Tomcat安装目录?server/lib中选择catalina-ant.jar。添加到classpath中。然后点击Properties书签Q点击Add Files按钮Q选择目Hello\lib\ant-contrib-1.0b1中的tomcatTasks.properties作ؓ(f)全局的属性文件?br>5、在Eclipse中将刚刚创徏的名为Hello的应用导入进来。他在你解压appfuse-1.9.4-src.zip后appfuse文g的同U目录下?nbsp;打开Antq行H口Q导入Hello下Buile.xmlq执行setup指o(h),如果你的MySql讄了密码的话,他会(x)提示出错Q新目的缺省密码ؓ(f)I。这时候你可以打开Hello下的properties.xml文gQ找?lt;property name="database.admin.username" value="root"/><property name="database.admin.password" value=""/>修改你的用户名和密码Q保存后重新q行setupQAnt?x)自动完成资料库及(qing)相关代码的创徏。运行start.tomcat启动Tomcat服务器。访问:(x)http://localhost:8080/Hello讉K你创建的W一个应用,看看AppFuse的效果吧Q?br>xQ第一个AppFuse应用完成?br>