【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);能够正常获取到值app

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

这只是尝试,可是到底为何会这样咱们也不清楚,可是确定是默认拦截器和paramsPrepareParamsStack拦截器的差别形成的。spa

 

默认的拦截器栈: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拦截器栈:debug

 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拦截器栈在prepare拦截器以前增长了params拦截器。code

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

  这里获取参数的过程是在目标方法中完成的,这里获取不到参数是由于在拦截器栈中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共同完成。

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

相关文章
相关标签/搜索