【Java EE 学习第69天】【struts2】【paramsPrepareParamsStack拦截器栈解决model对象和属性赋值冲突问题】

昨天有同学问我问题,他告诉我他的Action中的一个属性明明提供了get/set方法,但是在方法中却获取不到表单中传递过来的值。代码如下(简化后的代码)

 1 public class UserAction implements modelDriven<User>(){
 2       private String name;
 3       private User model;
 4       public void setName(String name){
 5             this.name=name;
 6       }
 7       public String getName(){
 8             return this.name;
 9       }
10       public User getModel(){
11             this.model=model;
12       }
13
14       public String saveUser() throws Exception{
15             System.out.println(this.name);//输出null
16             System.out.println(this.model.getName());//正常输出表单中提交的值
17             return "toUserInfoPage";
18       };
19 }

  经过尝试,我们使用两种方法解决了这个问题,

    1.将模型驱动接口直接去掉,这样在目标方法中直接使用System.out.pritln(this.name);可以正常获取到值

    2.将拦截器栈改换成paramsPrepareParamsStack拦截器栈也可以解决掉这个问题,这个时候同时使用this.model和通过属性this.name都可以正常获取到值。

这只是尝试,但是到底为什么会这样我们也不清楚,但是肯定是默认拦截器和paramsPrepareParamsStack拦截器的差异造成的。

默认的拦截器栈:defaultStack

 1 <!-- A complete stack with all the common interceptors in place.
 2                  Generally, this stack should be the one you use, though it
 3                  may do more than you need. Also, the ordering can be
 4                  switched around (ex: if you wish to have your servlet-related
 5                  objects applied before prepare() is called, you‘d need to move
 6                  servletConfig interceptor up.
 7
 8                  This stack also excludes from the normal validation and workflow
 9                  the method names input, back, and cancel. These typically are
10                  associated with requests that should not be validated.
11                  -->
12             <interceptor-stack name="defaultStack">
13                 <interceptor-ref name="exception"/>
14                 <interceptor-ref name="alias"/>
15                 <interceptor-ref name="servletConfig"/>
16                 <interceptor-ref name="i18n"/>
17                 <interceptor-ref name="prepare"/>
18                 <interceptor-ref name="chain"/>
19                 <interceptor-ref name="debugging"/>
20                 <interceptor-ref name="scopedModelDriven"/>
21                 <interceptor-ref name="modelDriven"/>
22                 <interceptor-ref name="fileUpload"/>
23                 <interceptor-ref name="checkbox"/>
24                 <interceptor-ref name="multiselect"/>
25                 <interceptor-ref name="staticParams"/>
26                 <interceptor-ref name="actionMappingParams"/>
27                 <interceptor-ref name="params">
28                   <param name="excludeParams">dojo\..*,^struts\..*</param>
29                 </interceptor-ref>
30                 <interceptor-ref name="conversionError"/>
31                 <interceptor-ref name="validation">
32                     <param name="excludeMethods">input,back,cancel,browse</param>
33                 </interceptor-ref>
34                 <interceptor-ref name="workflow">
35                     <param name="excludeMethods">input,back,cancel,browse</param>
36                 </interceptor-ref>
37             </interceptor-stack>

paramsPrepareParamsStack拦截器栈:

 1 <!-- An example of the paramsPrepareParams trick. This stack
 2                  is exactly the same as the defaultStack, except that it
 3                  includes one extra interceptor before the prepare interceptor:
 4                  the params interceptor.
 5
 6                  This is useful for when you wish to apply parameters directly
 7                  to an object that you wish to load externally (such as a DAO
 8                  or database or service layer), but can‘t load that object
 9                  until at least the ID parameter has been loaded. By loading
10                  the parameters twice, you can retrieve the object in the
11                  prepare() method, allowing the second params interceptor to
12                  apply the values on the object. -->
13             <interceptor-stack name="paramsPrepareParamsStack">
14                 <interceptor-ref name="exception"/>
15                 <interceptor-ref name="alias"/>
16                 <interceptor-ref name="i18n"/>
17                 <interceptor-ref name="checkbox"/>
18                 <interceptor-ref name="multiselect"/>
19                 <interceptor-ref name="params">
20                     <param name="excludeParams">dojo\..*,^struts\..*</param>
21                 </interceptor-ref>
22                 <interceptor-ref name="servletConfig"/>
23                 <interceptor-ref name="prepare"/>
24                 <interceptor-ref name="chain"/>
25                 <interceptor-ref name="modelDriven"/>
26                 <interceptor-ref name="fileUpload"/>
27                 <interceptor-ref name="staticParams"/>
28                 <interceptor-ref name="actionMappingParams"/>
29                 <interceptor-ref name="params">
30                     <param name="excludeParams">dojo\..*,^struts\..*</param>
31                 </interceptor-ref>
32                 <interceptor-ref name="conversionError"/>
33                 <interceptor-ref name="validation">
34                     <param name="excludeMethods">input,back,cancel,browse</param>
35                 </interceptor-ref>
36                 <interceptor-ref name="workflow">
37                     <param name="excludeMethods">input,back,cancel,browse</param>
38                 </interceptor-ref>
39             </interceptor-stack>

由于是paramsPrepareParamsStack拦截器栈解决了问题,所以这里着重看paramsPrepareParamsStack拦截器栈,亮点在前面的注释部分:

This stack is exactly the same as the defaultStack, except that it includes one extra interceptor before the prepare interceptor:the params interceptor.

  这句英文意思简单明确,翻译过来大体意思就是paramsPrepareParamsStack拦截器栈和defaultStack拦截器栈相比只有一点不同:paramsPrepareParamsStack拦截器栈在params拦截器之前增加了params拦截器。

  简单回顾一下struts2的工作流程,首先当一次Action请求发生的时候,首先在DefaultActionInvocation类中的init方法中会创建Action对象并压栈,接着会执行设置的拦截器栈,执行完毕所有的拦截器之后会执行Action中的目标方法,最后执行结果集。

  这里获取参数的过程是在目标方法中完成的,这里获取不到参数是因为在拦截器栈中param拦截器的放置位置不对造成的,如果该拦截器放置到模型驱动拦截器之后则会发生以上问题的冲突;如果将该拦截器放置到模型驱动拦截器之前则不会发生上述的问题(模型驱动拦截器之后也放置相同的一个)

  接着看paramsPrepareParamsStack拦截器栈前面的注释描述:

This is useful for when you wish to apply parameters directly  to an object that you wish to load externally (such as a DAO or database or service layer), but can‘t load that object until at least the ID parameter has been loaded. By loading the parameters twice, you can retrieve the object in the prepare() method, allowing the second params interceptor to  apply the values on the object. 

  这段英文的意思并不难理解,翻译过来的意思就是:当你想给Action中的一个对象赋值的时候必须得获取该对象的唯一标识id,这样才能通过DAO层或者Service层或者查找数据库获取该对象,所以直到你能够获取到该id的值你都不能加载该对象;通过加载两次param拦截器,你能够在prepare方法中获取该对象,并且能够在第二个param拦截器中将所有的属性值赋值给该对象。

  我觉得这段话漏掉了一个重要的拦截器说明,那就是模型驱动拦截器,模型驱动拦截器的作用只有一个:调用Action对象的getModel方法并将获取到的值压栈。然后在param拦截器中对getModel获取到的model对象属性赋值,参数值都是从前端页面中获取到的,比如表单或者Ajax请求等;这样在Action的目标方法中使用Model对象的时候就有值了;既然涉及到了prepare方法的问题了,那么肯定还关系到了PrepareInterceptor拦截器。

  通过以上的分析,可以得到以下的工作流程:使用paramsPrepareParamsStack拦截器栈是有一定的时机的,使用paramsPrepareParamsStack拦截器的时候Action一定实现了接口Preparable(反之则不一定),并且有prepare[[Do]MethodName]方法,实现的拦截器是PrepareInterceptor拦截器;为了使得PrepareInterceptor拦截器能够正常工作,ParametersInterceptor拦截器必须提供某些参数值(如id),这个时候就给Action中的属性赋值了;DefaultActionInvocation执行完成PrepareInterceptor拦截器之后(可能做一些赋值的工作,比如为Action中的对象属性赋值(利用ParametersInterceptor拦截器提供的参数值),之前的一个项目就使用这种方法解决了Action中的模型赋值的问题),执行到了ModelDrivenInterceptor拦截器,该拦截器将model对象压栈;接着又有一个ParametersInterceptor拦截器,该拦截器的作用不再是为Action中的属性赋值,而是为model对象中的属性赋值,执行完所有的拦截器之后会执行Action中的目标方法,最后执行结果集。

  总的来说,虽然使用paramsPrepareParamsStack拦截器栈解决了之前的那个问题,但是该拦截器栈的设计本意并不是这样,Action中的属性赋值只是其作用中的一个环节,其余的需要使用PrepareInterceptor、ModelDrivenInterceptor以及后面的又一个ParametersInterceptor共同完成。

  没想到一个莫名其妙的问题背后竟然有这么多奇妙的东西,看来还需要更加努力才行啊~

时间: 2024-12-18 12:18:23

【Java EE 学习第69天】【struts2】【paramsPrepareParamsStack拦截器栈解决model对象和属性赋值冲突问题】的相关文章

[原创]java WEB学习笔记6:Struts2 学习之路--Struts的CRUD操作( 查看 / 删除/ 添加) 使用 paramsPrepareParamsStack 重构代码 ,PrepareInterceptor拦截器,paramsPrepareParamsStack 拦截器栈

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

从源代码剖析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(使您的Struts2代码更加简洁——怎样培养框架设计能力

源代码文件出处:Web App Libraries/struts2-core-2.3.15.3.jar/struts-default.xml 拦截器modelDriven: <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> 拦截器params: <interceptor name="par

调查管理系统 -(6)自定义Struts2的拦截器&amp;自定义UserAware接口&amp;Action中模型赋值问题&amp;Hibernate懒加载问题

1.对于一些功能,如我的调查或新建调查等,只有用户登录后才能进行操作,因此必须对用户是否登录进行判断.当用户登录后才能使用相应的功能,如果没有登录则需为用户导航到登录页面让其进行登录.这个功能可以通过自定义Struts2的拦截器来完成. 2.当用户登录之后,由于是将用户的信息保存在session中的.这样当一些Action中需要用到当前登录的用户的信息时需要手动的从session中获取,不太方便,因此我们声明了一个UserAware接口(即用户关注,类似于Struts2中的SessionAwar

使用 paramsPrepareParamsStack 拦截器栈后的运行流程

2. 使用 paramsPrepareParamsStack 拦截器栈后的运行流程 1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈. 而 struts-default 包默认使用的是defaultStack 2). 可以在 Struts 配置文件中通过以下方式修改使用的默认的拦截器栈 <default-interceptor-ref name="paramsPrepareParamsStack"></defau

Struts2默认拦截器栈及内建拦截器使用详解

Struts2内建拦截器介绍: alias (别名拦截器):允许参数在跨越多个请求时使用不同别名,该拦截器可将多个Action采用不同名字链接起来,然后用于处理同一信息. autowiring (自动装配拦截器):主要用于当Struts2和Spring整合时,Struts2可以使用自动装配的方式来访问Spring容器中的Bean. chain (链拦截器):构建一个Action链,使当前Action可以访问前一个Action的属性,一般和<result type="chain"

【Java EE 学习第69天】【数据采集系统第一天】【实体类分析和Base类书写】

之前SSH框架已经搭建完毕,现在进行实体类的分析和Base类的书写.Base类是抽象类,专门用于继承. 一.实体类关系分析 既然是数据采集系统,首先调查实体(Survey)是一定要有的,一个调查有多个页面(Page),一个页面有多个问题(Question),所以还要有页面和问题实体.参与完成调查之后一定还会生成若干个答案,所以还有答案实体(Answer),当然还有参与的用户(User),管理员是特殊的User,只需要登陆的时候进行判断即可. 分析实体类型是比较简单的,最重要的是设计,怎样设计才能

[原创]java WEB学习笔记75:Struts2 学习之路-- 总结 和 目录

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

【跟我一步一步学Struts2】——拦截器

前言 前面提到过拦截器.而且说拦截器仅仅能拦截Action.这里拦截器的调用体现了责任链模式.为什么说体现了责任链模式呢? 以下的一段话说的非常明确: Struts2将整个运行划分成若干同样类型的元素,每一个元素具备不同的逻辑责任,并将它们纳入到一个链式的数据结构中.而每一个元素又有责任负责链式结构中下一个元素的运行调用. 从代码重构的角度来看,实际上是将一个复杂的系统,分而治之.从而使得每一个部分的逻辑可以高度重用并具备高度可扩展性. 拦截器的原理 Struts2框架的拦截器是动态配置的,假设

Java EE学习--Quartz基本用法

新浪博客完全不适合写技术类文章.本来是想找一个技术性的博客发发自己最近学的东西,发现博客园起源于咱江苏,一个非常质朴的网站,行,咱要养成好习惯,以后没事多总结总结经验吧.很多时候都在网上搜索别人的总结,我自己也总结些东西,或许多多少少能帮得上别人. 首先提到的是Quartz,一个开源的定期执行计划任务的框架.其实我内心好奇这个框架很久了,像那些能定时修改数据库数据,定时分配任务的功能一直觉得很神奇.心动不如行动,今天我就小小的学习了一下用法,力求言简意赅,大家都懂的我就不说了. 第一步:下载Qu