spring mvc深入解析Word格式文档下载.docx
《spring mvc深入解析Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《spring mvc深入解析Word格式文档下载.docx(10页珍藏版)》请在冰豆网上搜索。
/bean>
beanid=\”secure_editAccount\”
另外,还有一个AOPadvice,SendOrderConfirmationEmailAdvice,用于在完成一条order的数据库插入之后,向用户发送一封确认邮件,相应的配置位于applicationContext.xml中。
beanid=\”mailSender\”
class=\”org.springframework.mail.javamail.JavaMailSenderImpl\”>
propertyname=\”host\”>
${mail.host}<
beanid=\”emailAdvice\”
class=\”org.springframework.samples.jpetstore.domain.logic.SendOrderConfirmationEmailAdvice\”>
propertyname=\”mailSender\”>
reflocal=\”mailSender\”/>
beanid=\”emailAdvisor\”
class=\”org.springframework.aop.support.RegexpMethodPointcutAdvisor\”>
constructor-arg>
reflocal=\”emailAdvice\”/>
/constructor-arg>
propertyname=\”pattern\”>
.*insertOrder.*<
在另一个包org.springframework.samples.jpetstore.domain中,包含了在整个JPetStore各个层中都会使用到的domainobject。
这些基本的model类,除了Cart,在数据访问层均有对应的Dao类,而由于Cart仅作为在Web层内部传递的model,因此不需要持久化。
SpringJPetStore学习小结
(2)
发表于3/29/2005由siteadmin
※数据访问层
在这一层里,所有的Dao类由于使用了ibatis,所以代码显得格外干净。
实际上,绝大部分逻辑都被搬到了外部的sqlmap文件里,这些文件位于org.springframework.samples.jpetstore.dao.ibatis.maps包下。
值得一提的是,JPetStore对于Sequence的处理。
JPetStore专门定义了一个Sequence的sqlmap,里面分别针对普通情况和Oracle数据库做了单独配置。
通常情况下是在数据库中单独维护一张代表sequence的表,使用时首先利用“getSequence”获取对应name的nextid,然后代码实现累加1,完成后再利用“updateSequence”更新sequence表。
以下是Sequence.xml中的相关定义:
selectid=\”getSequence\”resultMap=\”result\”>
selectname,nextidfromsequencewherename=#name#
/select>
updateid=\”updateSequence\”>
updatesequencesetnextid=#nextId#wherename=#name#
/update>
在SqlMapSequenceDao中的getNextId方法实现了上述的代码处理逻辑:
publicintgetNextId(Stringname)throwsDataAccessException{
Sequencesequence=newSequence(name,-1);
sequence=
(Sequence)getSqlMapClientTemplate().queryForObject(\”getSequence\”,sequence);
//…
ObjectparameterObject=newSequence(name,sequence.getNextId()+1);
getSqlMapClientTemplate().update(\”updateSequence\”,parameterObject,1);
returnsequence.getNextId();
}
而对于Oracle,则直接利用其sequence功能,相应的sqlmap定义如下:
selectid=\”oracleSequence\”resultMap=\”result\”>
select\’$name$\’asname,$name$.nextvalasnextidfromdual
另有一个OracleSequenceDao实现,其getNextId方法如下:
Sequencesequence=newSequence();
sequence.setName(name);
sequence=(Sequence)getSqlMapClientTemplate().queryForObject(\”oracleSequence\”,sequence);
包括SqlMapSequenceDao在内的所有Dao类都使用了SqlMapClientDaoSupport作为基类,同时,多数Dao类实现相应的Dao接口。
Dao实现类中会调用SqlMapClientDaoSupport提供了getSqlMapClientTemplate方法。
这样就可以很方便的从sqlmap文件中读取sql配置,而无需将sql语句hardcode到代码中了。
spring为ibatis提供了一层简单的封装,然后将自底层抛出的异常转义为spring框架所定义的runtime异常DataAccessException,可以在上层视情况决定是否catch该异常。
所有的Dao接口都位于org.springframework.samples.jpetstore.dao包中,而所有的ibatis实现类都位于org.springframework.samples.jpetstore.dao.ibatis包中。
SpringJPetStore学习小结(3)
发表于3/31/2005由siteadmin
※Web层
Web层是SpringJPetStore相对最为复杂的地方。
SpringJPetStore同时支持了两种WebFramework:
Struts和SpringWebMVC,分别位于org.springframework.samples.jpetstore.web.struts和org.springframework.samples.jpetstore.web.spring这两个包内。
两者的切换只需要修改一下web.xml文件的相应配置即可。
如果选择SpringWebMVC,则:
servlet>
servlet-name>
petstore<
/servlet-name>
servlet-class>
org.springframework.web.servlet.DispatcherServlet<
/servlet-class>
load-on-startup>
2<
/load-on-startup>
/servlet>
DispatcherServlet是SpringWebMVC中负责请求调度的核心引擎,通过内嵌的<
init-param>
节点可以为其配置名为“contextConfigLocation”的
参数,该参数指定了Spring专属的ApplicationContext配置文件的位置。
如果忽略此设定,则默认为“/WEB-INF/<
servletname>
-servlet.xml”,其中<
以此处的Servlet名替换(比如:
petstore-servlet.xml)
在org.springframework.samples.jpetstore.web.spring包中,绝大部分类属于Controller,它们均维护了一个PetStoreFacade的实例变量,通过PetStoreFacade来访问业务层的功能。
利用Spring的配置文件petstore-servlets.xml,可以将PetStoreFacade的实现类(PetStoreImpl)通过referencebean的方式“注入”到各个Controller中。
这些Controller几乎都实现自org.springframework.web.servlet.mvc.Controller接口,该接口只有一个抽象方法:
ModelAndViewhandleRequest(HttpServletRequestrequest,HttpServletResponseresponse);
handleRequest的处理逻辑大致是:
-从request中获取信息(并没有什么特别的,一切数据都是通过request以及session在众多jsp页面和controller间传递的)
-调用相应的petStore业务方法(处理Cart相关的Controller例外)
-装配并返回ModelAndView实例
-由于可以在handleRequest中直接操纵response,因此另一种返回形式是调用reponse.sendRedirect
从中可以看出,这是与ServletAPI紧密耦合的。
SpringJPetStore学习小结(4)
发表于4/1/2005由siteadmin
※Web层(续)
在众多Controller当中,有两个Controller比较特殊,它们涉及表单处理,分别是OrderFormController和AccountFormController,前者派生自AbstractWizardFormController,后者派生自SimpleFormController,而它们的父类则同为AbstractFormController。
SpringWebMVC对一般的Form处理,从流程(workflow)的角度做了分类(formworkflow),并以抽象类的形式封装与框架代码中。
像OrderFormController和AccountFormController即是从这些抽象类中扩展派生而来,它们对部分callback方法做了覆盖。
这是一个典型的TemplateMethodPattern的运用。
当然,这也使得Web层的应用代码严重依赖于SpringWebMVC框架本身。
以SimpleFormController为例,从其核心方法之一processFormSubmission的实现代码中可以看出大致的处理流程:
protectedModelAndViewprocessFormSubmission(
HttpServletRequestrequest,HttpServletResponseresponse,
Objectcommand,BindExceptionerrors)
throwsException{
if(errors.hasErrors()||isFormChangeRequest(request)){
returnshowForm(request,response,errors);
}
else{
returnonSubmit(request,response,command,errors);
在表单验证之后,如果用户输入有误,或者表单需要再次刷新,则会重新导向当前表单页面,否则说明一切正常,调用onSubmit方法。
onSubmit最终会调用doSubmitAction方法,然后调用getSuccessView获得后继视图的标识,配合errors.getModel(),组装成ModelAndView返回。
而这里的doSubmitAction则是一个需要派生类覆盖的protected方法。
与上述Controller配套的还有两个FormObject:
OrderForm,AccountForm,其所包含的实例变量对应于表单字段。
另一个值得一提的是拦截器SignOnInterceptor,它对部分用户操作实施了认证保护。
代码实现逻辑大致如下:
publicbooleanpreHandle(
HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)
UserSessionuserSession=
(UserSession)WebUtils.getSessionAttribute(request,\”userSession\”);
if(userSession==null){
ModelAndViewmodelAndView=newModelAndView(\”SignonForm\”);
thrownewModelAndViewDefiningException(modelAndView);
returntrue;
这里的UserSession是一个封装了包括帐号在内的用户信息的helperclass。
它被当作session属性在Web层的不同对象之间传递。
在petstore-servlets.xml文件中有如下一段配置信息:
beanid=\”secureHandlerMapping\”
class=\”org.springframework.web.servlet.handler.SimpleUrlHandlerMapping\”>
<
propertyname=\”interceptors\”>
<
list>
<
refbean=\”signonInterceptor\”/>
/list>
propertyname=\”urlMap\”>
map>
entrykey=\”/shop/editAccount.do\”>
reflocal=\”secure_editAccount\”/>
/entry>
…
/map>
beanid=\”signonInterceptor\”
class=\”org.springframework.samples.jpetstore.web.spring.SignonInterceptor\”/>
class=\”org.springframework.samples.jpetstore.web.spring.AccountFormController\”>
…
由此,我们可以看到,SignonInterceptor的preHandle方法,将会在AccountFormController执行之前被调用。
而当userSession为空时,则会被当作非法操作抛出ModelAndViewDefiningException异常,否则AccountFormController将会顺利执行。
至于如何处理ModelAndViewDefiningException,则要“上溯”到SpringWebMVC的核心请求处理类:
org.springframework.web.servlet.DispatcherServlet。
在该Servlet的doDispatch方法中有如下一段代码:
try{
}
catch(ModelAndViewDefiningExceptionex){
mv=ex.getModelAndView();
if(mv!
=null&
&
!
mv.isEmpty()){
render(mv,processedRequest,response);
由于ModelAndViewDefiningException中包含了出错以后的后继视图标识,因此最终将会导向该视图(在本例中,它导向用户登录页面)。
SpringJPetStore学习小结(5)
发表于4/4/2005由siteadmin
※Web层(续2)——分页机制
在SpringJPetStore中,为列表提供了简单的分页处理,这里使用了Spring的helperclass:
PagedListHolder。
它虽是用于WebUI,不过实际上是针对beanlist的维护,因此位于org.springframework.beans.support包中。
在Web层中,PagedListHolder的实例会被当作session属性传递,然后在jsp页面中以model获取。
PagedListHolderitemList=newPagedListHolder(this.petStore.getItemListByProduct(productId));
itemList.setPageSize(4);
Productproduct=this.petStore.getProduct(productId);
request.getSession().setAttribute(\”ViewProductAction_itemList\”,itemList);
request.getSession().setAttribute(\”ViewProductAction_product\”,product);
model.put(\”itemList\”,itemList);
model.put(\”product\”,product);
在PageListHolder中提供了很多分页时经常用到的方法:
isFirstPage、isLastPage、previousPage、nextPage、getPageList等。
通过设置其成员变量sort,PageListHolder也支持排序,该成员变量是一个SortDefinition接口的实现类,缺省使用的是MutableSortDefinition。
实际上MutableSortDefinition只不过维护了有关排序的一些状态信息,比如:
以那个属性排序、降序还是升序、是否忽略大小写等。
而真正的排序则是由PropertyComparator完成的,该类实现了java.util.Comparator接口,用于根据指定的beanproperty来比较两个bean的先后顺序。
在PageListHolder的resort方法中调用了PropertyComparator的静态方法sort。
PagedHolderList提供了对不可更新的beanlist的分页支持,如果需要处理可更新的beanlist,可以使用RefreshablePagedListHolder。
RefreshablePagedListHolder是PagedListHolder的子类,具备reloading功能。
调用其refresh方法,能够根据Locale和filter的更新情况自动实现数据的reloading。
为了让RefreshablePagedListHolder能够成功reload数据,我们还需要编写一个PagedListSourceProvider接口的实现类,因为RefreshablePagedListHolder会调用该接口的loadList方法。
Spring的org.springframework.beans.support包为Web分页机制提供了统一的解决方案,使用起来十分方便。
不过,需要指出的是,使用PagedHolderList/RefreshablePagedHolderList的隐含前提是,你需要将后台数据表中的所有数据悉数全部取出来,然后再交由它们进行分页处理。
SpringJPe