昨天有同学问我问题,他告诉我他的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. 转:CentOS/Debian/Ubuntu一键安装LAMP(Apache/MySQL/PHP)环境

    CentOS/Debian/Ubuntu一键安装LAMP(Apache/MySQL/PHP) 今天遇到一个网友提到需要在Linux VPS服务器中安装LAMP(Apache/MySQL/PHP)网站环 ...

  2. Oracle Data Guard的配置

    概述 Oracle Data Guard 是针对企业数据库的最有效和最全面的数据可用性.数据保护和灾难恢复解决方案.它提供管理.监视和自动化软件基础架构来创建和维护一个或多个同步备用数据库,从而保护数 ...

  3. SQL总结二

    一.基础 1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 drop database dbname 3.说明:备份sql server --- ...

  4. jquery之clone()方法详解

    clone()函数用于克隆当前匹配元素集合的一个副本,并以jQuery对象的形式返回.你也可以简单地理解为:克隆当前jQuery对象. 你还可以指定是否复制这些匹配元素(甚至它们的子元素)的附加数据( ...

  5. ruby杂记

    ruby基本类中的方法:puts Object.private_instance_methods

  6. 初识cocos2d-x-从环境配置到整体框架

    前言 ACM生涯在带着些许遗憾中结束了.春招的时候找了一份游戏开发的工作,现在学习cocos2dx中. 从ACM竞赛到实际项目开发的学习,第一感觉就是不适应,虽然感觉实际项目的确要比ACM简单的多.最 ...

  7. IOS UIImage 模糊

    #import <UIKit/UIKit.h> #import <Accelerate/Accelerate.h> #import <QuartzCore/QuartzC ...

  8. GridView添加事件监听和常用属性解析

    1. 使用流程 graph LR 准备数据源-->新建适配器 新建适配器-->绑定数据源 绑定数据源-->加载适配器 2. 常用属性 android:columnWidth:每一列的 ...

  9. OPPO X9007 升级到Android5.0 Color2.1(root版) 详细纪实

    今天要做个测试,而测试APK刚好是要求最低5.0版本,正好手里有个老款手机OPPO X9007,而预装的系统是4.3,试了下虽然也能运行,但是主要功能不正常,毕竟人家APK最低要求摆在那. 反正这个手 ...

  10. SpringBoot + Spring Security 学习笔记(三)实现图片验证码认证

    整体实现逻辑 前端在登录页面时,自动从后台获取最新的验证码图片 服务器接收获取生成验证码请求,生成验证码和对应的图片,图片响应回前端,验证码保存一份到服务器的 session 中 前端用户登录时携带当 ...