Struts源码粗略分析二:代码初探

分析范围

仅仅对Struts的基本运行原理进行分析,不包括实现细节以及标签库、插件等非主流程内容的分析,分析流程大致如下

request -> ActionServlet -> ActionForm -> Action -> Forward -> JSP/other rescources

请求 -> 中心应用控制器 -> 数据包装 -> 业务控制器 -> 跳转 -> JSP/其它资源

目录结构分析

源码部分:struts-1.3.10/src/apps/cookbook/src/main/java

运行部分(含*部分的内容在分析范围内):

/struts1/web-app
/struts1/web-app/css
/struts1/web-app/images
/struts1/web-app/jsp
/struts1/web-app/WEB-INF
/struts1/web-app/WEB-INF/lib
/struts1/web-app/WEB-INF/src
/struts1/web-app/WEB-INF/chain-config.xml *
/struts1/web-app/WEB-INF/struts-config-Wildcard.xml *
/struts1/web-app/WEB-INF/struts-config.xml *
/struts1/web-app/WEB-INF/validation.xml *
/struts1/web-app/WEB-INF/web.xml *
/struts1/web-app/index.jsp
/struts1/web-app/source.jsp

基本概念

ActionServlet - Struts中核心组件,用于管理与调度ActionForm、Action等组件,随着应用服务器启动而启动

ActionForm - 对客户端请求时所携带的数据进行包装,并能提供验证功能

Action - 对被请求的内容进行业务控制,执行业务选择与视图派发

Exception - 对Action执行时所发生的异常进行控制

Forward - 提供跳转相关信息

ActionMapping - 对应Struts配置文件,可视为配置文件的内存表现形式

web.xml

注意load-on-startup标签,代表ActionServlet会随着servlet容器启动而启动,也就是说,Struts的核心组件是ActionServlet,一切配置信息都是通过ActionServlet的启动而装载的。看下面代码会更清晰一些。

<?xml version="1.0" encoding="iso-8859-1"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Struts Examples Application</display-name> <!-- Standard Action Servlet Configuration (with debugging) --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value> /WEB-INF/struts-config.xml, /WEB-INF/struts-config-Wildcard.xml </param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <!-- Standard Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- The Usual Welcome File List --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>

Struts配置文件struts-config.xml

Struts配置文件内容大致可以分析以下几个内容:

  • form-beans
  • global-exceptions
  • global-forwards
  • action-mappings
  • message-resources
  • plug-in

至于各个部分的配置说明,请参考《Struts - The Complete Reference》与Struts的JavaDoc。

Struts初始化

现在就打开ActionServlet源码,位置是/struts1/struts-1.3.10/src/core/src/main/java/org/apache/struts/action/ActionServlet.java。

init与destory就不看了,大致就是配置信息的初始化校验等,不过init中值得注意的是下面这句:

initOther(); // BeanUtil转换器设置,这个地方可以扩展定义我们自己的转换器

假如JSP画面中一个checkbox的value值为“是”或“否”,想对应ActionForm中的一个布尔型属性,我们就可以仿照initOther来注册我们自己的转换器,至于怎么用自己查吧:)

处理请求

接下来我们来看看doGet和doPost的方法实现,二者步调一致,统一指向了process方法。如果说ActionServlet是Struts的核心组件,那么RequestProcessor则是ActionServlet的核心,因为从请求发起至完成响应,RequestProcessor负责了全部的调度工作。

/** * <p>Process an HTTP "GET" request.</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 servlet exception occurs */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } /** * <p>Process an HTTP "POST" request.</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 servlet exception occurs */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } /** * <p>Perform the standard request processing for this request, and create * the corresponding response.</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 servlet exception is thrown */ protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { ModuleUtils.getInstance().selectModule(request, getServletContext()); ModuleConfig config = getModuleConfig(request); RequestProcessor processor = getProcessorForModule(config); if (processor == null) { processor = getRequestProcessor(config); } processor.process(request, response); }

接下来进入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); }

现在我们粗略的分析一下在整个请求过程中,各个Struts组件是如何被组织调用的。从代码中可以看出,整个process方法内部是由若干个processXXX方法组成,每条语句都执行一个特别处理,先是processMultipart,处理与文件上传相关的内容;processPath,根据request中的信息来选择映射路径,以便后来选择合适的ActionMapping组件;processLocale,处理本地化信息;processContent、processNoCache,处理与HTML头相关的信息(contentType和缓存的使用);processPreprocess,该方法为模板方法,如果我们通过继承ActionServlet来定义自己的ActionServlet,可以覆盖此方法以达到预先处理请求内容的目的;processCachedMessages,清理session中的缓存消息;processMapping,通过之前查找到的path来确定可用的ActionMapping对象;processRoles,有点ACL的意思,还真没用过,主要是确认当前用户的角色是否有访问当前的资源。

剩下的内容将在下节进行细节分析。

  • processActionForm // 根据ActionMapping提供的信息生成ActionForm
  • processPopulate // 利用request中携带的请求信息填写ActionForm中定义的每个字段
  • processValidate // 调用ActionForm的validate方法来做数据验证
  • processException // 处理异常,对应struts-config.xml中Exception部分
  • processForward // 处理ForwardAction
  • processInclude // 处理IncludeAction
  • processActionCreate //根据ActionMapping提供的信息生成Action,每个Action只有一个实例存在
  • processActionPerform // 调用Action的execute方法
  • processForwardConfig // 根据Action.execute方法返回的ActionForward来决定接下来的URL切换动作(跳转或转发)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值