动作类实现了 Preparable,ModelDriven 这两个接口,但是却无法在其方法 prapare 和 getModel 方法里获得 其它参数,进而无法根据其它参数来查询数据库。
在 prapare,getModel 这两个方法都断点了,显示执行顺序为 prapare——》 getModel,但是其它参数(如 categoryId (有对应的get / set 方法)JSP 页面上已确定没有写错。
一、业务场景:
自己想要的结果是:先根据 参数 categoryId 来查询数据库,
public void prepare() throws Exception {
// 前端传递了参数,表示 需要 查看详细属性
if( categoryId !=null ) {
//查询数据库 该类对应的 Java 类名,因为 不同种类的属性差异很大,必须获得 对应的 类
String detailsClassName = categoryService.getDetailsClassName(categoryId);
// 安全起见:只允许加载 com.jian.vo 包下的 类
if(detailsClassName !=null && detailsClassName.startsWith("com.jian.vo") ) {
try {
itemInfo = Class.forName(detailsClassName).newInstance();
logger.info("顾客需要浏览商品详情:" + itemInfo.getClass().getName() );
} catch (InstantiationException | IllegalAccessException
| ClassNotFoundException e) {
logger.error("没有对应的 VO 类:" + detailsClassName );
e.printStackTrace();
}
}
}else {
itemInfo = new ItemBaseInfoVO();
}
}
@Override
public Object getModel() {
// 让模型 去接收 前端传递过来的 参数,最后保存这个模型即可
return itemInfo;
}
但是 categoryId 的值却为 null,这就导致 查询数据库失败,后者无法查到正确值。
。
二、分析:
(1)因为方法执行顺序为:prapare——》getModel,并且参数没有获得,所以拦截器执行的执行顺序为:prapare——》modelDriven——》params。
默认的拦截器栈为 defaultStack( 因为 struts2 配置文件里 的 package 设置 extends="struts-default" ,在 struts-core.jar 包下的 struts-default.xml 配置文件里有 设置
<default-interceptor-ref name="defaultStack"/> )。
defaultStack 中拦截器的 设置信息为:
<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="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\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</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-ref name="debugging"/>
</interceptor-stack>
这里验证了 拦截器的先后顺序为:prepare——》modelDriven——》params,所以方法的先后顺序为:prapare——》getModel——》setXXX(属性),所以 categoryId 为空值
(2)解决之道:
调整 拦截器的先后顺序:params——》prepare——》modelDrive,去 struts-core.jar 里的 struts-default.xml 搜索 符合这要求得 拦截器执行栈,(*^__^*) 嘻嘻……还真发现了有这一执行栈: paramsPrepareParamsStack
<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\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</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\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</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>
最后在 自个的 struts.xml 配置文件里的 引用这个 执行栈: paramsPrepareParamsStack
<struts>
<package name="normal" namespace="/" extends="struts-default">
<!-- 因为需要根据 参数来查询数据库,所以不再使用 defaultStack
原因是拦截器执行顺序为:prepare—》modelDriven—》params
参数还不能获取,params拦截器被排在了后面
所以改用其他拦截器栈,执行顺序改为:params—》prepare—》modelDriven
<default-interceptor-ref name="paramsPrepareParamsStack"/>
-->
<!-- 没有匹配的动作时,默认跳转到首页 -->
<default-action-ref name="homePage" />
<action name="homePage" method="welcome" class="homeAction">
<result name="success" >/jsp/home/HomeLayout.jsp</result>
</action>
最后:
感知到了 Struts2 的优良设计,把一些功能模块划分成拦截器,也渐渐理解了 责任链模式的 解耦合,谢谢Struts2 作者 的无私奉献!!!