源代码文件:Web App Libraries/struts2-core-2.3.15.3.jar/struts-default.xml

拦截器modelDriven

<interceptor
name="modelDriven"
class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>

拦截器params

<interceptor
name="params"
class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>

拦截器prepare:

<interceptor
name="prepare"
class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>

拦截器栈:

<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>

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 (action
instanceof 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 (cause
instanceof Exception) {

throw (Exception) cause;

} else
if
(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
ModelDrivenInterceptor
extends 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 (action
instanceof 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
RefreshModelBeforeResult
implements PreResultListener {

private Object
originalModel = null;

protected ModelDriven
action;

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() {

return
new
Employee();

}

版权声明:本文博客原创文章,博客,未经同意,不得转载。

从源代码分析modelDriven拦截器和params拦截器和拦截器prepare 和paramsPrepareParamsStack拦截器栈(让你的Struts2代码更简洁——如何培养框架设计能力的更多相关文章

  1. Android AsyncTask 源代码分析

    AsyncTask源代码分析 public abstract class AsyncTask<Params, Progress, Result> { //日志TAG private sta ...

  2. struts2 paramsPrepareParamsStack拦截器简化代码(源码分析)

    目录 一.在讲 paramsPrepareParamsStack 之前,先看一个增删改查的例子. 1. Dao.java准备数据和提供增删改查 2. Employee.java 为model 3. E ...

  3. SDL2源代码分析6:复制到渲染器(SDL_RenderCopy())

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

  4. SDL2源代码分析3:渲染器(SDL_Renderer)

    ===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...

  5. XBMC源代码分析 7:视频播放器(dvdplayer)-输入流(以libRTMP为例)

    前文分析了XBMC的基本结构: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 XBMC源代码分 ...

  6. XBMC源代码分析 6:视频播放器(dvdplayer)-文件头(以ffmpeg为例)

    XBMC分析系列文章: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 XBMC源代码分析 4: ...

  7. XBMC源代码分析 4:视频播放器(dvdplayer)-解码器(以ffmpeg为例)

    XBMC分析系列文章: XBMC源代码分析 1:整体结构以及编译方法 XBMC源代码分析 2:Addons(皮肤Skin) XBMC源代码分析 3:核心部分(core)-综述 本文我们分析XBMC中视 ...

  8. 使用 paramsPrepareParamsStack 拦截器栈后的运行流程

    2. 使用 paramsPrepareParamsStack 拦截器栈后的运行流程 1). paramsPrepareParamsStack 和 defaultStack 一样都是拦截器栈. 而 st ...

  9. 【Java EE 学习 69 上】【struts2】【paramsPrepareParamsStack拦截器栈解决model对象和属性赋值冲突问题】

    昨天有同学问我问题,他告诉我他的Action中的一个属性明明提供了get/set方法,但是在方法中却获取不到表单中传递过来的值.代码如下(简化后的代码) public class UserAction ...

随机推荐

  1. margin 还能够被缩回

    <p><strong>话:</strong>的肥沃和收获而被估价的.才干也是土地,只是它生产的不是粮食,而是真理.假设仅仅能滋生瞑想和幻想的话,即使再大的才干也仅仅 ...

  2. Deploy 11.2.0.3 RAC+DG on Windows 2008 R2 Step by Step

    环境规划: 节点1: tc1 192.168.56.101 内存:2G 节点2: tc2 192.168.56.102 内存:2G 物理备库:tcdg192.168.56.108内存:1.5G 操作系 ...

  3. HibernateReview Day1 - Introduction

    Hibernate已经学过去大概有半个月了,然后默默的忘掉了……所谓Practice makes perfect. 我尽力重新拾起来. 1.什么是ORM 在介绍Hibernate之前,我们先学习下OR ...

  4. 配置jndi服务,javax.naming.NamingException的四种情况

    1.当jndi服务没有启动,或者jndi服务的属性没有设置正确,抛出如下异常: javax.naming.CommunicationException: Can't find SerialContex ...

  5. Hibernate4.3.9Final常见问题汇总

    hibernate4下一个可用的hibernate.properties: jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql ...

  6. facade pattern

    外观模式是一种使用频率非常高的设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,使子系统与客户端的耦合度降低,且客户端调用非常方便.外观模式并不给系 ...

  7. SpringMVC单文件上传、多文件上传、文件列表显示、文件下载(转)

    林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 本文详细讲解了SpringMVC实例单文件上传.多文件上传.文件列表显示.文件下载. 本文工程 ...

  8. 找呀志_java网络编程(5)TCP和udp差额

    1.TCP定向链接,尽管该网络的不稳定性质,所述不安全确定多少次握手不能保证连接的可靠性.但TCP的三次握手至少(事实上确保了相当大的程度)以确保连接的可靠性; 和UDP不面向连接的,UDP前传送的数 ...

  9. 15一个NoSql数据库

    随着因特网web2.0该网站的兴起.非关系型数据库,现在已经成为一个非常受欢迎的新领域.非关系数据库产品的发展非常迅速.而在处理传统的关系数据库web2.0现场.特别是大规模,高并发SNS类型web2 ...

  10. 【Unity技能】做一个简单的NPC

    1. 写在前面 前几天我看到cgcookie一个教程.学习了下怎么依据已有人物模型制作一个仿版的NPC人物.感觉挺好玩的,整理一下放到博客里! 先看一下教程里面的终于效果. 是不是非常像个幽灵~ 以下 ...