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

 public class UserAction implements modelDriven<User>(){
private String name;
private User model;
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public User getModel(){
this.model=model;
} public String saveUser() throws Exception{
System.out.println(this.name);//输出null
System.out.println(this.model.getName());//正常输出表单中提交的值
return "toUserInfoPage";
};
}

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

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

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

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

默认的拦截器栈:defaultStack

 <!-- A complete stack with all the common interceptors in place.
Generally, this stack should be the one you use, though it
may do more than you need. Also, the ordering can be
switched around (ex: if you wish to have your servlet-related
objects applied before prepare() is called, you'd need to move
servletConfig interceptor up. This stack also excludes from the normal validation and workflow
the method names input, back, and cancel. These typically are
associated with requests that should not be validated.
-->
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*</param>
</interceptor-ref>

<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>

paramsPrepareParamsStack拦截器栈:

 <!-- An example of the paramsPrepareParams trick. This stack
is exactly the same as the defaultStack, except that it
includes one extra interceptor before the prepare interceptor:
the params interceptor. 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. -->
<interceptor-stack name="paramsPrepareParamsStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*</param>
</interceptor-ref>

<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*</param>
</interceptor-ref>

<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</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拦截器栈在prepare拦截器之前增加了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共同完成。

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

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

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

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

  2. struts2 paramsPrepareParamsStack拦截器简化代码(源码分析)

    目录 一.在讲 paramsPrepareParamsStack 之前,先看一个增删改查的例子. 1. Dao.java准备数据和提供增删改查 2. Employee.java 为model 3. E ...

  3. 从源代码分析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(让你的Struts2代码更简洁——如何培养框架设计能力

    源代码文件:Web App Libraries/struts2-core-2.3.15.3.jar/struts-default.xml 拦截器modelDriven: <interceptor ...

  4. 第十篇——Struts2的拦截器栈

    拦截器栈: 从结构上看:拦截器栈相当于多个拦截器的组合: 从功能上看:拦截器栈也是拦截器. 默认拦截器栈: 在struts-core.jar包中的struts-default.xml中自定义了一个de ...

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

    2. 使用 paramsPrepareParamsStack 拦截器栈后的运行流程 1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈. 而 st ...

  6. 【Java EE 学习 70 上】【数据采集系统第二天】【数据加密处理】【登陆验证】【登陆拦截器】【新建调查】【查询调查】

    一.数据加密处理 这里使用MD5加密处理,使用java中自带加密工具类MessageDigest. 该类有一个方法digest,该方法输入参数是一个字符串返回值是一个长度为16的字节数组.最关键的是需 ...

  7. 【Java EE 学习 72 上】【数据采集系统第四天】【增加调查logo】【文件上传】【动态错误页指定】【上传限制】【国际化】

    增加logo的技术点:文件上传,国际化 文件上传的功能在struts2中是使用文件上传拦截器完成的. 1.首先需要在页面上添加一个文件上传的超链接. 点击该超链接能够跳转到文件上传页面.我给该表单页面 ...

  8. Struts2默认拦截器栈及内建拦截器使用具体解释

    Struts2内建拦截器介绍:   alias (别名拦截器):同意參数在跨越多个请求时使用不同别名,该拦截器可将多个Action採用不同名字链接起来,然后用于处理同一信息.  autowiring  ...

  9. 【Java EE 学习 35 上】【strus2】【类型转换器】【struts2和Servlet API解耦】【国际化问题】【资源文件乱码问题已经解决】

    一.类型转换器 1.在动作类action中,声明和表单中name属性的值同名的属性,提供get和set方法,struts2就可以通过反射机制,从页面中获取对应的内容 package com.kdyzm ...

随机推荐

  1. ubuntu安装/卸载mysql

    1.安装mysql root@openstack001:/tmp# apt-get install mysql-server Reading package lists... Done Buildin ...

  2. Xcode 8 支持 iOS 7 真机解决过程记录

    领导要求不放弃iOS 7 用户,所以我们Xcode 8 上面支持ios 7 必须要解决! 解决方法(过程): 1.应用程序--Xcode(原来的Xcode 7)-- 显示包内容--Contents-- ...

  3. Mysql基础(二)

    学习路线:数据约束-> 数据库的设计过程-> 存储过程的相关知识-> 触发器-> 权限管理 (一)数据约束 1.1.默认值的设置 创建员工表emp 将默认地址设置为'中国'my ...

  4. eclipse新建web项目开发JSP

    1.创建项目:file---new--Dynamic Web Project 一直选next,到jsp文件目录所在地,打勾默认自动生成web.xml配置文件,也可以自己设置. 创建JSP文件: 选择创 ...

  5. HTML5 canvas学习笔记(一)

    canvas是HTML5中新增的标签,下面是各浏览器的支持程度: canvas的默认大小为:宽-300px,高-150px(注意:画布的大小作为canvas标签的行内样式设置,而且是没有“px”单位的 ...

  6. Sql Server中查询今天、昨天、本周、上周、本月、上月数据

    Sql Server中查询今天.昨天.本周.上周.本月.上月数据 在做Sql Server开发的时候有时需要获取表中今天.昨天.本周.上周.本月.上月等数据,这时候就需要使用DATEDIFF()函数及 ...

  7. mac 下设置jdk 路径,设置hadoop 路径

    1. touch ~/.bash_profile  创建一个文件 2.vim ~/.bash_profile JAVA_HOME=/Library/Java/JavaVirtualMachines/j ...

  8. ORA-01033 ORACLE 正在初始化或关闭

    (借鉴:该方法本人亲自操作过,解决了问题!) 解决连接ORACLE错误一例:ORA-01033: ORACLE 正在初始化或关闭 客户发现连接数据库有问题,错误有: ORA-01033: ORACLE ...

  9. Meterpreter run vnc 遇到的问题

    Metasploit框架中的meterpreter无疑是相当强大的工具,而且具有我目前挺喜欢的vnc.但是我在run vnc时发现得到的远程控制桌面是view-only的,通过-h选项发现没有修改的方 ...

  10. C#在二维码中添加圆角logo

    public class QRCodeHelper { #region 合并用户QR图片和用户头像 /// <summary> /// 合并用户QR图片和用户头像 /// </sum ...