首页 > 代码库 > 从源代码剖析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(使您的Struts2代码更加简洁——怎样培养框架设计能力
从源代码剖析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(使您的Struts2代码更加简洁——怎样培养框架设计能力
源代码文件出处:Web App Libraries/struts2-core-2.3.15.3.jar/struts-default.xml
拦截器modelDriven: <interceptorname="modelDriven"class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> |
拦截器params: <interceptorname="params"class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/> |
拦截器prepare: <interceptorname="prepare"class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/> |
拦截器栈: <interceptor-stackname="paramsPrepareParamsStack"> <interceptor-refname="exception"/> <interceptor-refname="alias"/> <interceptor-refname="i18n"/> <interceptor-refname="checkbox"/> <interceptor-refname="multiselect"/> <interceptor-refname="params"> <paramname="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param> </interceptor-ref> <interceptor-refname="servletConfig"/> <interceptor-refname="prepare"/> <interceptor-refname="chain"/> <interceptor-refname="modelDriven"/> <interceptor-refname="fileUpload"/> <interceptor-refname="staticParams"/> <interceptor-refname="actionMappingParams"/> <interceptor-refname="params"> <paramname="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param> </interceptor-ref> <interceptor-refname="conversionError"/> <interceptor-refname="validation"> <paramname="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-refname="workflow"> <paramname="excludeMethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack> |
ModelDriven拦截器的作用:
当用户触发每个请求时,ModelDriven拦截器将调用JavaBean对象的getModel()方法,并把返回值类型压入到ValueStack栈
Params拦截器的作用:
将表单的字段映射到ValueStack栈的栈顶对象的各个属性中,因为此时ValueStack栈的栈顶元素是刚被压入的模型(JavaBean)对象(先用到ModelDriven拦截器,才有这句话),所以该模型将被填充,如果每个字段在模型里没有匹配的属性,Params拦截器将尝试ValueStack栈中的下一个对象。
PrepareInterceptor拦截器的作用:
u 若Action实现Preparable接口,则Action方法需实现prepare()方法
u PrepareInterceptor拦截器将调用prepare()方法、prepareActionMethodName()方法和prepareDoActionMethodName()方法
u PrepareInterceptor拦截器根据firstCallPrepareDo属性决定获取prepareActionMethodName、prepareDoActionNam的顺序。默认情况下先获取prepareDoActionName(),如果没有该方法,就寻找prepareDoActionMethodName()。如果找到了对应的方法就调用该方法。
u PrepareInterceptor拦截器会根据alwaysInvokePrepare属性决定是否执行prepare()方法
paramsPrepareParamsStack拦截器栈的作用:(参考struts-default.xml配置文件的结构,就知道具体的含义了),现在详细解析一下:
u paramsPrepareParamsStack从字面上理解来说,这里Stack的拦截器调用的顺序为:首先params,然后prepare,接下来modelDriven,最后在params
u Struts2.0的设计上要求modelDriven在params之前调用,而业务中prepare要负责准备model,准备model又需要参数,这就需要在prepare之前运行params拦截器设置相关参数,这个也就是创建paramsPrepareParamsStack的原因。
u 流程如下:
A. Params拦截器首先给action中的相关参数赋值,如id
B. Prepare拦截器执行prepare方法,prepare方法中会根据参数,如id,去调用业务逻辑,设置model对象
C. ModelDriver拦截器将model对象压入ValueStack,这里的model对象就是在prepare中创建的
D. Params拦截器再将参数赋值给model对象
E. Action的业务逻辑执行
请参考下面源代码解析:(第一部分是PrepareInterceptor拦截器的操作流程)
package com.opensymphony.xwork2.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.Preparable; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory;
import java.lang.reflect.InvocationTargetException; public class PrepareInterceptor extends MethodFilterInterceptor {
private static final long serialVersionUID = -5216969014510719786L;
private final static String PREPARE_PREFIX ="prepare"; private final static String ALT_PREPARE_PREFIX ="prepareDo";
private boolean alwaysInvokePrepare = true; private boolean firstCallPrepareDo = false;
public void setAlwaysInvokePrepare(String alwaysInvokePrepare) { this.alwaysInvokePrepare = Boolean.parseBoolean(alwaysInvokePrepare); }
public void setFirstCallPrepareDo(String firstCallPrepareDo) { this.firstCallPrepareDo = Boolean.parseBoolean(firstCallPrepareDo); }
@Override public String doIntercept(ActionInvocation invocation)throws Exception { //获取Action对象 Object action = invocation.getAction(); //判断Action是否实现了preparable接口 if (actioninstanceof Preparable) { try { String[] prefixes; //根据当前拦截器的 firstCallPrepareDo(默认为 false)属性确定 prefixes if (firstCallPrepareDo) { prefixes = new String[] {ALT_PREPARE_PREFIX,PREPARE_PREFIX}; } else { prefixes = new String[] {PREPARE_PREFIX,ALT_PREPARE_PREFIX}; } //若为 false,则 prefixes: prepare, prepareDo //调用前缀方法. PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes); } catch (InvocationTargetException e) {
Throwable cause = e.getCause(); if (causeinstanceof Exception) { throw (Exception) cause; } elseif(cause instanceof Error) { throw (Error) cause; } else { throw e; } } //根据当前拦截器的 alwaysInvokePrepare(默认是 true)决定是否调用 Action的 prepare 方法 if (alwaysInvokePrepare) { ((Preparable) action).prepare(); } } return invocation.invoke(); } }
|
PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes) 方法: |
package com.opensymphony.xwork2.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class PrefixMethodInvocationUtil {
private static final Logger LOG = LoggerFactory.getLogger(PrefixMethodInvocationUtil.class);
private static final String DEFAULT_INVOCATION_METHODNAME ="execute";
private static final Class[] EMPTY_CLASS_ARRAY =new Class[0];
public static void invokePrefixMethod(ActionInvocation actionInvocation, String[] prefixes)throws InvocationTargetException, IllegalAccessException { //获取 Action实例 Object action = actionInvocation.getAction(); //获取要调用的 Action方法的名字(update) String methodName = actionInvocation.getProxy().getMethod();
if (methodName ==null) { // if null returns (possible according to the docs), use the default execute methodName = DEFAULT_INVOCATION_METHODNAME; } //获取前缀方法 Method method = getPrefixedMethod(prefixes, methodName, action); //若方法不为 null,则通过反射调用前缀方法 if (method !=null) { method.invoke(action, new Object[0]); } }
public static Method getPrefixedMethod(String[] prefixes, String methodName, Object action) { assert(prefixes !=null); //把方法的首字母变为大写 String capitalizedMethodName =capitalizeMethodName(methodName); //遍历前缀数组 for (String prefixe : prefixes) { String prefixedMethodName = prefixe + capitalizedMethodName; //通过拼接的方式,得到前缀方法名: 第一次 prepareUpdate,第二次prepareDoUpdate try { //利用反射获从 action中获取对应的方法, 若有直接返回. 并结束循环. return action.getClass().getMethod(prefixedMethodName,EMPTY_CLASS_ARRAY); } catch (NoSuchMethodException e) { // hmm -- OK, try next prefix if (LOG.isDebugEnabled()) { LOG.debug("cannot find method [#0] in action [#1]", prefixedMethodName, action.toString()); } } } return null; }
public static String capitalizeMethodName(String methodName) { assert(methodName !=null); return methodName.substring(0, 1).toUpperCase() + methodName.substring(1); }
}
|
第二部分(ModelDriver拦截器的源代码解析)
package com.opensymphony.xwork2.interceptor;
import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.ModelDriven; import com.opensymphony.xwork2.util.CompoundRoot; import com.opensymphony.xwork2.util.ValueStack;
public class ModelDrivenInterceptorextends AbstractInterceptor {
protected boolean refreshModelBeforeResult =false;
public void setRefreshModelBeforeResult(boolean val) { this.refreshModelBeforeResult = val; }
@Override public String intercept(ActionInvocation invocation)throws Exception { //获取 Action对象: EmployeeAction对象, 此时该 Action 已经实现了 ModelDriven 接口 //public class EmployeeAction implements RequestAware, ModelDriven<Employee> Object action = invocation.getAction(); //判断 action是否是 ModelDriven的实例 if (actioninstanceof ModelDriven) { //强制转换为 ModelDriven类型 ModelDriven modelDriven = (ModelDriven) action; //获取值栈 ValueStack stack = invocation.getStack(); //调用 ModelDriven接口的 getModel()方法 //即调用 EmployeeAction的 getModel()方法 /* public Employee getModel() { employee = new Employee(); return employee; } */ Object model = modelDriven.getModel(); if (model != null) { //把 getModel()方法的返回值压入到值栈的栈顶.实际压入的是 EmployeeAction的 employee 成员变量 stack.push(model); } if (refreshModelBeforeResult) { invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model)); } } return invocation.invoke(); }
/** * Refreshes the model instance on the value stack, if it has changed */ protected static class RefreshModelBeforeResultimplements PreResultListener { private ObjectoriginalModel = null; protected ModelDrivenaction;
public RefreshModelBeforeResult(ModelDriven action, Object model) { this.originalModel = model; this.action = action; }
public void beforeResult(ActionInvocation invocation, String resultCode) { ValueStack stack = invocation.getStack(); CompoundRoot root = stack.getRoot();
boolean needsRefresh =true; Object newModel = action.getModel();
// Check to see if the new model instance is already on the stack for (Object item : root) { if (item.equals(newModel)) { needsRefresh = false; } }
// Add the new model on the stack if (needsRefresh) {
// Clear off the old model instance if (originalModel !=null) { root.remove(originalModel); } if (newModel !=null) { stack.push(newModel); } } } } }
|
细节注释:
细节一:执行 ParametersInterceptor 的 intercept 方法: 把请求参数的值赋给栈顶对象对应的属性. 若栈顶对象没有对应的属性, 则查询值栈中下一个对象对应的属性...
细节二:getModel 方法不能提供以下实现. 的确会返回一个 Employee 对象到值栈的栈顶. 但当前 Action 的 employee 成员变量却是 null.
@Override public Employee getModel() { returnnew Employee(); } |
从源代码剖析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(使您的Struts2代码更加简洁——怎样培养框架设计能力