在Struts的过滤器中,经过调用PrepareOperations.createActionContext(Request, Response)函数后,一个ActionContext对象被创建了,与Action有关的数据全部放在了ActionContext中,有些地方仍然使用HttpRequestServlet.getAttribute(String)这个函数取值,所以struts框架包装HttpRequestServlet,覆盖此函数,使它能从ActionContext中取值,譬如说:取ValueStack的值,它的key是"com.opensymphony.xwork2.util.ValueStack.ValueStack"。
request = prepare.wrapRequest(request);
在框架内部,直到Action.execute()函数被调用之前,Model层所需要用的数据全部从ActionContext中取得。深入这行代码
execute.executeAction(request, response, mapping);
进去看,发现其实是调用了Dispatcher.serviceAction(...)函数,在这个函数首先根据所给的参数创建ActionContext(是因为在前面以前创建过,是不是多余?),然后根据ActionMapping对象,从x-work容器内载入ActionProxy类,这个时候,ActionProxy有一个重要的对象ActionInvocation被创建。
Dispatcher.serviceAction(HttpServletRequest, HttpServletResponse, ServletContext, ActionMapping)
这个方法里其实是调用了StrutsActionProxy.execute()方法,execute方法内调用了ActionContext.invoke()方法,再在invoke方法里先调用所有的拦截器,最后调用Action类的execute方法或者在配置文件里允许被调用的方法,这个过程描述出来很啰嗦。
但是有一点特别重要,在执行拦截器和Action时,都公用了ActionContext这个对象,对于拦截器来说,只用从ActionContext里取数据,执行功能代码后,再次调用ActionInnvocation.invoke()方法。这中约定对于链式的设计非常重要。
看下面的示意图(该死的word,那个ActionProxy方块不知道怎么搞的,怎么也拉不到对齐的位置)。
Action类被执行后,返回ExecuteResult对象,表示一个执行的对象结果,为什么有这个对象,而不是直接返回一个表示要跳转或转发的URL呢?让我们粗略看一下这个类继承图就知道了。
因为struts着力于优雅的处理错误信息,验证信息等,还要支持很多框架的视图技术,所以,也是一个很复杂的设计,这里暂时不讨论。
回到Model装配的问题上来,在经过拦截器层层拦截后,在Action类被执行前,Action类的Model Bean是一定要被装配好的,只是经过struts2的拦截器实在是很多,逐个逐个的看,真不知道要看到什么时候,这里投个巧,借助eclispse继承开发环境的条件断点则容易的定位到答案,Model Bean是在ParametersInterceptor这个类中被装配的。
Map<String, Object> contextMap = ac.getContextMap(); try { ReflectionContextState.setCreatingNullObjects(contextMap, true); //允许创建空对象 ReflectionContextState.setDenyMethodExecution(contextMap, true); //不容许调用非属性相关的setter方法 ReflectionContextState.setReportingConversionErrors(contextMap, true); //报告错误到contextMap ValueStack stack = ac.getValueStack(); setParameters(action, stack, parameters); //利用intercepter装配bean,且保证创建这个对象 } finally { ReflectionContextState.setCreatingNullObjects(contextMap, false); ReflectionContextState.setDenyMethodExecution(contextMap, false); ReflectionContextState.setReportingConversionErrors(contextMap, false); }
上面是这个拦截器的核心代码,在ParametersInterceptor的私有方法setParameters(action, stack, parameters)中,借助OGNL装配了Model Bean,正因为OGNL组件,参数值都在值栈里,并且用参数的名字作为key,即使装配复合的Model Bean,对于struts框架来说,也是非常简单的工作。
目前为止,Model Bean在struts2框架里是如何自动装配的,主要的线路点就介绍完了,也达到了让fastupload支持struts框架所必须掌握的知识,因为即将进行fastupload的新版本开发工作,这个系列暂停一段时间。
[本文系作者原创,如若转载,请注明出处,@仪山湖]