昨天有同学问我问题,他告诉我他的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. Your awesome titleHH

    Welcome to Jekyll! Your awesome titleHH About Blogging Like a Hacker Welcome to Jekyll! Jan 9, 2016 ...

  2. 30天,O2O速成攻略【8.30南京站】

    活动概况 时间:2015年8月30日13:30-16:30 地点:啡咖啡·孵化器(南京市玄武大道699-22号江苏软件园22栋) 主办:APICloud.Udesk.人为峰 网址:www.apiclo ...

  3. Mysql授权GRANT ALL PRIVILEGES

    1. 改表法. 可能是你的帐号不允许从远程登陆,只能在localhost.这个时候只要在localhost的那台电脑,登入mysql后,更改 "mysql" 数据库里的 " ...

  4. codeforces 392A Blocked Points

    我的方式是用暴力的方法找到每一行每一列的边界点: 但是有大神直接用一个公式解决了:floor(n*sqrt(2))*4: 想了很久还是不理解,求各路大神指点! #include<iostream ...

  5. MCI 函数与命令

    Microsoft 提供的 MMSYSTEM.H 文件中定义了调用 MCI 功能的数据类型和函数原型.在使用 MCI 功能的任何源模块中都应包含该文件. 1. MCI 函数 所有的 MCI 函数名都以 ...

  6. Junit4学习(一)新建Junit4工程

    一,学习Junit4,学以致用 二,熟悉编写流程 工具:Eclipse,Junit包,hamcrest.core包 1,打开Eclipse开发工具,新建工程:file->Java Project ...

  7. WeTest----如何使用WeTest进行App性能测试?

    使用Wetest可以测试手机app的性能,wetest主打游戏app测试,但是对于其余的app仍然适用,手机可以root,也可在非root的情况下进行测试, 此时可以获取的性能数据包括:FPS.整机C ...

  8. 20155324王鸣宇 《网络对抗技术》Web基础

    20155324王鸣宇 <网络对抗技术>Web基础 实践要求 ①Web前端HTML: 能正常安装.启停Apache.理解HTML,理解表单,理解GET与POST方法,编写一个含有表单的HT ...

  9. vue整理

    安装 vue ui axios import axios from 'axios' // let curWwwPath = window.document.location.href // let p ...

  10. 【C语言】符号优先级

    一. 问题的引出 今天看阿里的笔试题,看到一个非常有意思的题目,但是很容易出错. 题目:如下函数,在32bit系统foo(2^31-3)的值是: Int foo(int x) { return x&a ...