隨筆-295  評論-26  文章-1  trackbacks-0

           

          Struts2架構(gòu)圖

                  請求首先通過Filter chain,F(xiàn)ilter主要包括ActionContextCleanUp,它主要清理當(dāng)前線程的ActionContext和Dispatcher;FilterDispatcher主要通過AcionMapper來決定需要調(diào)用哪個Action。
                  ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里創(chuàng)建ActionProxy,ActionProxy創(chuàng)建ActionInvocation,然后ActionInvocation調(diào)用Interceptors,執(zhí)行Action本身,創(chuàng)建Result并返回,當(dāng)然,如果要在返回之前做些什么,可以實現(xiàn)PreResultListener。

          Struts2部分類介紹
          這部分從Struts2參考文檔中翻譯就可以了。
          ActionMapper
                  ActionMapper其實是HttpServletRequest和Action調(diào)用請求的一個映射,它屏蔽了Action對于Request等java Servlet類的依賴。Struts2中它的默認(rèn)實現(xiàn)類是DefaultActionMapper,ActionMapper很大的用處可以根據(jù)自己的需要來設(shè)計url格式,它自己也有Restful的實現(xiàn),具體可以參考文檔的docs\actionmapper.html。
          ActionProxy&ActionInvocation
                  Action的一個代理,由ActionProxyFactory創(chuàng)建,它本身不包括Action實例,默認(rèn)實現(xiàn)DefaultActionProxy是由ActionInvocation持有Action實例。ActionProxy作用是如何取得Action,無論是本地還是遠(yuǎn)程。而ActionInvocation的作用是如何執(zhí)行Action,攔截器的功能就是在ActionInvocation中實現(xiàn)的。
          ConfigurationProvider&Configuration
                  ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其實現(xiàn)類XmlConfigurationProvider及其子類StrutsXmlConfigurationProvider來解析,

          Struts2請求流程
          1、客戶端發(fā)送請求
          2、請求先通過ActionContextCleanUp-->FilterDispatcher
          3、FilterDispatcher通過ActionMapper來決定這個Request需要調(diào)用哪個Action
          4、如果ActionMapper決定調(diào)用某個Action,F(xiàn)ilterDispatcher把請求的處理交給ActionProxy,這兒已經(jīng)轉(zhuǎn)到它的Delegate--Dispatcher來執(zhí)行
          5、ActionProxy根據(jù)ActionMapping和ConfigurationManager找到需要調(diào)用的Action類
          6、ActionProxy創(chuàng)建一個ActionInvocation的實例
          7、ActionInvocation調(diào)用真正的Action,當(dāng)然這涉及到相關(guān)攔截器的調(diào)用
          8、Action執(zhí)行完畢,ActionInvocation創(chuàng)建Result并返回,當(dāng)然,如果要在返回之前做些什么,可以實現(xiàn)PreResultListener。添加PreResultListener可以在Interceptor中實現(xiàn),不知道其它還有什么方式?

          Struts2(2.1.2)部分源碼閱讀
              從org.apache.struts2.dispatcher.FilterDispatcher開始
              //創(chuàng)建Dispatcher,此類是一個Delegate,它是真正完成根據(jù)url解析,讀取對應(yīng)Action的地方
              public void init(FilterConfig filterConfig) throws ServletException {
                  
          try {
                      
          this.filterConfig = filterConfig;

                      initLogging();

                      dispatcher 
          = createDispatcher(filterConfig);
                      dispatcher.init();
                      dispatcher.getContainer().inject(
          this);
                      
          //讀取初始參數(shù)pakages,調(diào)用parse(),解析成類似/org/apache/struts2/static,/template的數(shù)組
                      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);
                  } 
          finally {
                      ActionContext.setContext(
          null);
                  }
              }
               順著流程我們看Disptcher的init方法。init方法里就是初始讀取一些配置文件等,先看init_DefaultProperties,主要是讀取properties配置文件。
              private void init_DefaultProperties() {
                  configurationManager.addConfigurationProvider(
          new DefaultPropertiesProvider());
              }
              打開DefaultPropertiesProvider
              public void register(ContainerBuilder builder, LocatableProperties props)
                      
          throws ConfigurationException {
                  
                  Settings defaultSettings 
          = null;
                  
          try {
                      defaultSettings 
          = new PropertiesSettings("org/apache/struts2/default");
                  } 
          catch (Exception e) {
                      
          throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e);
                  }
                  
                  loadSettings(props, defaultSettings);
              }

              
          //PropertiesSettings
              
          //讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在classpath里的struts.properties里覆寫
              public PropertiesSettings(String name) {
                  
                  URL settingsUrl 
          = ClassLoaderUtils.getResource(name + ".properties", getClass());
                  
                  
          if (settingsUrl == null) {
                      LOG.debug(name 
          + ".properties missing");
                      settings 
          = new LocatableProperties();
                      
          return;
                  }
                  
                  settings 
          = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));

                  
          // Load settings
                  InputStream in = null;
                  
          try {
                      in 
          = settingsUrl.openStream();
                      settings.load(in);
                  } 
          catch (IOException e) {
                      
          throw new StrutsException("Could not load " + name + ".properties:" + e, e);
                  } 
          finally {
                      
          if(in != null) {
                          
          try {
                              in.close();
                          } 
          catch(IOException io) {
                              LOG.warn(
          "Unable to close input stream", io);
                          }
                      }
                  }
              }
              再來看init_TraditionalXmlConfigurations方法,這個是讀取struts-default.xml和Struts.xml的方法。
              private void init_TraditionalXmlConfigurations() {
                  
          //首先讀取web.xml中的config初始參數(shù)值
                  
          //如果沒有配置就使用默認(rèn)的"struts-default.xml,struts-plugin.xml,struts.xml",
                  
          //這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個名稱了
                  
          //如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可
                  String configPaths = initParams.get("config");
                  
          if (configPaths == null) {
                      configPaths 
          = DEFAULT_CONFIGURATION_PATHS;
                  }
                  String[] files 
          = configPaths.split("\\s*[,]\\s*");
                  
          //依次解析配置文件,xwork.xml單獨解析
                  for (String file : files) {
                      
          if (file.endsWith(".xml")) {
                          
          if ("xwork.xml".equals(file)) {
                              configurationManager.addConfigurationProvider(
          new XmlConfigurationProvider(file, false));
                          } 
          else {
                              configurationManager.addConfigurationProvider(
          new StrutsXmlConfigurationProvider(file, false, servletContext));
                          }
                      } 
          else {
                          
          throw new IllegalArgumentException("Invalid configuration file name");
                      }
                  }
              }
              對于其它配置文件只用StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實現(xiàn)ConfigurationProvider接口。類XmlConfigurationProvider負(fù)責(zé)配置文件的讀取和解析,addAction()方法負(fù)責(zé)讀取<action>標(biāo)簽,并將數(shù)據(jù)保存在ActionConfig中;addResultTypes()方法負(fù)責(zé)將<result-type>標(biāo)簽轉(zhuǎn)化為ResultTypeConfig對象;loadInterceptors()方法負(fù)責(zé)將<interceptor>標(biāo)簽轉(zhuǎn)化為InterceptorConfi對象;loadInterceptorStack()方法負(fù)責(zé)將<interceptor-ref>標(biāo)簽轉(zhuǎn)化為InterceptorStackConfig對象;loadInterceptorStacks()方法負(fù)責(zé)將<interceptor-stack>標(biāo)簽轉(zhuǎn)化成InterceptorStackConfig對象。而上面的方法最終會被addPackage()方法調(diào)用,將所讀取到的數(shù)據(jù)匯集到PackageConfig對象中。來看XmlConfigurationProvider的源代碼,詳細(xì)的我自己也就大體瀏覽了一下,各位可以自己研讀。
              protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
                  PackageConfig.Builder newPackage 
          = buildPackageContext(packageElement);

                  
          if (newPackage.isNeedsRefresh()) {
                      
          return newPackage.build();
                  }
                  .

                  addResultTypes(newPackage, packageElement);
                  loadInterceptors(newPackage, packageElement);
                  loadDefaultInterceptorRef(newPackage, packageElement);
                  loadDefaultClassRef(newPackage, packageElement);
                  loadGlobalResults(newPackage, packageElement);
                  loadGobalExceptionMappings(newPackage, packageElement);
                  NodeList actionList 
          = packageElement.getElementsByTagName("action");

                  
          for (int i = 0; i < actionList.getLength(); i++) {
                      Element actionElement 
          = (Element) actionList.item(i);
                      addAction(actionElement, newPackage);
                  }
                  loadDefaultActionRef(newPackage, packageElement);
                  PackageConfig cfg 
          = newPackage.build();
                  configuration.addPackageConfig(cfg.getName(), cfg);
                  
          return cfg;
              }
              這兒發(fā)現(xiàn)一個配置上的小技巧,我的xwork2.0.*是沒有的,但是看源碼是看到xwork2.1.*是可以的。繼續(xù)看XmlConfigurationProvider的源代碼:
              private List loadConfigurationFiles(String fileName, Element includeElement) {
                  List
          <Document> docs = new ArrayList<Document>();
                  
          if (!includedFileNames.contains(fileName)) {
                          
                          Element rootElement 
          = doc.getDocumentElement();
                          NodeList children 
          = rootElement.getChildNodes();
                          
          int childSize = children.getLength();

                          
          for (int i = 0; i < childSize; i++) {
                              Node childNode 
          = children.item(i);

                              
          if (childNode instanceof Element) {
                                  Element child 
          = (Element) childNode;

                                  
          final String nodeName = child.getNodeName();
                                  
          //解析每個action配置是,對于include文件可以使用通配符*來進行配置
                                  
          //如Struts.xml中可配置成<include file="actions_*.xml"/>
                                  if (nodeName.equals("include")) {
                                      String includeFileName 
          = child.getAttribute("file");
                                      
          if(includeFileName.indexOf('*'!= -1 ) {
                                          ClassPathFinder wildcardFinder 
          = new ClassPathFinder();
                                          wildcardFinder.setPattern(includeFileName);
                                          Vector
          <String> wildcardMatches = wildcardFinder.findMatches();
                                          
          for (String match : wildcardMatches) {
                                              docs.addAll(loadConfigurationFiles(match, child));
                                          }
                                      }
                                      
          else {
                                          
                                          docs.addAll(loadConfigurationFiles(includeFileName, child));    
                                      }    
                              }
                          }
                          }
                          docs.add(doc);
                          loadedFileUrls.add(url.toString());
                      }
                  }
                  
          return docs;
              }
              init_CustomConfigurationProviders方式初始自定義的Provider,配置類全名和實現(xiàn)ConfigurationProvider接口,用逗號隔開即可。
              private void init_CustomConfigurationProviders() {
                  String configProvs 
          = initParams.get("configProviders");
                  
          if (configProvs != null) {
                      String[] classes 
          = configProvs.split("\\s*[,]\\s*");
                      
          for (String cname : classes) {
                          
          try {
                              Class cls 
          = ClassLoaderUtils.loadClass(cname, this.getClass());
                              ConfigurationProvider prov 
          = (ConfigurationProvider)cls.newInstance();
                              configurationManager.addConfigurationProvider(prov);
                          }
                          
                      }
                  }
              }
              好了,現(xiàn)在再回到FilterDispatcher,每次發(fā)送一個Request,F(xiàn)ilterDispatcher都會調(diào)用doFilter方法。
              public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

                  HttpServletRequest request 
          = (HttpServletRequest) req;
                  HttpServletResponse response 
          = (HttpServletResponse) res;
                  ServletContext servletContext 
          = getServletContext();

                  String timerKey 
          = "FilterDispatcher_doFilter: ";
                  
          try {
                      ValueStack stack 
          = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
                      ActionContext ctx 
          = new ActionContext(stack.getContext());
                      ActionContext.setContext(ctx);
                      
                      UtilTimerStack.push(timerKey);
                      
          //根據(jù)content type來使用不同的Request封裝,可以參見Dispatcher的wrapRequest
                      request = prepareDispatcherAndWrapRequest(request, response);
                      ActionMapping mapping;
                      
          try {
                          
          //根據(jù)url取得對應(yīng)的Action的配置信息--ActionMapping,actionMapper是通過Container的inject注入的
                          mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());
                      } 
          catch (Exception ex) {
                          log.error(
          "error getting ActionMapping", ex);
                          dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
                          
          return;
                      }
                      
          //如果找不到對應(yīng)的action配置,則直接返回。比如你輸入***.jsp等等
                      
          //這兒有個例外,就是如果path是以“/struts”開頭,則到初始參數(shù)packages配置的包路徑去查找對應(yīng)的靜態(tài)資源并輸出到頁面流中,當(dāng)然.class文件除外。如果再沒有則跳轉(zhuǎn)到404
                      if (mapping == null) {
                          
          // there is no action in this request, should we look for a static resource?
                          String resourcePath = RequestUtils.getServletPath(request);

                          
          if ("".equals(resourcePath) && null != request.getPathInfo()) {
                              resourcePath 
          = request.getPathInfo();
                          }

                          
          if (serveStatic && resourcePath.startsWith("/struts")) {
                              String name 
          = resourcePath.substring("/struts".length());
                              findStaticResource(name, request, response);
                          } 
          else {
                              chain.doFilter(request, response);
                          }
                          
          return;
                      }
                      
          //正式開始Action的方法了
                      dispatcher.serviceAction(request, response, servletContext, mapping);

                  } 
          finally {
                      
          try {
                          ActionContextCleanUp.cleanUp(req);
                      } 
          finally {
                          UtilTimerStack.pop(timerKey);
                      }
                  }
              }
              Dispatcher類的serviceAction方法:
              public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping) throws ServletException {

                  Map
          <String, Object> extraContext = createContextMap(request, response, mapping, context);

                  
          // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
                  ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
                  
          if (stack != null) {
                      extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
                  }

                  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 proxy 
          = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                              namespace, name, method, extraContext, 
          truefalse);

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

                      
          // 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();
                      }

                      
          // If there was a previous value stack then set it back onto the request
                      if (stack != null) {
                          request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
                      }
                  } 
          catch (ConfigurationException e) {
                      LOG.error(
          "Could not find action or result", e);
                      sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
                  } 
          catch (Exception e) {
                      sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
                  } 
          finally {
                      UtilTimerStack.pop(timerKey);
                  }
              }
              第一句createContextMap()方法,該方法主要把Application、Session、Request的key value值拷貝到Map中,并放在HashMap<String,Object>中,可以參見createContextMap方法:
              public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,
                      ActionMapping mapping, ServletContext context) {

                  
          // request map wrapping the http request objects
                  Map requestMap = new RequestMap(request);
                  
          // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately
                  Map params = new HashMap(request.getParameterMap());
                  
          // session map wrapping the http session
                  Map session = new SessionMap(request);
                  
          // application map wrapping the ServletContext
                  Map application = new ApplicationMap(context);
                  Map
          <String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);
                  extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);
                  
          return extraContext;
              }
              后面才是最主要的--ActionProxy,ActionInvocation。ActionProxy是Action的一個代理類,也就是說Action的調(diào)用是通過ActionProxy實現(xiàn)的,其實就是調(diào)用了ActionProxy.execute()方法,而該方法又調(diào)用了ActionInvocation.invoke()方法。歸根到底,最后調(diào)用的是DefaultActionInvocation.invokeAction()方法。先看DefaultActionInvocation的init方法。
              public void init(ActionProxy proxy) {
                  
          this.proxy = proxy;
                  Map contextMap 
          = createContextMap();

                  
          // Setting this so that other classes, like object factories, can use the ActionProxy and other
                  
          // contextual information to operate
                  ActionContext actionContext = ActionContext.getContext();

                  
          if(actionContext != null) {
                      actionContext.setActionInvocation(
          this);
                  }
                  
          //創(chuàng)建Action,可Struts2里是每次請求都新建一個Action
                  createAction(contextMap);

                  
          if (pushAction) {
                      stack.push(action);
                      contextMap.put(
          "action", action);
                  }

                  invocationContext 
          = new ActionContext(contextMap);
                  invocationContext.setName(proxy.getActionName());

                  
          // get a new List so we don't get problems with the iterator if someone changes the list
                  List interceptorList = new ArrayList(proxy.getConfig().getInterceptors());
                  interceptors 
          = interceptorList.iterator();
              }

              
          protected void createAction(Map contextMap) {
                  
          // load action
                  String timerKey = "actionCreate: "+proxy.getActionName();
                  
          try {
                      UtilTimerStack.push(timerKey);
                      
          //這兒默認(rèn)建立Action是StrutsObjectFactory,實際中我使用的時候都是使用Spring創(chuàng)建的Action,這個時候使用的是SpringObjectFactory
                      action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
                  } 
                  ..
                  } 
          finally {
                      UtilTimerStack.pop(timerKey);
                  }

                  
          if (actionEventListener != null) {
                      action 
          = actionEventListener.prepare(action, stack);
                  }
              }
              接下來看看DefaultActionInvocation 的invoke方法。
             public String invoke() throws Exception {
                  String profileKey 
          = "invoke: ";
                  
          try {
                      UtilTimerStack.push(profileKey);
                      
                      
          if (executed) {
                          
          throw new IllegalStateException("Action has already executed");
                      }
                          
          //先執(zhí)行interceptors
                      if (interceptors.hasNext()) {
                          
          final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
                          UtilTimerStack.profile(
          "interceptor: "+interceptor.getName(), 
                                  
          new UtilTimerStack.ProfilingBlock<String>() {
                                      
          public String doProfiling() throws Exception {
                                          resultCode 
          = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
                                          
          return null;
                                      }
                          });
                      } 
          else {
                                  
          //interceptor執(zhí)行完了之后執(zhí)行action
                          resultCode = invokeActionOnly();
                      }

                      
          // this is needed because the result will be executed, then control will return to the Interceptor, which will
                      
          // return above and flow through again
                      if (!executed) {
                                  
          //在Result返回之前調(diào)用preResultListeners
                          if (preResultListeners != null) {
                              
          for (Iterator iterator = preResultListeners.iterator();
                                  iterator.hasNext();) {
                                  PreResultListener listener 
          = (PreResultListener) iterator.next();
                                  
                                  String _profileKey
          ="preResultListener: ";
                                  
          try {
                                      UtilTimerStack.push(_profileKey);
                                      listener.beforeResult(
          this, resultCode);
                                  }
                                  
          finally {
                                      UtilTimerStack.pop(_profileKey);
                                  }
                              }
                          }

                          
          // now execute the result, if we're supposed to
                          if (proxy.getExecuteResult()) {
                              executeResult();
                          }

                          executed 
          = true;
                      }

                      
          return resultCode;
                  }
                  
          finally {
                      UtilTimerStack.pop(profileKey);
                  }
              }
              看程序中的if(interceptors.hasNext())語句,當(dāng)然,interceptors里存儲的是interceptorMapping列表(它包括一個Interceptor和一個name),所有的截攔器必須實現(xiàn)Interceptor的intercept方法,而該方法的參數(shù)恰恰又是ActionInvocation,在intercept方法中還是調(diào)用invocation.invoke(),從而實現(xiàn)了一個Interceptor鏈的調(diào)用。當(dāng)所有的Interceptor執(zhí)行完,最后調(diào)用invokeActionOnly方法來執(zhí)行Action相應(yīng)的方法。
              protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
                  String methodName 
          = proxy.getMethod();
                  String timerKey 
          = "invokeAction: "+proxy.getActionName();
                  
          try {
                      UtilTimerStack.push(timerKey);
                      
                      
          boolean methodCalled = false;
                      Object methodResult 
          = null;
                      Method method 
          = null;
                      
          try {
                          
          //獲得需要執(zhí)行的方法
                          method = getAction().getClass().getMethod(methodName, new Class[0]);
                      } 
          catch (NoSuchMethodException e) {
                          
          //如果沒有對應(yīng)的方法,則使用do+Xxxx來再次獲得方法
                          try {
                              String altMethodName 
          = "do" + methodName.substring(01).toUpperCase() + methodName.substring(1);
                              method 
          = getAction().getClass().getMethod(altMethodName, new Class[0]);
                          } 
          catch (NoSuchMethodException e1) {
                              
          // well, give the unknown handler a shot
                              if (unknownHandler != null) {
                                  
          try {
                                      methodResult 
          = unknownHandler.handleUnknownActionMethod(action, methodName);
                                      methodCalled 
          = true;
                                  } 
          catch (NoSuchMethodException e2) {
                                      
          // throw the original one
                                      throw e;
                                  }
                              } 
          else {
                                  
          throw e;
                              }
                          }
                      }
                      
                      
          if (!methodCalled) {
                          methodResult 
          = method.invoke(action, new Object[0]);
                      }
                      
          //根據(jù)不同的Result類型返回不同值
                      
          //如輸出流Result
                      if (methodResult instanceof Result) {
                          
          this.explicitResult = (Result) methodResult;
                          
          return null;
                      } 
          else {
                          
          return (String) methodResult;
                      }
                  } 
          catch (NoSuchMethodException e) {
                      
          throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
                  } 
          catch (InvocationTargetException e) {
                      
          // We try to return the source exception.
                      Throwable t = e.getTargetException();

                      
          if (actionEventListener != null) {
                          String result 
          = actionEventListener.handleException(t, getStack());
                          
          if (result != null) {
                              
          return result;
                          }
                      }
                      
          if (t instanceof Exception) {
                          
          throw(Exception) t;
                      } 
          else {
                          
          throw e;
                      }
                  } 
          finally {
                      UtilTimerStack.pop(timerKey);
                  }
              }
              好了,action執(zhí)行完了,還要根據(jù)ResultConfig返回到view,也就是在invoke方法中調(diào)用executeResult方法。
              private void executeResult() throws Exception {
                  
          //根據(jù)ResultConfig創(chuàng)建Result
                  result = createResult();
                  String timerKey 
          = "executeResult: "+getResultCode();
                  
          try {
                      UtilTimerStack.push(timerKey);
                      
          if (result != null) {
                          
          //這兒正式執(zhí)行:)
                          
          //可以參考Result的實現(xiàn),如用了比較多的ServletDispatcherResult,ServletActionRedirectResult,ServletRedirectResult
                          result.execute(this);
                      } 
          else if (resultCode != null && !Action.NONE.equals(resultCode)) {
                          
          throw new ConfigurationException("No result defined for action " + getAction().getClass().getName() 
                                  
          + " and result " + getResultCode(), proxy.getConfig());
                      } 
          else {
                          
          if (LOG.isDebugEnabled()) {
                              LOG.debug(
          "No result returned for action "+getAction().getClass().getName()+" at "+proxy.getConfig().getLocation());
                          }
                      }
                  } 
          finally {
                      UtilTimerStack.pop(timerKey);
                  }
              }
              
          public Result createResult() throws Exception {
                  
          if (explicitResult != null) {
                      Result ret 
          = explicitResult;
                      explicitResult 
          = null;;
                      
          return ret;
                  }
                  ActionConfig config 
          = proxy.getConfig();
                  Map results 
          = config.getResults();
                  ResultConfig resultConfig 
          = null;
                  
          synchronized (config) {
                      
          try {
                          
          //根據(jù)result名稱獲得ResultConfig,resultCode就是result的name
                          resultConfig = (ResultConfig) results.get(resultCode);
                      } 
          catch (NullPointerException e) {
                      }
                      
          if (resultConfig == null) {
                          
          //如果找不到對應(yīng)name的ResultConfig,則使用name為*的Result
                          resultConfig = (ResultConfig) results.get("*");
                      }
                  }
                  
          if (resultConfig != null) {
                      
          try {
                          
          //參照StrutsObjectFactory的代碼
                          Result result = objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
                          
          return result;
                      } 
          catch (Exception e) {
                          LOG.error(
          "There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
                          
          throw new XWorkException(e, resultConfig);
                      }
                  } 
          else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandler != null) {
                      
          return unknownHandler.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
                  }
                  
          return null;
              }
              
          //StrutsObjectFactory
              public Result buildResult(ResultConfig resultConfig, Map extraContext) throws Exception {
                  String resultClassName 
          = resultConfig.getClassName();
                  
          if (resultClassName == null)
                      
          return null;
                  
          //創(chuàng)建Result,因為Result是有狀態(tài)的,所以每次請求都新建一個
                  Object result = buildBean(resultClassName, extraContext);
                  
          //這句很重要,后面將會談到,reflectionProvider參見OgnlReflectionProvider;
                  
          //resultConfig.getParams()就是result配置文件里所配置的參數(shù)<param></param>
                  
          //setProperties方法最終調(diào)用的是Ognl類的setValue方法
                  
          //這句其實就是把param名值設(shè)置到根對象result上
                  reflectionProvider.setProperties(resultConfig.getParams(), result, extraContext);
                  
          if (result instanceof Result)
                      
          return (Result) result;
                  
          throw new ConfigurationException(result.getClass().getName() + " does not implement Result.");
              }
              最后補充一下,Struts2的查找值和設(shè)置值都是使用Ognl來實現(xiàn)的。關(guān)于Ognl的介紹可以到其官方網(wǎng)站查看http://www.ognl.org/,我在網(wǎng)上也找到另外一篇http://www.javaeye.com/topic/254684http://www.javaeye.com/topic/223612。完了來看下面這段小測試程序(其它的Ognl的測試可以自己添加)。
          public class TestOgnl {
              
              
          private User user;
              
          private Map context;
              
              @Before
              
          public void setUp() throws Exception {
              
              }

              @Test
              
          public void ognlGetValue() throws Exception {
              reset();
              Assert.assertEquals(
          "myyate", Ognl.getValue("name", user));
              Assert.assertEquals(
          "cares", Ognl.getValue("dept.name", user));
              Assert.assertEquals(
          "myyate", Ognl.getValue("name", context, user));
              Assert.assertEquals(
          "contextmap", Ognl.getValue("#name", context, user));
              Assert.assertEquals(
          "parker", Ognl.getValue("#pen", context, user));
              }
              
              @Test
              
          public void ognlSetValue() throws Exception {
              reset();
              Ognl.setValue(
          "name", user, "myyateC");
              Assert.assertEquals(
          "myyateC", Ognl.getValue("name", user));
              
              Ognl.setValue(
          "dept.name", user, "caresC");
              Assert.assertEquals(
          "caresC", Ognl.getValue("dept.name", user));
              
              Assert.assertEquals(
          "contextmap", Ognl.getValue("#name", context, user));
              Ognl.setValue(
          "#name", context, user, "contextmapC");
              Assert.assertEquals(
          "contextmapC", Ognl.getValue("#name", context, user));
              
              Assert.assertEquals(
          "parker", Ognl.getValue("#pen", context, user));
              Ognl.setValue(
          "#name", context, user, "parkerC");
              Assert.assertEquals(
          "parkerC", Ognl.getValue("#name", context, user));
              }
              
              
          public static void main(String[] args) throws Exception {
              JUnitCore.runClasses(TestOgnl.
          class);
              }
              
              
          private void reset() {
              user 
          = new User("myyate"new Dept("cares"));
              context 
          = new OgnlContext();
              context.put(
          "pen""parker");
              context.put(
          "name""contextmap");
              }
          }

          class User {
              
          public User(String name, Dept dept) {
              
          this.name = name;
              
          this.dept = dept;
              }
              String name;
              
          private Dept dept;
              
          public Dept getDept() {
                  
          return dept;
              }
              
          public String getName() {
                  
          return name;
              }
              
          public void setDept(Dept dept) {
                  
          this.dept = dept;
              }
              
          public void setName(String name) {
                  
          this.name = name;
              }
          }

          class Dept {
              
          public Dept(String name) {
              
          this.name = name;
              }
              
          private String name;
              
          public String getName() {
                  
          return name;
              }
              
          public void setName(String name) {
                  
          this.name = name;
              }
          }
              這樣,一個Struts2的請求流程基本上就結(jié)束了。其實我覺得做項目把Struts2參考文檔看兩遍就可以了,呵呵!(寫博客比看代碼還累)


          大盤預(yù)測 國富論
          posted on 2009-10-21 10:05 華夢行 閱讀(7480) 評論(2)  編輯  收藏

          評論:
          # re: Struts2架構(gòu)圖 2012-09-21 12:28 | lafe
          受益良多!  回復(fù)  更多評論
            
          # re: Struts2架構(gòu)圖 [未登錄] 2016-01-29 17:22 | Java
          不錯哦,CSDN還開設(shè)了視頻教程呢“Struts2架構(gòu)設(shè)計之路 自主編寫Web開發(fā)框架”http://edu.csdn.net/course/detail/1938  回復(fù)  更多評論
            

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 龙海市| 板桥市| 东丽区| 乌兰县| 商都县| 周宁县| 黄浦区| 长武县| 九寨沟县| 香格里拉县| 洱源县| 讷河市| 桃江县| 元氏县| 台南县| 涪陵区| 沙田区| 合作市| 磴口县| 通化县| 沛县| 旺苍县| 毕节市| 南开区| 繁昌县| 赣州市| 洪泽县| 故城县| 湟源县| 达州市| 安溪县| 房山区| 长沙市| 涟水县| 积石山| 平邑县| 丰镇市| 瑞金市| 安岳县| 阳泉市| 蒙城县|