Struts源码粗略分析三:代码剖析

非核心内容分析

继续上节,我们继续分析ActionServlet.process中的关键组件RequestProcessor的process方法。

/** * <p>Process an <code>HttpServletRequest</code> and create the * corresponding <code>HttpServletResponse</code> or dispatch to another * resource.</p> * * @param request The servlet request we are processing * @param response The servlet response we are creating * @throws IOException if an input/output error occurs * @throws ServletException if a processing exception occurs */ public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Wrap multipart requests with a special wrapper request = processMultipart(request); // Identify the path component we will use to select a mapping String path = processPath(request, response); if (path == null) { return; } if (log.isDebugEnabled()) { log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'"); } // Select a Locale for the current user if requested processLocale(request, response); // Set the content type and no-caching headers if requested processContent(request, response); processNoCache(request, response); // General purpose preprocessing hook if (!processPreprocess(request, response)) { return; } this.processCachedMessages(request, response); // Identify the mapping for this request ActionMapping mapping = processMapping(request, response, path); if (mapping == null) { return; } // Check for any role required to perform this action if (!processRoles(request, response, mapping)) { return; } // Process any ActionForm bean related to this request ActionForm form = processActionForm(request, response, mapping); processPopulate(request, response, form, mapping); // Validate any fields of the ActionForm bean, if applicable try { if (!processValidate(request, response, form, mapping)) { return; } } catch (InvalidCancelException e) { ActionForward forward = processException(request, response, e, form, mapping); processForwardConfig(request, response, forward); return; } catch (IOException e) { throw e; } catch (ServletException e) { throw e; } // Process a forward or include specified by this mapping if (!processForward(request, response, mapping)) { return; } if (!processInclude(request, response, mapping)) { return; } // Create or acquire the Action instance to process this request Action action = processActionCreate(request, response, mapping); if (action == null) { return; } // Call the Action instance itself ActionForward forward = processActionPerform(request, response, action, form, mapping); // Process the returned ActionForward instance processForwardConfig(request, response, forward); }

processMultipart的目的就是包装一个可以处理以multipart-data形式发送的表单数据,多用于文件上传。相关内容请参考http://commons.apache.org/fileupload/和http://www.ietf.org/rfc/rfc2388.txt。

processPath,返回一个路径,之后会利用该路径找个一个匹配的ActionMapping,ActionMapping属于一种不可见的关键性组件,它负责组织path,Action,ActionForm,forward并让它们协作。这里的处理包括模块与非模块部分。

processLocale,这个比较有意思,从下面代码以及对应的JavaDoc说明可以得知,Struts的Locale是可以通过配置来决定是否可以自动为客户端选择语言类型。

// Are we configured to select the Locale automatically? if (!moduleConfig.getControllerConfig().getLocale()) { return; }

语言相关内容请参考http://www.openinternetlexicon.com/HowTo/HowToServe.html,它描述了如何用浏览器发送带有指定语言的HTTP请求。当一个页面需要表现多种语言时,我们就可以像下面代码这样在页面的不同区域切换不同语言,虽然这样的场景不多,但确实存在,我就不走运碰到过~~~

// === 切换成加拿大法语 session.setAttribute(Globals.LOCALE_KEY, Locale.CANADA_FRENCH); // === 切换成中国汉语(简体,具体可以查看源码中CHINESE的值) session.setAttribute(Globals.LOCALE_KEY, Locale.CHINESE);

至于processContent、processNoCache,没什么好说的,HTTP头相关处理。

processPreprocess,一个没有意义的实现,之所以留有这个方法就是允许我们扩展ActionServlet来定义自己的ActionServlet实现,然后覆盖掉这个方法来添加一些自定义的内容。如果不想写自己的ActionServlet,那这个方法基本上是作废了,没什么使用价值。到此为止,与Struts核心功能组件不相关的内容就结束了~~~(processCachedMessages,看样子是清理Struts框架中的消息,节省资源,但什么时候用过,还真不知道:)

ActionMapping

负责把ActionForm、Action和ActionForward组织起来,让它们协同工作。之前所说的processPath就是帮助我们返回一个可以标识唯一的ActionMapping关键字,这个定位操作就是由processMapping完成的。而接下来的processRoles,则是服务器安全校验,相关内容请参考《Core Servlets and JavaServer Pages, Volume 2: Advanced Technologies, Second Edition》第三章、第四章,此书已有中文版。

ActionForm

processActionForm,创建一个ActionForm并根据对应的ActionMapping中配置信息放入session或reqeust中。processPopulate,这个方法值得一看,其中心内容就是BeanUtils.populate(bean, properties),如何利用一个Map类型对象为一个JavaBean填充数据,具体内容请参考BeanUtils文档。processValidate,先调用form.validate的方法,如果返回内容空或没有错误,则将控制权返回给上层代码,否则返回失败标识并执行跳转动作,以下这些跳转动作都是值得关注的:

  • response.sendError
  • response.sendRedirect
  • getServletContext().getRequestDispatcher(uri).forward(request, response);

Action

processForward和processInclude分别处理ForwardAction和IncludeAction,对应实现分别为doForward和doInclude,这两个实现技巧都非常有用,可以查看具体代码以加深印象!

processActionCreate是用于Action创建的,Struts中所有的Action都是统一管理,且每个定义只有一个实例存在,即单态。因为Servlet是以多线程形式相应客户端,而Action又作为共享资源存在,这里就会有多线程安全问题,所以在创建Action的时候,共享变量actions是被synchronized块所包围的。

有了Action,就可以调用processActionPerform了,这里的设计模式属于模板方法,直接调用我们最熟悉的Action.execute。现在打开/struts1/struts-1.3.10/src/extras/src/main/java下的org.apache.struts.actions,里面罗列了许多Action的实现,对于有些Action的特殊用法,小秘密都集中在execute方法中:)

  • apache/struts/actions/ActionDispatcher.java
  • apache/struts/actions/BaseAction.java
  • apache/struts/actions/DispatchAction.java
  • apache/struts/actions/DownloadAction.java
  • apache/struts/actions/EventActionDispatcher.java
  • apache/struts/actions/EventDispatchAction.java
  • apache/struts/actions/ForwardAction.java
  • apache/struts/actions/IncludeAction.java
  • apache/struts/actions/LocaleAction.java
  • apache/struts/actions/LookupDispatchAction.java
  • apache/struts/actions/MappingDispatchAction.java
  • apache/struts/actions/SwitchAction.java

ActionForward

最后一步就是processForwardConfig,执行跳转或转发操作,之前已经说过,这里就不再重复。

异常控制

如果在struts-config.xml中定义了异常控制句柄,在form.validate或action.execute发生异常时就会调用processException,该方法又会根据异常类型来自动选择异常句柄,主要是利用getClass方法一层层向上查找看是否有合适的处理句柄。

/** * <p>Find and return the <code>ExceptionConfig</code> instance defining * how <code>Exceptions</code> of the specified type should be handled. * This is performed by checking local and then global configurations for * the specified exception's class, and then looking up the superclass * chain (again checking local and then global configurations). If no * handler configuration can be found, return <code>null</code>.</p> * * <p>Introduced in <code>ActionMapping</code> in Struts 1.1, but pushed * up to <code>ActionConfig</code> in Struts 1.2.0.</p> * * @param type Exception class for which to find a handler * @since Struts 1.2.0 */ public ExceptionConfig findException(Class type) { // Check through the entire superclass hierarchy as needed ExceptionConfig config; while (true) { // Check for a locally defined handler String name = type.getName(); log.debug("findException: look locally for " + name); config = findExceptionConfig(name); if (config != null) { return (config); } // Check for a globally defined handler log.debug("findException: look globally for " + name); config = getModuleConfig().findExceptionConfig(name); if (config != null) { return (config); } // Loop again for our superclass (if any) type = type.getSuperclass(); if (type == null) { break; } } return (null); // No handler has been configured }

结束语

分析过程虽然粗糙(实在没那么多时间和精力),但主线过程基本都光顾到了,如有遗漏还望指出。

推荐几个参考文档:

《Prentice Hall - Core Servlets and JavaServer Pages, Volume 1 Core Technologies, 2nd Edition》

Prentice Hall -Core Servlets and JavaServer Pages, Volume 2: Advanced Technologies》

《McGraw-Hill -Struts: The Complete Reference

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值