作者:zhyiwww zhyiwww@163.com |
分以下幾步來理解: [1] Web 應用如何知道使用的是 Struts我們知道,在 Web 工程的 /WEB-INF/ 目錄下面,我們有兩個配置文件 Web.xml 和 Struts-config.xml. 一般情況下就只有一個 web.xml 文件,如果系統(tǒng)是用了 struts 技術(shù)的話,那么就會有 struts-config.xml 文件。 [2] 系統(tǒng)是如何啟動和初始化 struts 的在 Web.xml 中有這么一段代碼: < servlet > < servlet-name > action </ servlet-name > < servlet-class > org.apache.struts.action.ActionServlet </ servlet-class > < init-param > < param-name > config </ param-name > < param-value > /WEB-INF/struts-config.xml </ param-value > </ init-param > < init-param > < param-name > debug </ param-name > < param-value > 3 </ param-value > </ init-param > < init-param > < param-name > detail </ param-name > < param-value > 3 </ param-value > </ init-param > < load-on-startup > 0 </ load-on-startup > </ servlet > < servlet > < servlet-name > startThread </ servlet-name > < servlet-class > com.cgogo.ypindex.StartThread </ servlet-class > < init-param > < param-name > startParam </ param-name > < param-value > 2 </ param-value > </ init-param > < load-on-startup > 1 </ load-on-startup > </ servlet > < servlet-mapping > < servlet-name > action </ servlet-name > < url-pattern > *.do </ url-pattern > </ servlet-mapping > 在 web.xml 中會配置 struts 的中央控制器類。 Web.xml 配置文件的初始化是由容器來實現(xiàn)載入和初始化的。如果你使用的是 tomcat 的話,那么就是由 tomcat 在啟動的時候會載入此 web.xml 文件,也就為此 web 應用創(chuàng)建了一個 web Context, 此 context 也就是你 Web 應用的訪問入口。 載入中央控制器 org.apache.struts.action.ActionServlet ,這是一個 Servlet, 那么,其就要進行 servlet 的初始化操作。此 action 是如何對 Struts 系統(tǒng)進行初始化的呢? [3] init()-- ActionServlet 如何初始化 Struts 系統(tǒng)?看一下 ActionServlet 的源代碼: 我們知道,在一個 Servlet 中,在其啟動的時候,首先要執(zhí)行 init() 方法,那么我們先來看一下 actionServlet 的 init() 方法的源代碼: /** * <p>Initialize this servlet. Most of the processing has been factored into * support methods so that you can override particular functionality at a * fairly granular level.</p> * * @exception ServletException if we cannot configure ourselves correctly */ public void init() throws ServletException { // Wraps the entire initialization in a try/catch to better handle // unexpected exceptions and errors to provide better feedback // to the developer try { (1) initInternal(); (2) initOther(); (3) initServlet(); getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this); (4) initModuleConfigFactory(); // Initialize modules as needed ModuleConfig moduleConfig = initModuleConfig("", config); (5) initModuleMessageResources(moduleConfig); (6) initModuleDataSources(moduleConfig); (7) initModulePlugIns(moduleConfig); moduleConfig.freeze(); (8) 初始化配置參數(shù) 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(); } this.initModulePrefixes(this.getServletContext()); 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()); } } 先來看一下初始化( 1 )的部分 initInternal() 。 [4] initInternal()-- 內(nèi)部資源文件的初始化
initInternal() 就是實現(xiàn)內(nèi)部資源文件的初始化的,也就是轉(zhuǎn)為 Struts 系統(tǒng)本身提供的以下錯誤信息提示等文字的國際化實現(xiàn)的。我們看一下源代碼是如何實現(xiàn)的: /** * <p> Initialize our internal MessageResources bundle. </p> * * @exception ServletException if we cannot initialize these resources */ protected void initInternal() throws ServletException { // : FIXME : Document UnavailableException 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 根據(jù)配置文件的名字得到一個資源文件。 internalName 就是內(nèi)部資源文件的名稱,其在 actionServlet 中定義: /** * <p> The Java base name of our internal resources. </p> * @since Struts 1.1 */ protected String internalName = "org.apache.struts.action.ActionResources" ; 取得后的對象是一個 MessageResources 的對象,保存在 internal 中, /** * <p>The resources object for our internal resources.</p> */ protected MessageResources internal = null; 經(jīng)過此初始化后, internal 就不在是 null 了。 至此就實現(xiàn)完成了內(nèi)部資源文件的初始化。如果出現(xiàn)了異常的話,那么系統(tǒng)就捕捉到。 然后,系統(tǒng)就開始初始化其它的配置,即( 2 ) initOther(); [5] initOther()-- 如何初始化其他的配置的?/** * <p>Initialize other global characteristics of the controller servlet.</p> * * @exception ServletException if we cannot initialize these resources */ 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); } } 然后就執(zhí)行( 3 ) initServlet(); [6] initServlet()-- 如何初始化 servlet
這個初始化主要是初始化 servlet 的,哪些 servlet 呢?就是我們在 web.xml 中配置的那些需要在 web application 初始化時就栽入系統(tǒng)的 servlet 這是一個復雜的過程: 我的理解: 這部分代碼就執(zhí)行一次,僅在初始化的時候執(zhí)行一次。 /** * <p>Initialize the servlet mapping under which our controller servlet * is being accessed. This will be used in the <code>&html:form></code> * tag to generate correct destination URLs for form submissions.</p> * * @throws ServletException if error happens while scanning web.xml */ protected void initServlet() throws ServletException { // Remember our servlet name // 這里保存當前的servlet名字,保存在actionServlet的servletName屬性中 this.servletName = getServletConfig().getServletName(); // Prepare a Digester to scan the web application deployment descriptor
Digester digester = new Digester(); digester.push(this); digester.setNamespaceAware(true); digester.setValidating(false); // Register our local copy of the DTDs that we can find for (int i = 0; i < registrations.length; i += 2) { URL url = this.getClass().getResource(registrations[i+1]); if (url != null) { digester.register(registrations[i], url.toString()); } } // Configure the processing rules that we need digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2); digester.addCallParam("web-app/servlet-mapping/servlet-name", 0); digester.addCallParam("web-app/servlet-mapping/url-pattern", 1); // Process the web application deployment descriptor if (log.isDebugEnabled()) { log.debug("Scanning web.xml for controller servlet mapping"); } // 取得當前的配置文件 InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml"); if (input == null) { log.error(internal.getMessage("configWebXml")); throw new ServletException(internal.getMessage("configWebXml")); } try { // 解析當前配置文件 digester.parse(input); } catch (IOException e) { log.error(internal.getMessage("configWebXml"), e); throw new ServletException(e); } catch (SAXException e) { log.error(internal.getMessage("configWebXml"), e); throw new ServletException(e); } finally { try { // 解析完畢,關(guān)閉輸入 input.close(); } catch (IOException e) { log.error(internal.getMessage("configWebXml"), e); /** 如果有異常,當前部進行處理,而是留給他的調(diào)用者來處理。其實是當前的調(diào)用部分沒有處理的能力。我們可以這樣理解,假設(shè)你想在出現(xiàn)了這類異常異常的地方給用戶一個提示,但是在我們封裝功能實現(xiàn)的時候,我們并不知道誰會來調(diào)用,所以我們只有把異常拋出,讓調(diào)用者自己去處理。 這一點也許不太好理解,不過,如果理解了,可能你就能夠靈活的使用異常了。 */ throw new ServletException(e); } } // Record a servlet context attribute (if appropriate) if (log.isDebugEnabled()) { log.debug("Mapping for servlet ‘" + servletName + "‘ = ‘" + servletMapping + "‘"); } if (servletMapping != null) { getServletContext().setAttribute(Globals.SERVLET_KEY, servletMapping); } } [7] 初始化其他模塊(1) 初始化工廠 getServletContext().setAttribute( Globals . ACTION_SERVLET_KEY , this ); initModuleConfigFactory(); (2) 初始化資源模塊 // Initialize modules as needed ModuleConfig moduleConfig = initModuleConfig( "" , config ); initModuleMessageResources(moduleConfig); (3) 初始化數(shù)據(jù)源配置模塊initModuleDataSources(moduleConfig); (4) 初始化 PlugIn 模塊 initModulePlugIns(moduleConfig); moduleConfig.freeze(); [8] 初始化參數(shù)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(); } [9] 我也不知道做什么用的
this .initModulePrefixes( this .getServletContext()); this .destroyConfigDigester(); 以上就是Struts的初始化流程。 [10] 部分模塊的詳細實現(xiàn):a. 工廠的初始化如何實現(xiàn)的: /** * <p>Initialize the factory used to create the module configuration.</p> * @since Struts 1.2 */ protected void initModuleConfigFactory(){ /** 這個部分的代碼就是取得參數(shù)。 這個參數(shù)你可以自己擴展你的模塊實現(xiàn)工廠。但是一般都沒有自己去做。 所以一般都使用的默認的工廠初始化配置。 */ String configFactory = getServletConfig().getInitParameter("configFactory"); /** 下面的代碼,只有你做了自己的配置才會有效。否則一般是不執(zhí)行的。 */ if (configFactory != null) { /** 設(shè)置此工廠,并把其參數(shù)存入到 ModuleConfigFactory.factoryClass 屬性中。 此部分可以看 ModuleConfigFactory 的代碼。 ModuleConfigFactory 是一個 */ ModuleConfigFactory.setFactoryClass(configFactory); } } b. 資源模塊式如何初始化的// 調(diào)用的部分 protected String config = "/WEB-INF/struts-config.xml"; ------------------------------------------------------------------ ModuleConfig moduleConfig = initModuleConfig("", config); initModuleMessageResources(moduleConfig); // 實現(xiàn)的部分: protected void initModuleMessageResources (ModuleConfig config) throws ServletException { /** 在 struts-config.xml 中的資源文件配置,你可能配置了多個資源,所以此處取得是一個數(shù)組 */ 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() + "‘"); } /** protected String factory = "org.apache.struts.util.PropertyMessageResourcesFactory"; 就是返回的這個值,如果你沒有做其他的設(shè)置的話。 一般情況下,我們都用得是默認的 */ String factory = mrcs[i].getFactory(); /** 此處對每一個資源配置文件都回去創(chuàng)建一個工廠 */ MessageResourcesFactory.setFactoryClass(factory); MessageResourcesFactory factoryObject = MessageResourcesFactory.createFactory(); factoryObject.setConfig(mrcs[i]); MessageResources resources = factoryObject.createResources(mrcs[i].getParameter()); resources.setReturnNull(mrcs[i].getNull()); resources.setEscape(mrcs[i].isEscape()); /** 這一部分非常重要。 我們之所以能夠直接調(diào)用,就是因為,初始化后,我們就把此 resources 放到了其對應的當前應用的屬性值里面了。之后我們就可以直接調(diào)用了。 getServletContext().setAttribute( mrcs[i].getKey() + config.getPrefix(), resources); } } c. 數(shù)據(jù)模塊是如何初始化的========================================================== // 調(diào)用部分 initModuleDataSources(moduleConfig); // 實現(xiàn)部分: /** * <p>Initialize the data sources for the specified module.</p> * * @param config ModuleConfig information for this module * * @exception ServletException if initialization cannot be performed * @since Struts 1.1 */ protected void initModuleDataSources(ModuleConfig config) throws ServletException { // :FIXME: Document UnavailableException? if (log.isDebugEnabled()) { log.debug("Initializing module path ‘" + config.getPrefix() + "‘ data sources"); } ServletContextWriter scw = new ServletContextWriter(getServletContext()); /** 因為你可能配置了多個數(shù)據(jù)源,所以此處返回的是一個數(shù)組 DataSourceConfig dscs[] = config.findDataSourceConfigs(); // 處理沒有配置數(shù)據(jù)源的情況 if (dscs == null) { dscs = new DataSourceConfig[0]; } /** /** * <p>The JDBC data sources that has been configured for this module, * if any, keyed by the servlet context attribute under which they are * stored.</p> */ protected FastHashMap dataSources = new FastHashMap(); 這是一個加工過的 HashMap ,又不同的工作模式 */ 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())); } /** 這一個部分很重要。 把初始化后的數(shù)據(jù)源放入到 servlet 的屬性中,所以我們才可以通過 struts 的對應屬性直接訪問。 protected String key = Globals.DATA_SOURCE_KEY; 所以,我們可以通過 Globals.DATA_SOURCE_KEY 屬性的值來取得其配制后的數(shù)據(jù)源。 如果多個的話,可以通過數(shù)據(jù)源的參數(shù) ID 來配置和調(diào)用。 */ getServletContext().setAttribute (dscs[i].getKey() + config.getPrefix(), ds); dataSources.put(dscs[i].getKey(), ds); } dataSources.setFast(true); } 至此,Struts系統(tǒng)初始化完畢。 |
|
|