struts2中接收非基本类型的对象时必须有setter

今天重构以前代码过程中发现一个struts2设置参数的问题,前后足足追了半天,顺便看了OGNL的源码,在这里记录下原因和解决方案:

 

在struts2的action中接收参数时,通常我是不喜欢写getter,只写setter。这样可以更加清楚的表示这是一个输入参数,比如

 

public class XXAction{
     private int size;
     public viod setSize(int size){
         this.size=size;     
      }

}

 

 

当只接收普通类型时没有问题,可对于如下复杂类型

 

public class PageBean{
         int pageSize;
         int pageNo;
         String orderBy;

         // getter &setter
         // ...

}

 

 

   如果不写getter

 

public class XXAction{
       private PageBean page;
       public void setPage(PageBean page){
              this.page=page;
       }
}

  

    前端传过来的参数 page.pageSize=10&page.pageNo=1&page.orderBy=name

    在execute()方法中会发现page对象只有 pageSize被赋值了(10),pageNo和orderBy都为null。而且无论怎么调整传入参数的顺序结果都是一样的。

 

    这个问题让我很费解,网上找了半天也没找到答案,老老实实的去跟OGNL的源码,最后发现原因是这样的:

 

    1) OGNL 首先收集所有需要赋值的属性( page.pageSize, page.pageNo, page.orderBy)

    2) 如果是复杂对象(如这里的PageBean),OGNL并不是首先构造好这个PageBean,赋上所有的值(pageSize,pageNo,orderBy),然后再把整个对象赋给action.page;而是通过一个循环依次往action.page这个对象上追加值。循环中每次执行时并不知道action.page对象是否已经执行过初始化,所以每次都要用以下方法测试action.page是否存在,若不存在则会初始化它 (action.page=new PageBean()), 然后完成本次赋值

 

          

if (OgnlRuntime.hasSetProperty(ognlContext, o, name)) {
                    OgnlRuntime.setProperty(ognlContext, o, name, value);

                    return;
                }

 

           由于我没有在Action中对page属性写setter,所以.hasSetPropertyf方法每次都会返回false,然后OGNL就会傻傻的重新初始化并赋值, 对于以上三个属性,它一共会初始化page对象三遍,而只保留下最后赋值的属性pageSize。

           至于为什么每次都是pageSize排在最后呢? 这是因为OGNL在步骤1)中收集对象时用的是 TreeMap, 而前段穿过来的这三个key(page.pageSize, page.pageNo, page.orderBy) 按照String.compareTo()的实现总是page.pageSize排在最后一个。

 

 

        知道了原因,解决方法当然很简单,只要加上 page属性的setter就行了。不过这里还是要对Struts的设计吐个槽,如果是我来设计,输入参数肯定不会要求实现者提供getter,而是用一个Map保存对象的中间状态,最后统一调用setter赋给action。 现在这种拿action当临时对象用的方法看起来简化了一些实现,但作为框架来说,强制要求使用者提供getter完全是过分的要求。

 

        

 

 

     

你可能感兴趣的:(java,前端)