首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

Struts2源代码分析之Struts2 Interceptor形式

2012-09-08 
Struts2源代码分析之Struts2 Interceptor模式首先贴出众所周知的Struts工作流程图:流程解说:???????1、客户

Struts2源代码分析之Struts2 Interceptor模式

首先贴出众所周知的Struts工作流程图:

Struts2源代码分析之Struts2 Interceptor形式

流程解说:

Struts2源代码分析之Struts2 Interceptor形式???????1、客户端向Servlet容器(如Tomcat)提交一个请求

?

?

?

?

Struts2源代码分析之Struts2 Interceptor形式???????2、请求经过一系列过滤器(如ActionContextCleanUp过滤器等)

?

?

?

?

Struts2源代码分析之Struts2 Interceptor形式???????3、核心控制器被调用,询问ActionMapper来决定请求是否需要调用某个Action

?

?

?

?

Struts2源代码分析之Struts2 Interceptor形式???????4、如果ActionMapper决定需要调用某个Action,核心控制器把控制权委派给ActionProxy?(备注:JSP请求无需调用Action

?

?

Struts2源代码分析之Struts2 Interceptor形式???????5、ActionProxy通过Configuration Manager询问框架的配置文件(struts.xml),找到需调用的Action

?

?

?

?

Struts2源代码分析之Struts2 Interceptor形式???????6、ActionProxy创建一个ActionInvocation的实例

?

?

?

?

Struts2源代码分析之Struts2 Interceptor形式???????7、?ActionInvocation负责调用Action,在此之前会依次调用所有配置的拦截器

?

?

?

?

Struts2源代码分析之Struts2 Interceptor形式???????8、Action执行完毕,ActionInvocation负责根据结果码字符串在struts.xml的配置中找到对应的返回结果

?

?

?

?

Struts2源代码分析之Struts2 Interceptor形式???????9、拦截器被再次执行

?

?

?

?

Struts2源代码分析之Struts2 Interceptor形式???????10、过滤器被再次执行

?

?

Struts2源代码分析之Struts2 Interceptor形式??????

?

?

l?????????寻找核心控制器,以下是struts2.3核心控制器StrutsPrepareAndExecuteFilter的关键代码:

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

?

?

?

?

?

????????HttpServletRequest request = (HttpServletRequest) req;

?

?

????????HttpServletResponse response = (HttpServletResponse) res;

?

?

?

?

?

????????try {

?

?

????????????prepare.setEncodingAndLocale(request, response);

?

?

????????????prepare.createActionContext(request, response);

?

?

????????????prepare.assignDispatcherToThread();

?

?

????????????if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {

?

?

????????????????chain.doFilter(request, response);

?

?

????????????} else {

?

?

????????????????request = prepare.wrapRequest(request);

?

?

????????????????//根据请求上下文获得一个ActionMapping

?

?

????????????????ActionMapping mapping = prepare.findActionMapping(request, response, true);

?

?

????????????????if (mapping == null) {

?

?

????????????????????boolean handled = execute.executeStaticResourceRequest(request, response);

?

?

????????????????????if (!handled) {

?

?

????????????????????????chain.doFilter(request, response);

?

?

????????????????????}

?

?

????????????????} else {

?

?

????????????????????//根据ActionMapping执行该Action,里面还隐藏着很多代码,此处execute是当前类的一个属性“protected ExecuteOperations execute;”,通过跟踪我们发现”?execute.executeAction”方法执行的实际上是另一个Dispatcher类的serviceAction方法

?

?

????????????????????execute.executeAction(request, response, mapping);

?

?

????????????????}

?

?

????????????}

?

?

????????} finally {

?

?

????????????prepare.cleanupRequest(request);

?

?

????????}

?

?

}

?

?

?

?

?

l?????????寻找Dispatcher类的serviceAction方法:

?

?

public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,

?

?

??????????????????????????????ActionMapping mapping) throws ServletException {

?

?

?

?

?

????????//…省略

?

?

?

?

?

????????String timerKey = "Handling request from Dispatcher";

?

?

????????try {

?

?

????????????UtilTimerStack.push(timerKey);

?

?

????????????String namespace = mapping.getNamespace();

?

?

????????????String name = mapping.getName();

?

?

????????????String method = mapping.getMethod();

?

?

?

?

?

????????????Configuration config = configurationManager.getConfiguration();

?

?

????????????//创建代理ActionProxy

?

?

????????????ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(

?

?

????????????????????namespace, name, method, extraContext, true, false);

?

?

?

?

?

????????????request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

?

?

?

?

?

????????????//执行该ActionProxy

?

?

????????????// if the ActionMapping says to go straight to a result, do it!??????

?

?

????????????if (mapping.getResult() != null) {

?

?

????????????????Result result = mapping.getResult();

?

?

????????????????result.execute(proxy.getInvocation());

?

?

????????????} else {

?

?

????????????????proxy.execute();

?

?

????????????}

?

?

??????????

?

?

????????} catch (Exception e) {

?

?

????????????sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);

?

?

????????} finally {

?

?

????????????UtilTimerStack.pop(timerKey);

?

?

????????}

?

?

????}

?

?

l?????????寻找代理Action的execute方法[ActionProxy是一个接口,我们观察他的一个缺省实现DefaultActionProxy]:

public class DefaultActionProxy implements ActionProxy, Serializable {

?

?

????????public String execute() throws Exception {

?

?

????????//…省略

?

?

????????try {

?

?

????????????UtilTimerStack.push(profileKey);

?

?

??????????//每个ActionProxy会持有一个唯一的ActionInvocation,?struts2的Interceptor模式就在这里体现

?

?

????????????retCode = invocation.invoke();

?

?

????????} finally {

?

?

????????????if (cleanupContext) {

?

?

????????????????ActionContext.setContext(nestedContext);

?

?

????????????}

?

?

????????????UtilTimerStack.pop(profileKey);

?

?

????????}

?

?

?

?

?

????????return retCode;

?

?

?????????}

?

?

?

?

?

}

l?????????寻找ActionInvocation

public class DefaultActionInvocation implements ActionInvocation {

?

?

????//每个ActionInvocation对象持有一个InterceptorMapping对象组成的迭代器.

?

?

//根据前面的工作原理我们知道代理Action在执行真实Action的前后要执行Interceptor.这里就是struts2 Interceptor模式所在

?

?

????protected Iterator<InterceptorMapping> interceptors;

?

?

????public String invoke() throws Exception {??????

?

?

????????try {

?

?

???????????if (interceptors.hasNext()) {

?

?

????????????????//从迭代器取出一个InterceptorMapping

?

?

????????????????final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();

?

?

????????????????String interceptorMsg = "interceptor: " + interceptor.getName();

?

?

????????????????UtilTimerStack.push(interceptorMsg);

?

?

????????????????try {

?

?

????????????????????//从InterceptorMapping取出Interceptor执行其intercept方法,并将当前ActionInvocation实例传递给拦截器对象,核心所在,

?

?

//我们下面马上去看一个Interceptor的实现

?

?

?????????????????????resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);

?

?

????????????????}

?

?

????????????????finally {

?

?

????????????????????UtilTimerStack.pop(interceptorMsg);

?

?

????????????????}

?

?

????????????} else {

?

?

????????????????resultCode = invokeActionOnly();

?

?

????????????}

?

?

????????????return resultCode;

?

?

????????}

?

?

????????finally {

?

?

????????????UtilTimerStack.pop(profileKey);

?

?

????????}

?

?

????}

?

?

?

?

?

????public String invokeActionOnly() throws Exception {

?

?

????????return invokeAction(getAction(), proxy.getConfig());

?

?

}

?

?

}

?

?

?

?

?

l?????????寻找Interceptor

public class AliasInterceptor extends AbstractInterceptor {

?

?

????@Override public String intercept(ActionInvocation invocation) throws Exception {

?

?

????????//执行拦截器业务操作1.

?

?

?????//执行拦截器业务操作2.

?

?

????????return invocation.invoke();//通过ActionInvocation回调invoke方法,再次取出下一个InterceptorMapping

?

?

????}

?

?

}

?

?

?

?

?

拦截器模式分析:

?

?

????以上组后两个步骤体现出拦截器模式,为了便于理解,我用图示说明

?

?

解决问题:我们有个一个Action和一组拦截器,要拦截对对Action对象execute方法的执行,以使得在Action对象执行前后可以有机会执行拦截器,达到如下效果图:

?

?

Struts2源代码分析之Struts2 Interceptor形式

?

?

实现流程:

?

?

Struts2源代码分析之Struts2 Interceptor形式

?

?

流程说明:

?

?

1.ActionInvocation发起invoke调用,迭代器中有没有下一个拦截器Mapping,迭代器中有下一个拦截器Mapping,取出该拦截器并执行它的intercept方法,同时将当前实例传递给该方法[红色圆形]

?

?

2.执行第一个拦截器的拦截业务,执行完毕后调用上一步传递进来的ActionInvocation实例引用的invoke方法,再次发起对ActionInvocation.invoke的第二次调用[有点递归的意思J]

?

?

3.程序流程再次回调invoke方法,和第一部操作一样判断迭代器中有无下一个拦截器Mapping,有的话取出该拦截器并执行它的intercept方法,同时将当前实例传递给该方法[红色圆形]

?

?

4.执行第二个拦截器的拦截业务,执行完毕后调用上一步传递进来的ActionInvocation实例引用的invoke方法,再次发起对ActionInvocation.invoke的第三次调用[有点递归的意思J]

?

?

5.程序流程再次回调invoke方法,和第一部操作一样判断迭代器中有无下一个拦截器Mapping,但是现在迭代器已经到达末尾了,没有拦截器可以被调用执行,因此程序执行else部分【请观察上面贴出的ActionInvocation的代码】,执行真正的Action实例。

?

?

6.真实的 Action执行完毕后程序回到地4步的拦截器里面执行回调代码处,程序继续向下执行拦截器的后半部分代码[当然可能没有代码],由此看出对于当前这个拦截器而言,Action执行前和执行后都经过了该拦截器【想想既然如此我们可以在拦截器里做些什么?参数赋值、文件上传…】

?

?

7.当前拦截器的后半部分代码执行完了以后,程序回到第3步调用拦截器的intercept方法处继续执行,由此代码又回到了ActionInvocation,此处程序不在执行?else部分【请观察上面贴出的ActionInvocation的代码】,因为执行时的上下文环境中迭代器是有下一个拦截器的。故方法直接执行结束。

?

?

8.上一步的ActionInvocation.invoke执行完毕后继续程序回到第2步的拦截器里面执行回调代码处,程序继续向下执行拦截器的后半部分代码[当然可能没有代码],由此看出对于当前这个拦截器而言,Action执行前和执行后都经过了该拦截器【想想既然如此我们可以在拦截器里做些什么?参数赋值、文件上传…】

?

?

9.当前拦截器的后半部分代码执行完了以后,程序回到第1步调用拦截器的intercept方法处继续执行,由此代码又回到了ActionInvocation,此处程序不在执行?else部分【请观察上面贴出的ActionInvocation的代码】,因为执行时的上下文环境中迭代器是有下一个拦截器的。故方法直接执行结束。至此嗲用结束。

?

?

?

?

?

我们来简化一下下拦截器模式流程图:

?

?

Struts2源代码分析之Struts2 Interceptor形式

?

?

时间紧张,代码不再示范,我们看看这位朋友的博客上的代码示范:

http://www.cnblogs.com/west-link/archive/2011/06/22/2086591.html

拦截器模式为我们提供了一种拦截方法调用或消息的途径,整个过程是自动的、透明的,下面是一个简单的拦截器示意图:

?

?

Struts2源代码分析之Struts2 Interceptor形式

从图中可以看到,拦截器可以访问到方法调用的输入参数和返回结果,这样的话,拦截器能做的事儿就多啦,比如:?
? 1、验证输入参数是否正确?
? 2、偷偷地修改参数的值,例如参数类型的自动转换等?
? 3、依赖注入?
? 4、修改返回结果的内容、格式等

?

?

??下面是一个包含我们要拦截的方法的类:

?

?

public?class?Action{?
??????//?拦截器集合的迭代器?
??????private?Iterator interceptors;?
??????//?输入参数?
??????private?Parameter param;?
??????//?返回结果?
??????private?Result result;?
??????//?构造函数?
??????public?Action(Parameter param);?
??????//?这个方法是我们要拦截的(具体实现见下面)?
??????public?Result invoke();?
??????//?其他...?
??}

?

?

?

?

下面声明一个拦截器接口:

?

?

public?interface?Interceptor{?
??????public?Result intercept(Action action);?
}??

?

?

?

?

我们可能会想到以下的拦截方式:

?

?

//?先在方法调用之前拦截一下,可以处理输入参数?
intercept(param);?
//?调用方法?
result = action.invoke(param);?
//?在方法调用之后再拦截一下,可以处理返回结果?
intercept(result);

?

?

?

?

这种方式的缺点是不能完全地控制方法的调用,比如不能跳过方法的调用而直接返回结果、不能更改所在对象内部的状态等。

?

?

下面的实现也能达到方法调用的前后拦截,并且有完全控制该对象的能力

?

?

public?Result invoke(){?
????if( interceptors.hasNext() ){?
????????Interceptor interceptor = interceptors.next();?
????????result = interceptor.intercept(this);?
????}?
}

?

?

?

?

下面我们举几个拦截的具体例子:

?

?

//?参数合法性验证拦截器?
public?ParamInvalidator?implements?Interceptor{?
????public?Result intercept(Action action){?
????????Paramemter param = action.getParam();?
????????//?确保参数不为null?
????????if(param ==?null){?
????????????addMessege("param is null!");?
????????????//?创建一个默认的参数对象?
????????????action.setParam(new?Paramemter());?
????????}?
????????//?继续调用过程?
????????return?action.invoke();?
????}?
}

?

?

?

?

一般来说,这类拦截器应该放在拦截器集合的最前面,所以,拦截器的被执行顺序是比较重要的!这依赖于具体的实现需求。

?

?

如果需求有要求:参数为null时直接返回一个null的结果,停止调用过程,那么我们可以把上面的方法改成这样:

?

?

public?Result intercept(Action action){?
????Paramemter param = action.getParam();?
????if(param ==?null){?
????????return?null;?
????}?
????//?继续调用过程?
????return?action.invoke();?
}

?

?

?

?

所以,在所有的拦截器的实现中,action.invoke()这一行代码的位置或者有无非常重要,通过控制它我们就能在调用的前后进行拦截,甚至不调用它!

?

?

public?interface?MoneyAware{?
????public?void?setMoney(int?money);?
}?
//?依赖注入拦截器?
public?DependencyInjector?implements?Interceptor{?
????public?Result intercept(Action action){?
????????//?如果它实现了MoneyAware接口,那么我们就给它注入5毛钱?
????????if(action?instanceof?MoneyAware){?
????????????action.setMoney(5);?
????????}?
????????//?继续调用过程?
????????return?action.invoke();?
????}?
}

?

?

?

?

从这个拦截器可以看出,我们能修改被拦截对象的内部状态。

?

?

//?结果格式化拦截器?
public?ResultFormater?implements?Interceptor{?
????public?Result intercept(Action action){?
????????//?先调用要拦截的方法?
????????result = action.invoke();?
????????//?将结果格式化?
????????formatResult(result);?
????????//?返回结果?
????????return?result;?
????}?
}

?

?

?

?

这个拦截器就是方法调用后的拦截,所以这种拦截器被执行的时候被放在调用堆栈的最下面,当其他拦截器执行完后,它才被执行!

?

?

?

?

热点排行