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

           

          Struts2架構圖

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

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

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

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

                      initLogging();

                      dispatcher 
          = createDispatcher(filterConfig);
                      dispatcher.init();
                      dispatcher.getContainer().inject(
          this);
                      
          //讀取初始參數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);
                  } 
          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初始參數值
                  
          //如果沒有配置就使用默認的"struts-default.xml,struts-plugin.xml,struts.xml",
                  
          //這兒就可以看出為什么默認的配置文件必須取名為這三個名稱了
                  
          //如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可
                  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又實現ConfigurationProvider接口。類XmlConfigurationProvider負責配置文件的讀取和解析,addAction()方法負責讀取<action>標簽,并將數據保存在ActionConfig中;addResultTypes()方法負責將<result-type>標簽轉化為ResultTypeConfig對象;loadInterceptors()方法負責將<interceptor>標簽轉化為InterceptorConfi對象;loadInterceptorStack()方法負責將<interceptor-ref>標簽轉化為InterceptorStackConfig對象;loadInterceptorStacks()方法負責將<interceptor-stack>標簽轉化成InterceptorStackConfig對象。而上面的方法最終會被addPackage()方法調用,將所讀取到的數據匯集到PackageConfig對象中。來看XmlConfigurationProvider的源代碼,詳細的我自己也就大體瀏覽了一下,各位可以自己研讀。
              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;
              }
              這兒發現一個配置上的小技巧,我的xwork2.0.*是沒有的,但是看源碼是看到xwork2.1.*是可以的。繼續看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,配置類全名和實現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);
                          }
                          
                      }
                  }
              }
              好了,現在再回到FilterDispatcher,每次發送一個Request,FilterDispatcher都會調用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);
                      
          //根據content type來使用不同的Request封裝,可以參見Dispatcher的wrapRequest
                      request = prepareDispatcherAndWrapRequest(request, response);
                      ActionMapping mapping;
                      
          try {
                          
          //根據url取得對應的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;
                      }
                      
          //如果找不到對應的action配置,則直接返回。比如你輸入***.jsp等等
                      
          //這兒有個例外,就是如果path是以“/struts”開頭,則到初始參數packages配置的包路徑去查找對應的靜態資源并輸出到頁面流中,當然.class文件除外。如果再沒有則跳轉到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的調用是通過ActionProxy實現的,其實就是調用了ActionProxy.execute()方法,而該方法又調用了ActionInvocation.invoke()方法。歸根到底,最后調用的是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);
                  }
                  
          //創建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);
                      
          //這兒默認建立Action是StrutsObjectFactory,實際中我使用的時候都是使用Spring創建的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");
                      }
                          
          //先執行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執行完了之后執行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返回之前調用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())語句,當然,interceptors里存儲的是interceptorMapping列表(它包括一個Interceptor和一個name),所有的截攔器必須實現Interceptor的intercept方法,而該方法的參數恰恰又是ActionInvocation,在intercept方法中還是調用invocation.invoke(),從而實現了一個Interceptor鏈的調用。當所有的Interceptor執行完,最后調用invokeActionOnly方法來執行Action相應的方法。
              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 {
                          
          //獲得需要執行的方法
                          method = getAction().getClass().getMethod(methodName, new Class[0]);
                      } 
          catch (NoSuchMethodException e) {
                          
          //如果沒有對應的方法,則使用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]);
                      }
                      
          //根據不同的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執行完了,還要根據ResultConfig返回到view,也就是在invoke方法中調用executeResult方法。
              private void executeResult() throws Exception {
                  
          //根據ResultConfig創建Result
                  result = createResult();
                  String timerKey 
          = "executeResult: "+getResultCode();
                  
          try {
                      UtilTimerStack.push(timerKey);
                      
          if (result != null) {
                          
          //這兒正式執行:)
                          
          //可以參考Result的實現,如用了比較多的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 {
                          
          //根據result名稱獲得ResultConfig,resultCode就是result的name
                          resultConfig = (ResultConfig) results.get(resultCode);
                      } 
          catch (NullPointerException e) {
                      }
                      
          if (resultConfig == null) {
                          
          //如果找不到對應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;
                  
          //創建Result,因為Result是有狀態的,所以每次請求都新建一個
                  Object result = buildBean(resultClassName, extraContext);
                  
          //這句很重要,后面將會談到,reflectionProvider參見OgnlReflectionProvider;
                  
          //resultConfig.getParams()就是result配置文件里所配置的參數<param></param>
                  
          //setProperties方法最終調用的是Ognl類的setValue方法
                  
          //這句其實就是把param名值設置到根對象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的查找值和設置值都是使用Ognl來實現的。關于Ognl的介紹可以到其官方網站查看http://www.ognl.org/,我在網上也找到另外一篇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的請求流程基本上就結束了。其實我覺得做項目把Struts2參考文檔看兩遍就可以了,呵呵!(寫博客比看代碼還累)


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

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

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


          網站導航:
           
          主站蜘蛛池模板: 冷水江市| 屏南县| 台中县| 龙里县| 高邑县| 景宁| 井冈山市| 德令哈市| 武宁县| 荃湾区| 东乌珠穆沁旗| 江川县| 高阳县| 嫩江县| 商河县| 三明市| 台前县| 灵璧县| 七台河市| 宁陵县| 平江县| 富民县| 东安县| 灌阳县| 南澳县| 武城县| 政和县| 卢龙县| 阿拉善右旗| 荔浦县| 南开区| 陆川县| 东光县| 通州市| 图木舒克市| 礼泉县| 新闻| 上饶市| 色达县| 新竹市| 密山市|