Struts2源码阅读

Struts包介绍

http://www.blogjava.net/lzhidj/archive/2008/07/10/213898.html )(大部分叙述性的文字和图从其文中拷贝):

包名

说明

org.apache.struts2. components

该包封装视图组件, Struts2在视图组件上有了很大加强,不仅增加了组件的属性个数,更新增了几个非常有用的组件,如 updownselect、 doubleselect、 datetimepicker、 token、 tree等。

另外, Struts2可视化视图组件开始支持主题 (theme),缺省情况下,使用自带的缺省主题,如果要自定义页面效果,需要将组件的 theme属性设置为 simple。

org.apache.struts2. config

该包定义与配置相关的接口和类。实际上,工程中的 xml和 properties文件的读取和解析都是由 WebWork完成的, Struts只做了少量的工作。

org.apache.struts2.dispatcher

Struts2的核心包,最重要的类都放在该包中。

org.apache.struts2.impl

该包只定义了 3个类,他们是 StrutsActionProxy、 StrutsActionProxyFactory、 StrutsObjectFactory,这三个类都是对 xwork的扩展。

org.apache.struts2.interceptor

定义内置的截拦器。

org.apache.struts2.util

实用包。

org.apache.struts2.validators

只定义了一个类: DWRValidator。

org.apache.struts2.views

提供 freemarker、 jsp、 velocity等不同类型的页面呈现。

下表是对一些重要类的说明:

类名

说明

org.apache.struts2.dispatcher. Dispatcher

该类有两个作用:

1、初始化

2、调用指定的 Action的 execute()方法。

org.apache.struts2.dispatcher. FilterDispatcher

    这是一个过滤器。文档中已明确说明,如果没有经验,配置时请将 url-pattern的值设成 /*。

    该类有四个作用:

    1、执行 Action

    2、清理 ActionContext,避免内存泄漏

    3、处理静态内容( Serving static content)

    4、为请求启动 xwork’s的截拦器链。

com.opensymphony.xwork2. ActionProxy

Action的代理接口。

com.opensymphony.xwork2. ctionProxyFactory

 生产 ActionProxy的工厂。

com.opensymphony.xwork2.ActionInvocation

负责调用 Action和截拦器。

com.opensymphony.xwork2.config.providers. XmlConfigurationProvider

负责 Struts2的配置文件的解析。


Struts体系结构

Struts工作机制
    1、客户端初始化一个指向Servlet容器(例如Tomcat)的请求;
    2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin);
    3、接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper来决定这个请求是否需要调用某个Action;
    4、如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy;
    5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类;
    6、ActionProxy创建一个ActionInvocation的实例。
    7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
    8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总 是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper。
Struts源码分析
    从org.apache.struts2.dispatcher.FilterDispatcher开始

   //创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析,读取对应Action。。。的地方    public   void  init(FilterConfig filterConfig)  throws  ServletException {           this .filterConfig = filterConfig;                   dispatcher = createDispatcher(filterConfig);         dispatcher.init();         //读取初始参数pakages,调用parse(),解析成类似/org/apache/struts2/static,/template的数组         String param = filterConfig.getInitParameter( "packages" );         String packages =  "org.apache.struts2.static template org.apache.struts2.interceptor.debugging" ;          if  (param !=  null ) {             packages = param +  " "  + packages;         }          this .pathPrefixes = parse(packages);     }
    顾名思义,init方法里就是初始读取一些属性配置文件,先看init_DefaultProperties。
  1.      public   void  init() {
  2.          if  (configurationManager ==  null ) {
  3.             configurationManager =  new  ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
  4.         }
  5.         init_DefaultProperties();  // [1]
  6.         init_TraditionalXmlConfigurations();  // [2]
  7.         init_LegacyStrutsProperties();  // [3]
  8.         init_ZeroConfiguration();  // [4]
  9.         init_CustomConfigurationProviders();  // [5]
  10.         init_MethodConfigurationProvider();
  11.         init_FilterInitParameters() ;  // [6]
  12.         init_AliasStandardObjects() ;  // [7]
  13.         Container container = init_PreloadConfiguration();
  14.         init_CheckConfigurationReloading(container);
  15.         init_CheckWebLogicWorkaround(container);
  16.     }
  17.      private   void  init_DefaultProperties() {
  18.         configurationManager.addConfigurationProvider( new  DefaultPropertiesProvider());
  19.     }
  20.      //DefaultPropertiesProvider
  21.      public   void  register(ContainerBuilder builder, LocatableProperties props)
  22.              throws  ConfigurationException {
  23.         
  24.         Settings defaultSettings =  null ;
  25.          try  {
  26.             defaultSettings =  new  PropertiesSettings( "org/apache/struts2/default" );
  27.         }  catch  (Exception e) {
  28.              throw   new  ConfigurationException( "Could not find or error in org/apache/struts2/default.properties" , e);
  29.         }
  30.         
  31.         loadSettings(props, defaultSettings);
  32.     }
  33.      //PropertiesSettings
  34.      //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写
  35.      public  PropertiesSettings(String name) {
  36.         
  37.         URL settingsUrl = ClassLoaderUtils.getResource(name +  ".properties" , getClass());
  38.         
  39.          if  (settingsUrl ==  null ) {
  40.             LOG.debug(name +  ".properties missing" );
  41.             settings =  new  LocatableProperties();
  42.              return ;
  43.         }
  44.          //settings的类型为LocatableProperties,继承Properties
  45.         settings =  new  LocatableProperties( new  LocationImpl( null , settingsUrl.toString()));
  46.          // Load settings
  47.         InputStream in =  null ;
  48.          try  {
  49.             in = settingsUrl.openStream();
  50.             settings.load(in);
  51.         }  catch  (IOException e) {
  52.              throw   new  StrutsException( "Could not load "  + name +  ".properties:"  + e, e);
  53.         }  finally  {
  54.              if (in !=  null ) {
  55.                  try  {
  56.                     in.close();
  57.                 }  catch (IOException io) {
  58.                     LOG.warn( "Unable to close input stream" , io);
  59.                 }
  60.             }
  61.         }
  62.     }
    再来看init_TraditionalXmlConfigurations方法,这个是读取Action配置的方法。
  1.      private   void  init_TraditionalXmlConfigurations() {
  2.          //首先读取web.xml中的config初始参数值
  3.          //如果没有配置就使用默认的"struts-default.xml,struts-plugin.xml,struts.xml",
  4.          //这儿就可以看出为什么默认的配置文件必须取名为这三个名称了
  5.          //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可
  6.         String configPaths = initParams.get( "config" );
  7.          if  (configPaths ==  null ) {
  8.             configPaths = DEFAULT_CONFIGURATION_PATHS;
  9.         }
  10.         String[] files = configPaths.split( "//s*[,]//s*" );
  11.          //依次解析配置文件,xwork.xml单独解析
  12.          for  (String file : files) {
  13.              if  (file.endsWith( ".xml" )) {
  14.                  if  ( "xwork.xml" .equals(file)) {
  15.                     configurationManager.addConfigurationProvider( new  XmlConfigurationProvider(file,  false ));
  16.                 }  else  {
  17.                     configurationManager.addConfigurationProvider( new  StrutsXmlConfigurationProvider(file,  false , servletContext));
  18.                 }
  19.             }  else  {
  20.                  throw   new  IllegalArgumentException( "Invalid configuration file name" );
  21.             }
  22.         }
  23.     }
     对于其它配置文件只用StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProvider接口。 XmlConfigurationProvider 负责配置文件的读取和解析, addAction() 方法负责读取 <action> 标签,并将数据保存在 ActionConfig 中; addResultTypes() 方法负责将 <result-type> 标签转化为 ResultTypeConfig 对象; loadInterceptors() 方法负责将 <interceptor> 标签转化为 InterceptorConfi 对象; loadInterceptorStack() 方法负责将 <interceptor-ref> 标签转化为 InterceptorStackConfig 对象; loadInterceptorStacks() 方法负责将 <interceptor-stack> 标签转化成 InterceptorStackConfig 对象。而上面的方法最终会被 addPackage() 方法调用,将所读取到的数据汇集到 PackageConfig 对象中
  1.      protected  PackageConfig addPackage(Element packageElement)  throws  ConfigurationException {
  2.         PackageConfig.Builder newPackage = buildPackageContext(packageElement);
  3.          if  (newPackage.isNeedsRefresh()) {
  4.              return  newPackage.build();
  5.         }
  6.          if  (LOG.isDebugEnabled()) {
  7.             LOG.debug( "Loaded "  + newPackage);
  8.         }
  9.          // add result types (and default result) to this package
  10.         addResultTypes(newPackage, packageElement);
  11.          // load the interceptors and interceptor stacks for this package
  12.         loadInterceptors(newPackage, packageElement);
  13.          // load the default interceptor reference for this package
  14.         loadDefaultInterceptorRef(newPackage, packageElement);
  15.          // load the default class ref for this package
  16.         loadDefaultClassRef(newPackage, packageElement);
  17.          // load the global result list for this package
  18.         loadGlobalResults(newPackage, packageElement);
  19.          // load the global exception handler list for this package
  20.         loadGobalExceptionMappings(newPackage, packageElement);
  21.          // get actions
  22.         NodeList actionList = packageElement.getElementsByTagName( "action" );
  23.          for  ( int  i =  0 ; i < actionList.getLength(); i++) {
  24.             Element actionElement = (Element) actionList.item(i);
  25.             addAction(actionElement, newPackage);
  26.         }
  27.          // load the default action reference for this package
  28.         loadDefaultActionRef(newPackage, packageElement);
  29.         PackageConfig cfg = newPackage.build();
  30.         configuration.addPackageConfig(cfg.getName(), cfg);
  31.          return  cfg;
  32.     }
    这儿发现一个配置上的小东西,我的xwork2.0.*是没有的,但是看源码是看到xwork2.1.*是可以的。看如下代码:
  1.      private  List loadConfigurationFiles(String fileName, Element includeElement) {
  2.         List<Document> docs =  new  ArrayList<Document>();
  3.          if  (!includedFileNames.contains(fileName)) {
  4.             ...........
  5.                 Element rootElement = doc.getDocumentElement();
  6.                 NodeList children = rootElement.getChildNodes();
  7.                  int  childSize = children.getLength();
  8.                  for  ( int  i = 0; i < childSize; i++) {
  9.                     Node childNode = children.item(i);
  10.                      if  (childNode instanceof Element) {
  11.                         Element child = (Element) childNode;
  12.                         final String nodeName = child.getNodeName();
  13.                          //解析每个action配置是,对于include文件可以使用通配符*来进行配置
  14.                          //如Struts.xml中可配置成<include file="actions_*.xml"/>
  15.                          if  (nodeName.equals( "include" )) {
  16.                             String includeFileName = child.getAttribute( "file" );
  17.                              if (includeFileName.indexOf( '*' ) != -1 ) {
  18.                                  // handleWildCardIncludes(includeFileName, docs, child);
  19.                                 ClassPathFinder wildcardFinder =  new  ClassPathFinder();
  20.                                 wildcardFinder.setPattern(includeFileName);
  21.                                 Vector<String> wildcardMatches = wildcardFinder.findMatches();
  22.                                  for  (String match : wildcardMatches) {
  23.                                     docs.addAll(loadConfigurationFiles(match, child));
  24.                                 }
  25.                             }
  26.                              else  {
  27.                                 
  28.                                 docs.addAll(loadConfigurationFiles(includeFileName, child));    
  29.                             }    
  30.                     }
  31.                 }
  32.                 }
  33.                 docs.add(doc);
  34.                 loadedFileUrls.add(url.toString());
  35.             }
  36.         }
  37.          return  docs;
  38.     }
    init_CustomConfigurationProviders方式初始自定义的Provider,配置类全名和实现ConfigurationProvider接口就可以。
  1.      public   void  doFilter(ServletRequest req, ServletResponse res, FilterChain chain)  throws  IOException, ServletException {
  2.         HttpServletRequest request = (HttpServletRequest) req;
  3.         HttpServletResponse response = (HttpServletResponse) res;
  4.         ServletContext servletContext = getServletContext();
  5.         String timerKey =  "FilterDispatcher_doFilter: " ;
  6.          try  {
  7.             UtilTimerStack.push(timerKey);
  8.              //根据content type来使用不同的Request封装,可以参见Dispatcher的wrapRequest
  9.             request = prepareDispatcherAndWrapRequest(request, response);
  10.             ActionMapping mapping;
  11.              try  {
  12.                  //根据url取得对应的Action的配置信息--ActionMapping,actionMapper是通过Container的inject注入的
  13.                 mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
  14.             }  catch  (Exception ex) {
  15.                 LOG.error( "error getting ActionMapping" , ex);
  16.                 dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
  17.                  return ;
  18.             }
  19.              //如果找不到对应的action配置,则直接返回。比如你输入***.jsp等等
  20.             //这儿有个例外,就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,当然.class文件除外。如果再没有则跳转到404
  21.           if  (mapping ==  null ) {
  22.                  // there is no action in this request, should we look for a static resource?
  23.                 String resourcePath = RequestUtils.getServletPath(request);
  24.                  if  ( "" .equals(resourcePath) &&  null  != request.getPathInfo()) {
  25.                     resourcePath = request.getPathInfo();
  26.                 }
  27.                  if  (serveStatic && resourcePath.startsWith( "/struts" )) {
  28.                     String name = resourcePath.substring( "/struts" .length());
  29.                     findStaticResource(name, request, response);
  30.                 }  else  {
  31.                      // this is a normal request, let it pass through
  32.                     chain.doFilter(request, response);
  33.                 }
  34.                  // The framework did its job here
  35.                  return ;
  36.             }
  37.              //正式开始执行Action的方法了
  38.             dispatcher.serviceAction(request, response, servletContext, mapping);
  39.         }  finally  {
  40.              try  {
  41.                 ActionContextCleanUp.cleanUp(req);
  42.             }  finally  {
  43.                 UtilTimerStack.pop(timerKey);
  44.             }
  45.         }
  46.     }
    来看Dispatcher类的serviceAction方法:
  1.      public   void  serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
  2.                               ActionMapping mapping)  throws  ServletException {
  3.         Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
  4.          // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
  5.         ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
  6.          if  (stack !=  null ) {
  7.             extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));
  8.         }
  9.         String timerKey =  "Handling request from Dispatcher" ;
  10.          try  {
  11.             UtilTimerStack.push(timerKey);
  12.             String namespace = mapping.getNamespace();
  13.             String name = mapping.getName();
  14.             String method = mapping.getMethod();
  15.             Configuration config = configurationManager.getConfiguration();
  16.             ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory. class ).createActionProxy(namespace, name, extraContext,  truefalse );
  17.             proxy.setMethod(method);
  18.             request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
  19.              // if the ActionMapping says to go straight to a result, do it!
  20.              if  (mapping.getResult() !=  null ) {
  21.                 Result result = mapping.getResult();
  22.                 result.execute(proxy.getInvocation());
  23.             }  else  {
  24.                 proxy.execute();
  25.             }
  26.              // If there was a previous value stack then set it back onto the request
  27.              if  (stack !=  null ) {
  28.                 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
  29.             }
  30.         }  catch  (ConfigurationException e) {
  31.             LOG.error( "Could not find action or result" , e);
  32.             sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
  33.         }  catch  (Exception e) {
  34.              throw   new  ServletException(e);
  35.         }  finally  {
  36.             UtilTimerStack.pop(timerKey);
  37.         }
  38.     }
    第一句 createContextMap() 方法,该方法主要把Application、Session、Request的key value值拷贝到Map中,并放在HashMap<String,Object>中,可以参见createContextMap方法:
  1.      public  HashMap<String,Object> createContextMap(Map requestMap,
  2.                                     Map parameterMap,
  3.                                     Map sessionMap,
  4.                                     Map applicationMap,
  5.                                     HttpServletRequest request,
  6.                                     HttpServletResponse response,
  7.                                     ServletContext servletContext) {
  8.         HashMap<String,Object> extraContext =  new  HashMap<String,Object>();
  9.         extraContext.put(ActionContext.PARAMETERS,  new  HashMap(parameterMap));
  10.         extraContext.put(ActionContext.SESSION, sessionMap);
  11.         extraContext.put(ActionContext.APPLICATION, applicationMap);
  12.         Locale locale;
  13.          if  (defaultLocale !=  null ) {
  14.             locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());
  15.         }  else  {
  16.             locale = request.getLocale();
  17.         }
  18.         extraContext.put(ActionContext.LOCALE, locale);
  19.          //extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(devMode));
  20.         extraContext.put(StrutsStatics.HTTP_REQUEST, request);
  21.         extraContext.put(StrutsStatics.HTTP_RESPONSE, response);
  22.         extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);
  23.          // helpers to get access to request/session/application scope
  24.         extraContext.put( "request" , requestMap);
  25.         extraContext.put( "session" , sessionMap);
  26.         extraContext.put( "application" , applicationMap);
  27.         extraContext.put( "parameters" , parameterMap);
  28.         AttributeMap attrMap =  new  AttributeMap(extraContext);
  29.         extraContext.put( "attr" , attrMap);
  30.          return  extraContext;
  31.     }
    后面才是最主要的--ActionProxy,ActionInvocation。ActionProxy是Action的一个代理类,也就是说 Action的调用是通过ActionProxy实现的,其实就是调用了ActionProxy.execute()方法,而该方法又调用了 ActionInvocation.invoke()方法。归根到底,最后调用的是 DefaultActionInvocation.invokeAction()方法。先看DefaultActionInvocation的init方 法。
  1.      public   void  init(ActionProxy proxy) {
  2.          this .proxy = proxy;
  3.         Map contextMap = createContextMap();
  4.          //设置ActionContext,把ActionInvocation和Action压入ValueStack
  5.         ActionContext actionContext = ActionContext.getContext();
  6.          if (actionContext !=  null ) {
  7.             actionContext.setActionInvocation( this );
  8.         }
  9.          //创建Action,可以看出Struts2里是每次请求都新建一个Action,careateAction方法可以自己参考
  10.         createAction(contextMap);
  11.          if  (pushAction) {
  12.             stack.push(action);
  13.             contextMap.put( "action" , action);
  14.         }
  15.         invocationContext =  new  ActionContext(contextMap);
  16.         invocationContext.setName(proxy.getActionName());
  17.         List interceptorList =  new  ArrayList(proxy.getConfig().getInterceptors());
  18.         interceptors = interceptorList.iterator();
  19.     }
  1.      protected   void  createAction(Map contextMap) {
  2.         String timerKey =  "actionCreate: " +proxy.getActionName();
  3.          try  {
  4.             UtilTimerStack.push(timerKey);
  5.              //这儿默认建立Action是StrutsObjectFactory,实际中我使用的时候都是使用Spring创建的Action,这个时候使用的是SpringObjectFactory
  6.             action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
  7.         }
  8.          ....... 
  9.           catch  (Exception e) {
  10.             ........
  11.              throw   new  XWorkException(gripe, e, proxy.getConfig());
  12.         }  finally  {
  13.             UtilTimerStack.pop(timerKey);
  14.         }
  15.          if  (actionEventListener !=  null ) {
  16.             action = actionEventListener.prepare(action, stack);
  17.         }
  18.     }
    接下来看看DefaultActionInvocation 的invoke方法。
  1.      public  String invoke()  throws  Exception {
  2.         String profileKey =  "invoke: " ;
  3.          try  {
  4.             UtilTimerStack.push(profileKey);
  5.             
  6.              if  (executed) {
  7.                  throw   new  IllegalStateException( "Action has already executed" );
  8.             }
  9.                  //先执行interceptors
  10.              if  (interceptors.hasNext()) {
  11.                  final  InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
  12.                 UtilTimerStack.profile( "interceptor: " +interceptor.getName(), 
  13.                          new  UtilTimerStack.ProfilingBlock<String>() {
  14.                              public  String doProfiling()  throws  Exception {
  15.                                 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation. this );
  16.                                  return   null ;
  17.                             }
  18.                 });
  19.             }  else  {
  20.                          //interceptor执行完了之后执行action
  21.                 resultCode = invokeActionOnly();
  22.             }
  23.              if  (!executed) {
  24.                  if  (preResultListeners !=  null ) {
  25.                      for  (Iterator iterator = preResultListeners.iterator();
  26.                         iterator.hasNext();) {
  27.                         PreResultListener listener = (PreResultListener) iterator.next();
  28.                         
  29.                         String _profileKey= "preResultListener: " ;
  30.                          try  {
  31.                             UtilTimerStack.push(_profileKey);
  32.                             listener.beforeResult( this , resultCode);
  33.                         }
  34.                          finally  {
  35.                             UtilTimerStack.pop(_profileKey);
  36.                         }
  37.                     }
  38.                 }
  39.                  // now execute the result, if we're supposed to
  40.                  if  (proxy.getExecuteResult()) {
  41.                     executeResult();
  42.                 }
  43.                 executed =  true ;
  44.             }
  45.              return  resultCode;
  46.         }
  47.          finally  {
  48.             UtilTimerStack.pop(profileKey);
  49.         }
  50.     }
     看程序中的if(interceptors.hasNext() )语句,当然,interceptors里存储的是 interceptorMapping列表(它包括一个Interceptor和一个name), 所有的截拦器必须实现 Interceptor的intercept 方法,而该方法的参数恰恰又是 ActionInvocation ,在 intercept 方法中还是调用 invocation.invoke() ,从而实现了一个 Interceptor链的调用。当所有的Interceptor执行完,最后调用invokeActionOnly方法来执行Action相应的方法。
  1.      protected  String invokeAction(Object action, ActionConfig actionConfig)  throws  Exception {
  2.         String methodName = proxy.getMethod();
  3.         String timerKey =  "invokeAction: " +proxy.getActionName();
  4.          try  {
  5.             UtilTimerStack.push(timerKey);
  6.             
  7.              boolean  methodCalled =  false ;
  8.             Object methodResult =  null ;
  9.             Method method =  null ;
  10.              try  {
  11.                  //获得Action对应的方法
  12.                 method = getAction().getClass().getMethod(methodName,  new  Class[ 0 ]);
  13.             }  catch  (NoSuchMethodException e) {
  14.                  try  {
  15.                      //如果没有对应的方法,则使用do+Xxxx来再次获得方法
  16.                     String altMethodName =  "do"  + methodName.substring( 01 ).toUpperCase() + methodName.substring( 1 );
  17.                     method = getAction().getClass().getMethod(altMethodName,  new  Class[ 0 ]);
  18.                 }  catch  (NoSuchMethodException e1) {
  19.                     .....
  20.                 }
  21.             }
  22.             
  23.              if  (!methodCalled) {
  24.                 methodResult = method.invoke(action,  new  Object[ 0 ]);
  25.             }
  26.              //根据不同的Result类型返回不同值
  27.              if  (methodResult  instanceof  Result) {
  28.                  this .explicitResult = (Result) methodResult;
  29.                  return   null ;
  30.             }  else  {
  31.                  return  (String) methodResult;
  32.             }
  33.         }
  34.         ....
  35.         }  finally  {
  36.             UtilTimerStack.pop(timerKey);
  37.         }
  38.     }
      好了,action执行完了,还要根据ResultConfig返回到view,也就是在invoke方法中调用executeResult方法。
  1.      private   void  executeResult()  throws  Exception {
  2.          //根据ResultConfig创建Result
  3.         result = createResult();
  4.         String timerKey =  "executeResult: " +getResultCode();
  5.          try  {
  6.             UtilTimerStack.push(timerKey);
  7.              if  (result !=  null ) {
  8.                  //这儿正式执行:)
  9.                 //可以参考Result的实现,如用了比较多的 ServletDispatcherResult,ServletActionRedirectResult,ServletRedirectResult
  10.                 result.execute( this );
  11.             }  else   if  (resultCode !=  null  && !Action.NONE.equals(resultCode)) {
  12.                  throw   new  ConfigurationException( "No result defined for action "  + getAction().getClass().getName() 
  13.                         +  " and result "  + getResultCode(), proxy.getConfig());
  14.             }  else  {
  15.                  if  (LOG.isDebugEnabled()) {
  16.                     LOG.debug( "No result returned for action " +getAction().getClass().getName()+ " at " +proxy.getConfig().getLocation());
  17.                 }
  18.             }
  19.         }  finally  {
  20.             UtilTimerStack.pop(timerKey);
  21.         }
  22.     }
  1.      public  Result createResult()  throws  Exception {
  2.          if  (explicitResult !=  null ) {
  3.             Result ret = explicitResult;
  4.             explicitResult =  null ;;
  5.              return  ret;
  6.         }
  7.         ActionConfig config = proxy.getConfig();
  8.         Map results = config.getResults();
  9.         ResultConfig resultConfig =  null ;
  10.          synchronized  (config) {
  11.              try  {
  12.                  //根据result名称获得ResultConfig,resultCode就是result的name
  13.                 resultConfig = (ResultConfig) results.get(resultCode);
  14.             }  catch  (NullPointerException e) {
  15.             }
  16.              if  (resultConfig ==  null ) {
  17.                  //如果找不到对应name的ResultConfig,则使用name为*的Result
  18.                 resultConfig = (ResultConfig) results.get( "*" );
  19.             }
  20.         }
  21.          if  (resultConfig !=  null ) {
  22.              try  {
  23.                  //参照StrutsObjectFactory的代码
  24.                 Result result = objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
  25.                  return  result;
  26.             }  catch  (Exception e) {
  27.                 LOG.error( "There was an exception while instantiating the result of type "  + resultConfig.getClassName(), e);
  28.                  throw   new  XWorkException(e, resultConfig);
  29.             }
  30.         }  else   if  (resultCode !=  null  && !Action.NONE.equals(resultCode) && unknownHandler !=  null ) {
  31.              return  unknownHandler.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
  32.         }
  33.          return   null ;
  34.     }
  35.     // StrutsObjectFactory
  36.     public  Result buildResult(ResultConfig resultConfig, Map extraContext)  throws  Exception {
  37.         String resultClassName = resultConfig.getClassName();
  38.          if  (resultClassName ==  null )
  39.              return   null ;
  40.          //创建Result,因为Result是有状态的,所以每次请求都新建一个
  41.         Object result = buildBean(resultClassName, extraContext);
  42.         reflectionProvider.setProperties(resultConfig.getParams(), result, extraContext);
  43.          if  (result  instanceof  Result)
  44.              return  (Result) result;
  45.          throw   new  ConfigurationException(result.getClass().getName() +  " does not implement Result." );
  46.     }

    最后的时序图我拷贝开头url里那位作者的图吧。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值