struts初始化起始於ActionServlet。正如其名,它是Servlet,按照Servlet的聲明週期,struts的初始化放在了init方法之中。java
public void init() throws ServletException { // struts初始化流程放入try/catch中,這樣能夠更好的處理未捕獲的異常或錯誤 try { // 1. 初始化內部國際化信息 initInternal(); // 2. 判斷convertNull,進行特殊類型轉換器註冊 initOther(); // 3. 獲取當前serverlet的url-pattern initServlet(); // 4. 將自身(ActionServlet)放入Servlet上下文中 getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this); // 5. 判斷用戶是否有自定義模塊初始化功能,若存在註冊到模塊工廠 initModuleConfigFactory(); // 6. 初始化模塊默認配置文件 ModuleConfig moduleConfig = initModuleConfig("", config); // 7. 初始化自定義國際化信息 initModuleMessageResources(moduleConfig); // 8. 初始化數據源 initModuleDataSources(moduleConfig); // 9. 初始化struts插件 initModulePlugIns(moduleConfig); // 10. 凍結配置信息 moduleConfig.freeze(); // 11. 初始化struts自定義模塊配置信息 Enumeration names = getServletConfig().getInitParameterNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); if (!name.startsWith("config/")) { continue; } String prefix = name.substring(6); moduleConfig = initModuleConfig (prefix, getServletConfig().getInitParameter(name)); initModuleMessageResources(moduleConfig); initModuleDataSources(moduleConfig); initModulePlugIns(moduleConfig); moduleConfig.freeze(); } // 12. 存儲模塊前綴,放入ServletContext this.initModulePrefixes(this.getServletContext()); // 13. 銷燬digester this.destroyConfigDigester(); } catch (UnavailableException ex) { throw ex; } catch (Throwable t) { // The follow error message is not retrieved from internal message // resources as they may not have been able to have been // initialized log.error("Unable to initialize Struts ActionServlet due to an " + "unexpected exception or error thrown, so marking the " + "servlet as unavailable. Most likely, this is due to an " + "incorrect or missing library dependency.", t); throw new UnavailableException(t.getMessage()); } }
使用struts*.jar中的org/apache/struts/action/ActionResources*.properties文件初始化國際化信息。web
1.1 ActionResources*僅包含默認英文和日文兩類,所以初始化過程當中發生的異常信息僅能使用以上兩種語言進行提示;apache
1.2 方法初始化一個MessageResources對象,初始化流程和裏面的內容稍後介紹,目前僅須要知道國際化信息存放到了internal中;app
1.3 若國際化信息初始化失敗(目前僅多是國際化文件未找到),拋出UnavailableExceptionthis
protected MessageResources internal = null; protected void initInternal() throws ServletException { try { internal = MessageResources.getMessageResources(internalName); } catch (MissingResourceException e) { log.error("Cannot load internal resources from '" + internalName + "'", e); throw new UnavailableException ("Cannot load internal resources from '" + internalName + "'"); } }
struts的form bean使用beanUtil進行轉換。initOther對null進行了指定處理,保證特殊類能夠正確轉換爲form bean。url
2.1 參數使用web.xml中actionServlet的參數convertNull;spa
2.2 struts爲保證嚴格的兼容性,建議設置爲true插件
protected void initOther() throws ServletException { String value = null; value = getServletConfig().getInitParameter("config"); if (value != null) { config = value; } // Backwards compatibility for form beans of Java wrapper classes // Set to true for strict Struts 1.0 compatibility value = getServletConfig().getInitParameter("convertNull"); if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value) || "y".equalsIgnoreCase(value) || "1".equalsIgnoreCase(value)) { convertNull = true; } if (convertNull) { ConvertUtils.deregister(); ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class); ConvertUtils.register(new BigIntegerConverter(null), BigInteger.class); ConvertUtils.register(new BooleanConverter(null), Boolean.class); ConvertUtils.register(new ByteConverter(null), Byte.class); ConvertUtils.register(new CharacterConverter(null), Character.class); ConvertUtils.register(new DoubleConverter(null), Double.class); ConvertUtils.register(new FloatConverter(null), Float.class); ConvertUtils.register(new IntegerConverter(null), Integer.class); ConvertUtils.register(new LongConverter(null), Long.class); ConvertUtils.register(new ShortConverter(null), Short.class); } }
獲取ActionServlet對應的url-pattern,即ActionServlet所可以攔截的請求類型,配置的值將存放在ServletContext中。debug
將actionServlet存放到ServletContext中,須要注意的是ActionServlet是單例的。code
struts的配置信息存放在ModuleConfig中(後文詳細介紹),使用工廠模式進行建立ModuleConfigImpl對象,參數使用web.xml中ActionServlet的configFactory參數。這個配置不是必須的,由於Struts有默認的實現工廠類DefaultModuleConfigFactory。
initModuleConfig方法用於初始化struts配置文件,這塊是struts初始化的核心內容。
protected ModuleConfig initModuleConfig(String prefix, String paths) throws ServletException { // :FIXME: Document UnavailableException? (Doesn't actually throw anything) if (log.isDebugEnabled()) { log.debug( "Initializing module path '" + prefix + "' configuration from '" + paths + "'"); } /* * 獲取工程類並建立ModuleConfig: * 1. createFactory常見工廠類,默認是DefaultModuleConfigFactory,若用戶在第5步中自定義了工廠類,將建立該工廠類的實例; * 2. 使用抽象工廠模式,createModuleConfig是抽象方法,用於建立ModuleConfigImpl。DefaultModuleConfigFactory實現createModuleConfig方法並經過prefix區分並建立ModuleConfigImpl */ ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory(); ModuleConfig config = factoryObject.createModuleConfig(prefix); // 初始化解析器 Digester digester = initConfigDigester(); // 若配置多個struts配置文件,遍歷解析 while (paths.length() > 0) { digester.push(config); String path = null; int comma = paths.indexOf(','); if (comma >= 0) { path = paths.substring(0, comma).trim(); paths = paths.substring(comma + 1); } else { path = paths.trim(); paths = ""; } if (path.length() < 1) { break; } this.parseModuleConfigFile(digester, path); } // 使用prefix進行區分,將對應的ModuleConfigImpl存放到ServletContext getServletContext().setAttribute( Globals.MODULE_KEY + config.getPrefix(), config); // 獲取配置文件中的FormBean配置,若是是DynaActionForm進行初始化操做 FormBeanConfig fbs[] = config.findFormBeanConfigs(); for (int i = 0; i < fbs.length; i++) { if (fbs[i].getDynamic()) { fbs[i].getDynaActionFormClass(); } } return config; }
moduleConfig是struts-config.xml配置信息存放的對象,也就是第5步中所說的ModuleConfigImpl,裏面存放有國際化信息:
7.1 國際化信息能夠是多個,每一個國際化對應一個抽象類MessageResources;
7.2 struts對MessageResources的默認實現是PropertyMessageResources;
7.3 國際化信息內容在首次獲取時寫入HashMap;
protected void initModuleMessageResources(ModuleConfig config) throws ServletException { // 獲取struts-config.xml配置的國際化信息 MessageResourcesConfig mrcs[] = config.findMessageResourcesConfigs(); for (int i = 0; i < mrcs.length; i++) { if ((mrcs[i].getFactory() == null) || (mrcs[i].getParameter() == null)) { continue; } if (log.isDebugEnabled()) { log.debug( "Initializing module path '" + config.getPrefix() + "' message resources from '" + mrcs[i].getParameter() + "'"); } // 若配置了國際化工廠類,使用自定義工廠類,不然使用默認的PropertyMessageResourcesFactory String factory = mrcs[i].getFactory(); MessageResourcesFactory.setFactoryClass(factory); MessageResourcesFactory factoryObject = MessageResourcesFactory.createFactory(); // 設置國際化配置信息 factoryObject.setConfig(mrcs[i]); // 經過配置中的parameter參數建立國際化信息存儲對象,parameter用於穩定國際化文件的配置 MessageResources resources = factoryObject.createResources(mrcs[i].getParameter()); // 參數null用於設置國際化信息未找到時的顯示內容,true-null false-返回包含key值、國際化等信息的字符串 resources.setReturnNull(mrcs[i].getNull()); // 設置轉義參數:對單引號'作特殊處理 resources.setEscape(mrcs[i].isEscape()); // 將國際化信息以配置信息的key參數和prefix做爲 key值存放到ServletContext getServletContext().setAttribute( mrcs[i].getKey() + config.getPrefix(), resources); } }
初始化數據源,初始化以後的數據源根據配置文件的prefix(配置文件前綴),放置到ServletContext。
protected void initModuleDataSources(ModuleConfig config) throws ServletException { // :FIXME: Document UnavailableException? if (log.isDebugEnabled()) { log.debug("Initializing module path '" + config.getPrefix() + "' data sources"); } // PrintWriter的特殊實現類,將出現新行、flush()被調用、println被調用時將信息輸出到ServletContext ServletContextWriter scw = new ServletContextWriter(getServletContext()); // 讀取數據源配置信息 DataSourceConfig dscs[] = config.findDataSourceConfigs(); if (dscs == null) { dscs = new DataSourceConfig[0]; } // 使用FastHashMap存儲數據源信息 dataSources.setFast(false); for (int i = 0; i < dscs.length; i++) { if (log.isDebugEnabled()) { log.debug("Initializing module path '" + config.getPrefix() + "' data source '" + dscs[i].getKey() + "'"); } DataSource ds = null; try { ds = (DataSource) RequestUtils.applicationInstance(dscs[i].getType()); BeanUtils.populate(ds, dscs[i].getProperties()); ds.setLogWriter(scw); } catch (Exception e) { log.error(internal.getMessage("dataSource.init", dscs[i].getKey()), e); throw new UnavailableException (internal.getMessage("dataSource.init", dscs[i].getKey())); } // 使用數據源key參數和prefix作key,存放到ServletContext中 getServletContext().setAttribute (dscs[i].getKey() + config.getPrefix(), ds); dataSources.put(dscs[i].getKey(), ds); } dataSources.setFast(true); }
初始化struts插件,在1.2.9版本中,struts提供4類插件:DigestingPlugIn、ModuleConfigVerifier、TilesPlugin、ValidatorPlugIn。
初始化後的插件信息將經過prefix進行區分後存放到ServletContext。
protected void initModulePlugIns (ModuleConfig config) throws ServletException { if (log.isDebugEnabled()) { log.debug("Initializing module path '" + config.getPrefix() + "' plug ins"); } // 獲取配置文件中的插件信息並建立插件對象 PlugInConfig plugInConfigs[] = config.findPlugInConfigs(); PlugIn plugIns[] = new PlugIn[plugInConfigs.length]; // 使用prefix存放到ServletContext getServletContext().setAttribute(Globals.PLUG_INS_KEY + config.getPrefix(), plugIns); for (int i = 0; i < plugIns.length; i++) { try { plugIns[i] = (PlugIn)RequestUtils.applicationInstance(plugInConfigs[i].getClassName()); BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties()); // Pass the current plugIn config object to the PlugIn. // The property is set only if the plugin declares it. // This plugin config object is needed by Tiles try { PropertyUtils.setProperty( plugIns[i], "currentPlugInConfigObject", plugInConfigs[i]); } catch (Exception e) { // FIXME Whenever we fail silently, we must document a valid reason // for doing so. Why should we fail silently if a property can't be set on // the plugin? /** * Between version 1.138-1.140 cedric made these changes. * The exceptions are caught to deal with containers applying strict security. * This was in response to bug #15736 * * Recommend that we make the currentPlugInConfigObject part of the PlugIn Interface if we can, Rob */ } plugIns[i].init(this, config); } catch (ServletException e) { throw e; } catch (Exception e) { String errMsg = internal.getMessage( "plugIn.init", plugInConfigs[i].getClassName()); log(errMsg, e); throw new UnavailableException(errMsg); } } }
將ModuleConfigImpl的configured屬性設置爲true。其餘配置信息進行變動前都會判斷configured屬性,而ModuleConfigImpl沒有提供將configured設置爲false的方法,由於配置文件一旦凍結,不可修改。
根據web.xml中ActionServlet啓動參數進行其餘配置文件的初始化工做:
11.1 配置信息須要以config開頭;
11.2 每個配置文件對應一個MuduleConfigImpl,用過前綴進行區分,存放到ServletContext;
11.3 每一個配置文件的信息都是獨立的,做用於僅限當前配置文件;
11.4 每一個配置文件在進行初始化操做後都會凍結;
將全部配置文件的前綴放入ServletContext;
銷燬解析器。