拦截器栈paramsPrepareParamsStack概述
1. 拦截器栈paramsPrepareParamsStack中三个重点的拦截器params,prepare和modelDriven。
params:com.opensymphony.xwork2.interceptor.ParametersInterceptor
prepare:com.opensymphony.xwork2.interceptor.PrepareInterceptor
modelDriven: com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor
2. paramsPrepareParamsStack拦截器栈的主流程为:
params->prepare->modelDriven->params
此流程为:
第一步params[参数]拦截器把表单数据注入到栈顶的Action对象中;
第二步prepare[准备]拦截器为modelDriven[模型驱动]拦截器准备向ValueStack[值栈]栈顶添加的对象,此对象作为第一步中Action对象的属性;
第三部modelDriven[模型驱动]拦截器把prepare[准备]拦截器创建的对象压入ValueStack[值栈]栈顶;
第四部params[参数]拦截器把表单数据注入到栈顶的Action的一个属性对象中;
通过以上的流程,Struts2可以简洁轻便并且高性能的完成业务的增删改查操作。
拦截器栈paramsPrepareParamsStack使用
下面,通过一个员工管理的案例来深入了解该拦截器栈的使用:
本案例通过四个版本不断优化,最终成为一个相对完美的解决方案。案例的处理难点是表单数据的提交与回填。 下面依次说明各个版本的实现方式和存在的缺点。
版本一:
实现方式:表单数据提交是把模型的属性直接添加到Action类中完成,表单回填测试在Action类中把数据写入request对象,在JSP页面中使用s:push标签将数据取出压入值栈完成回填;
存在缺点:Action类中加入模型类的属性以及get和set方法,造成代码大量冗余;
版本二:
实现方式:在版本一的基础上,直接办模型类作为Action类的属性,并把JSP页面中attr改为object.attr;
存在缺点:JSP页面获取数据代码不简洁,原来id变成employee.id;
版本三:
实现方式:使用拦截器栈paramsPrepareParamsStack,由系统自动把数据添加到值栈栈顶,从而简洁的完成数据映射;
存在缺点:执行每次请求到EmployeeAction执行view,add,edit,delete,create,update任意一个方法时,都会执行prepare方法。 其中,view,add,delete是不需要执行prepare方法的。特别是执行delete方法会执行"dao.selectEmployee(id);"代码浪费数据库资源。 view,add方法会产生不需要的对象,浪费内存。
解决办法:要解决资源浪费的问题,就需要程序能够做到在需要资源的时候执行prepare方法。幸运的是,struts2已经为此准备好了。 其解决办法是为每一个action方法提供了一个私人订制的prepare方法。其名称为prepare加原方法名首字符大写。因此,可以在此方法中完成必要的资源准备。 对于不使用的prepare方法,可以在struts.xml中配置使其不再被系统调用。
版本四:
实现方式:在版本三的基础上,禁用通用的prepare方法,使用每个方法自己的prepare方法。
拦截器栈paramsPrepareParamsStack详解
ParametersInterceptor拦截器
作用:把action请求参数注入到值栈栈顶对象的对应属性中,没有对应属性则忽略。
源码:
@Override public String doIntercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); if (!(action instanceof NoParameters)) { ActionContext ac = invocation.getInvocationContext(); //1. 获取请求参数 final Map<String, Object> parameters = retrieveParameters(ac); if (LOG.isDebugEnabled()) { LOG.debug("Setting params " + getParameterLogMap(parameters)); } if (parameters != null) { Map<String, Object> contextMap = ac.getContextMap(); try { ReflectionContextState.setCreatingNullObjects(contextMap, true); ReflectionContextState.setDenyMethodExecution(contextMap, true); ReflectionContextState.setReportingConversionErrors(contextMap, true); //2. 获取值栈 ValueStack stack = ac.getValueStack(); //3. 向值栈栈顶对象注入参数 setParameters(action, stack, parameters); } finally { ReflectionContextState.setCreatingNullObjects(contextMap, false); ReflectionContextState.setDenyMethodExecution(contextMap, false); ReflectionContextState.setReportingConversionErrors(contextMap, false); } } } return invocation.invoke(); } protected void setParameters(final Object action, ValueStack stack, final Map<String, Object> parameters) { Map<String, Object> params; Map<String, Object> acceptableParameters; if (ordered) { params = new TreeMap<String, Object>(getOrderedComparator()); acceptableParameters = new TreeMap<String, Object>(getOrderedComparator()); params.putAll(parameters); } else { params = new TreeMap<String, Object>(parameters); acceptableParameters = new TreeMap<String, Object>(); } //3.1 过滤可接受的参数 for (Map.Entry<String, Object> entry : params.entrySet()) { String name = entry.getKey(); if (isAcceptableParameter(name, action)) { acceptableParameters.put(name, entry.getValue()); } } //3.2 基于目前值栈生成一个新的值栈 ValueStack newStack = valueStackFactory.createValueStack(stack); boolean clearableStack = newStack instanceof ClearableValueStack; if (clearableStack) { //if the stack's context can be cleared, do that to prevent OGNL //from having access to objects in the stack, see XW-641 ((ClearableValueStack)newStack).clearContextValues(); Map<String, Object> context = newStack.getContext(); ReflectionContextState.setCreatingNullObjects(context, true); ReflectionContextState.setDenyMethodExecution(context, true); ReflectionContextState.setReportingConversionErrors(context, true); //keep locale from original context context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE)); } boolean memberAccessStack = newStack instanceof MemberAccessValueStack; if (memberAccessStack) { //block or allow access to properties //see WW-2761 for more details MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack; accessValueStack.setAcceptProperties(acceptParams); accessValueStack.setExcludeProperties(excludeParams); if (action instanceof ParameterNameAware) accessValueStack.setPropertiesJudge(new PropertiesJudge() { public boolean acceptProperty(String propertyName) { return ((ParameterNameAware) action).acceptableParameterName(propertyName); } }); } //3.3 遍历将每个参数注入值栈栈顶属性中 for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); try { //3.4 将参数注入值栈栈顶属性中 newStack.setParameter(name, value); } catch (RuntimeException e) { if (devMode) { String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{ "Unexpected Exception caught setting '" + name + "' on '" + action.getClass() + ": " + e.getMessage() }); LOG.error(developerNotification, e); if (action instanceof ValidationAware) { ((ValidationAware) action).addActionMessage(developerNotification); } } } } if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null)) stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS)); addParametersToContext(ActionContext.getContext(), acceptableParameters); }
PrepareInterceptor拦截器
作用:为ModelDrivenInterceptor拦截器准备/创建准备压入值栈栈顶的对象。
源码:
@Override public String doIntercept(ActionInvocation invocation) throws Exception { //1. 获取请求action Object action = invocation.getAction(); //2. 判断请求Action是否实现Preparable接口 if (action instanceof Preparable) { try { String[] prefixes; //3. 判断先执行目标方法的prepareDoXxx方法还是prepareXxx,这两个方法只执行一个,默认执行prepareXxx方法 if (firstCallPrepareDo) { prefixes = new String[] {ALT_PREPARE_PREFIX, PREPARE_PREFIX}; } else { prefixes = new String[] {PREPARE_PREFIX, ALT_PREPARE_PREFIX}; } PrefixMethodInvocationUtil.invokePrefixMethod(invocation, prefixes); } catch (InvocationTargetException e) { /* * The invoked method threw an exception and reflection wrapped it * in an InvocationTargetException. * If possible re-throw the original exception so that normal * exception handling will take place. */ Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if(cause instanceof Error) { throw (Error) cause; } else { /* * The cause is not an Exception or Error (must be Throwable) so * just re-throw the wrapped exception. */ throw e; } } //4. 判断是否执行action类实现的prepare方法,默认执行 if (alwaysInvokePrepare) { ((Preparable) action).prepare(); } } return invocation.invoke(); }
配置:可以通过以下配置改变此拦截器中和属性的默认值,其他方式可以查阅struts2文档。
<package name="employee-manage-v4" extends="struts-default"> <!-- 配置使用 paramsPrepareParamsStack 作为默认的拦截器栈 --> <!-- 修改 PrepareInterceptor 拦截器的 alwaysInvokePrepare 属性值为 false,Struts2将不执行Preparable接口的prepare方法 --> <interceptors> <interceptor-stack name="rabbitx-paramsPrepareParamsStack"> <interceptor-ref name="paramsPrepareParamsStack"> <!--使用拦截器在struts.xml中配置的name的值加点号再加属性值的方式设置属性的值--> <param name="prepare.alwaysInvokePrepare">false</param> <param name="prepare.firstCallPrepareDo">true</param> </interceptor-ref> </interceptor-stack> </interceptors> <default-interceptor-ref name="rabbitx-paramsPrepareParamsStack"/> <action name="employee-v4-*" class="org.rabbitx.web.struts2.paramsPrepareParamsStack.action.v4.EmployeeAction" method="{1}"> <result name="{1}">/v4/{1}.jsp</result> <result name="success" type="redirectAction">employee-v4-view</result> </action> </package>
ModelDrivenInterceptor拦截器
作用:把PrepareInterceptor拦截器准备好的或其他方式准备好的对象压入值栈栈顶。
源码:
@Override public String intercept(ActionInvocation invocation) throws Exception { //1. 获取请求Action对象 Object action = invocation.getAction(); //2. 判断Action对象是否实现ModelDriven接口 if (action instanceof ModelDriven) { ModelDriven modelDriven = (ModelDriven) action; //3. 获取值栈 ValueStack stack = invocation.getStack(); //4. 获取Action对象中创建好的对象; Object model = modelDriven.getModel(); if (model != null) { //5. 把对象压入值栈栈顶 stack.push(model); } if (refreshModelBeforeResult) { invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model)); } } return invocation.invoke(); }
拦截器栈paramsPrepareParamsStack执行流程
下面以员工管理案例中修改用户信息为例分析拦截器栈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>
01) 用户点击编辑按钮发送employee-v4-edit.action?id=10001请求;
02) params拦截器把请求参数id:10001注入到EmployeeAction对象的属性id中;
03) prepare拦截器调用EmployeeAction的edit对应的prepare方法perpareEdit,此方法根据id获取Employee对象并赋值给EmployeeAction的employee属性;
04) modelDriven拦截器把赋值好的employee对象压入值栈栈顶;
05) params拦截器把请求参数id:10001注入到employee对象的id属性中(此步骤无意义);
06) 拦截器执行完毕;
07) 执行对应action[EmployeeAction]方法edit;
08) 返回edit.jsp,页面根据栈顶对象employee的属性值回填表单;
到此,一个完整的paramsPrepareParamsStack拦截器栈执行流程完毕。
相关推荐
这里`myStack`是一个拦截器栈,包含`paramsPrepareParamsStack`(Struts2默认的参数处理拦截器栈)和`myCustomInterceptor`(自定义拦截器)。 - **Action配置**:在Action的配置中,通过`<action>`标签的`...
`paramsPrepareParamsStack`是Struts2默认的拦截器栈,包含了参数处理和准备动作等步骤。通过`<param>`标签,我们可以自定义拦截器的行为,比如`prepare.alwaysInvokePrepare`被设置为`false`,意味着只有当Action类...
这个特性主要依赖于Struts2的OGNL(Object-Graph Navigation Language)表达式语言和Struts2的核心拦截器栈。OGNL是一种强大的、直观的语言,用于访问和操作对象及其属性。在Struts2中,它用于从HTTP请求中提取参数并...
视频10、20和21深入讲解了Preparable拦截器、paramsPrepareParamsStack拦截器栈以及自定义拦截器的实现和应用,例如防止表单重复提交。 5. **类型转换**:Struts 2自动处理请求参数到Action属性的类型转换。视频11...
在SSH2框架中,拦截器(Interceptor)是非常重要的组件之一,它可以用来增强Action的功能,比如权限检查、日志记录等。为了更好地利用Spring的依赖注入功能,可以通过以下步骤在拦截器中注入Spring管理的对象: 1. ...
3. 配置Struts2的拦截器栈。默认情况下,Struts2的`default-stack`已经包含了`paramsPrepareParamsStack`,但是为了处理文件上传,需要添加`fileUpload`拦截器。在struts.xml中,你可以这样配置: ```xml ...
Struts2分页技术是Java Web开发中一种常见的...Struts2提供了`params`拦截器,用于从请求中获取参数,`prepare`拦截器则负责在Action执行之前初始化Action的属性。在`struts.xml`配置文件中,可以这样配置: ```xml ...
4. **Struts2拦截器**:Struts2的DefaultActionInvocation拦截器链中的`ParamsPrepareParamsStack`包含了一个`ParamsInterceptor`,它可以处理multipart请求并填充Action的属性。 5. **文件存储**:上传的文件需要...
在Java Web应用中,特别是使用Struts2框架时,拦截器扮演着至关重要的角色。下面我们将深入探讨如何实现并使用Java自定义拦截器。 首先,我们来看`LoginInterceptor`类,它是基于Struts2框架的自定义拦截器。`...
Struts2的拦截器机制允许在Action执行前后插入自定义逻辑,如日志记录、权限验证、数据验证等。CCValidate可能就是一个拦截器,负责在Action执行前对用户输入进行检查。 9. **最佳实践**: 对于安全性,应使用预...
在struts.xml或struts-default.xml配置文件中,需要添加拦截器链,其中包括`paramsPrepareParamsStack`,它包含了`i18n`拦截器,该拦截器负责从session中获取用户的locale并加载相应的资源文件。 6. **使用资源...
同时,需要在Action配置中添加`<interceptor-ref name="paramsPrepareParamsStack"/>`拦截器。 #### 六、总结 通过以上介绍,我们可以看到Struts2提供的验证机制非常强大且灵活。通过合理的配置和使用,开发者可以...
在Struts2中,可以使用拦截器(Interceptors)来增强安全性,例如使用`paramsPrepareParamsStack`预处理用户输入。 以上就是基于Struts2的登录系统的基本实现原理和关键点。通过这个项目,开发者可以学习到如何在...
Struts2的核心配置文件,用于定义Action、拦截器、结果类型等。以下是`struts.xml`的部分内容: ```xml <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" ...
在Struts2配置文件(struts.xml)中,你需要启用文件上传拦截器(`paramsPrepareParamsStack`)。这个拦截器栈会处理上传请求,并将文件内容转换为Action类中的属性,以便后续处理。 单个文件上传通常涉及到一个...
同时,为了实现Struts2与Hibernate的结合,可能需要配置拦截器,如`paramsPrepareParamsStack`,它可以帮助处理请求参数。 5. **编写DAO层**:Data Access Object层负责与数据库交互。这里可以创建接口和实现类,...