struts2作为封装前后台模型数据的优良框架,会发生一些如在模型在Action中被偷梁换柱而显示不了正确的模型数据的错误。
首先我们来分析一下Struts2模型数据在从前台到后台再到前台的流程。如下:
第一步:Action实例初始化,在执行完StrutsPrepareAndExecuteFilter后,Struts2开始通过配置文件生成目标Action的代理对象ActionProxy,同时将Action实例压入值栈,同时根据模型数据生成对应实例model
第二步:开始执行ActionInvocation类的interceptor()方法一步一步执行拦截器中的方法,当执行到ModelDrivenInterceptor拦截器后,通过getModel方法的返回值将model对象的引用压入值栈,此时值栈的栈顶元素是根据前台数据得到的model
第三步:执行action的目标方法,调用service中的方法得到新的模型数据,然后赋给model对象,此时model不在指向原始的模型数据,而指向新的模型数据model,这样栈顶元素仍然是旧的模型数据,在前台使用OGNL表达式显示的时候仍然是旧的数据,而不是从数据库中获取的新数据。
那么如何让前台页面显示新数据呢
有如下解决方法:
1),手动压入值栈 valueStack.push(model).
这种简单粗暴,但是并不好有几个原因:一,Struts2标榜耦合度低的特点,这样手动操作Struts2的后置对象,会增加耦合度。
二,valueStack是Struts2后台默默运行的机制,最好不要自己操作,值栈是数据传递的中心,其作用就是希望使用的编程人员更加方便的使用数据的传递封装。三,栈中的模型数据过多
2),属性复制的方式,将新模型的数据一个个赋值给就模型
但是这样很麻烦,通过get,set一个个设置,虽然可以使用BeanUtil工具包,这样就需要额外映入jar包,还要通过反射,性能较低
3),通过prepare拦截器,Struts2默认拦截器defaultStack,在ModelDriven拦截器前面还有一个Prepare拦截器,在每个目标方法调用prepare()方法,也可以说是为每个目标方法准备模型数据,通过prepareXxxx()可以再制定目标方法前调用,可以再这里面替换模型数据,然后再待用ModelDriven拦截器时压入值栈的就是新模型。但是这样就接受不了前台传递的参数,需要使用另外一个拦截器栈paramsPrepareParamsStack,参数准备参数拦截器栈执行顺序:params—>prepare—>modeldriven—>params。缺点就是一个目标方法前就要增加一个prepare方法,会造成方法的爆炸。
4)使用刷新机制,进行赋值。
还是使用defaultStack默认栈,通过查看模型驱动拦截器源码我们发现其有一个属性refreshModelBeforeResult,在结果之前刷新属性,其默认false,我们看其实现机制
在执行intercept方法时,如果refreshModelBeforeResult是true,会给model增加一个监听,从action获取模型数据,然后遍历值栈,如果发现栈顶元素不是newModel则取出旧数据放入新模型。这是最好的实现解决方法。