風(fēng)之力

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            19 Posts :: 2 Stories :: 18 Comments :: 0 Trackbacks
          轉(zhuǎn)載:轉(zhuǎn)載請(qǐng)保留本信息,本文來自http://www.matrix.org.cn/resource/article/1/1071.html
          我在幾個(gè)月前曾經(jīng)發(fā)表過一個(gè)帖子,就是和大家一起學(xué)習(xí)struts源代碼。成為一名合格的程序員,閱讀大量的優(yōu)秀程序是必不可少的。只看書是不會(huì)讓你水平有很大提高的,要多看多寫。
          本來是打算等下面幾篇文章寫好后一起發(fā)布的,這樣大家可能才能看得明白些,但是根據(jù)我現(xiàn)在的狀況,估計(jì)還要一、兩個(gè)月。所以,為了防止在struts源代碼發(fā)生過大變化后我的文章就沒有太大價(jià)值了,所以就提前發(fā)表了,霍霍~~~
          我的email為:mariah_fan@hotmail.com,有什么不對(duì)的地方請(qǐng)大家指正:)
          struts作為J2EE的MVC框架已經(jīng)取得了很大的成功,下面將分幾篇文章說明struts源程序的結(jié)構(gòu)。
          第一篇 struts的初始化

          struts 的核心類是org.apache.struts.action.ActionServlet,這個(gè)類將會(huì)在struts第一次使用時(shí),
          作為servlet初始化并存入tomcat容器。很顯然的,初始化將會(huì)調(diào)用init方法初始化相應(yīng)的數(shù)據(jù)。

          一、initInternal()方法:
          通過調(diào)用MessageResources.getMessageResources(internalName)方法生成一個(gè)
          MessageResources類,getMessageResources是通過調(diào)用MessageResourcesFactory.
          createResources(config)來實(shí)現(xiàn)的。至于MessageResourcesFactory是一個(gè)abstract類,任何
          繼承自它的類都要實(shí)現(xiàn)createResources方法,生成MessageResources對(duì)象。整個(gè)程序生成
          MessageResourcesFactory使用了如下技巧:
          MessageResourcesFactory.factoryClass = factoryClass;
          MessageResourcesFactory.clazz = null;
          首先會(huì)通過factoryClass來定義一個(gè)類全名,然后通過ClassLoader.loadClass
          (factoryClass)方法來生成這個(gè)類,并賦給clazz,然后通過newInstance來生成一個(gè)對(duì)象。
          在本程序中,生成MessageResources對(duì)象實(shí)際就是對(duì)如下屬性進(jìn)行了初始化:
          this.factory = factory;("org.apache.struts.util.PropertyMessageResourcesFactory")
          this.config = config;("org.apache.struts.action.ActionResources")
          this.returnNull = returnNull;(true/false)

          對(duì)于MessageResources類的作用是根據(jù)不同的Locate來格式化相應(yīng)的string。或者把你需要改變
          的string存放到數(shù)組中,然后通過getMessage(Locale locale, String key, Object args[])
          方法來格式化。然后把格式好的string存放到HashMap里,這樣就可以為以后重用。這里的key是
          使用的locale.toString() + "." + key

          在PropertyMessageResources中的loadLocale方法用來讀取resource的初始化信息。首先它會(huì)
          通過一個(gè)HashMap檢測(cè)這個(gè)localKey相關(guān)的message是否已經(jīng)被初始化了,如果被初始化過就跳
          出,檢測(cè)的方法是locales.get(localeKey) != null。
          然后會(huì)讀取如下一個(gè)文件:
          org/apache/struts/action/ActionResources_(localKey).properties,然后進(jìn)行如下操作:
          Properties props = new Properties();
          ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
          is = classLoader.getResourceAsStream(name);
          props.load(is);
          Iterator names = props.keySet().iterator();
          while (names.hasNext()) {
          String key = (String) names.next();
          if (log.isTraceEnabled()) {
          log.trace(" Saving message key '" + messageKey(localeKey, key));
          }
          messages.put(messageKey(localeKey, key), props.getProperty(key));
          }

          PropertyMessageResources 就是通過上面的loadLocale方法查找與Locale locale, String key
          相對(duì)對(duì)應(yīng)的Message.查找的次序如下locale.toString(),然后是
          localeKey = localeKey.substring(0, underscore),然后是defaultLocale,然后是key。

          最后,resource類的結(jié)構(gòu)如下:
          PropertyMessageResources extends MessageResources
          PropertyMessageResourcesFactory extends MessageResourcesFactory

          二、initOther()方法:
          從servlet中獲取config和debug兩個(gè)參數(shù),然后初始化ConvertUtils對(duì)象。由于
          ConvertUtils.deregister()的初始化,所有的Converter都是有初始值的,所以這里Struts自己
          把這些初始值設(shè)置為null,即轉(zhuǎn)換出錯(cuò)的時(shí)候返回null,而不是初始值。使用ConvertUtils類的
          原因是由于從form傳輸過來的都是String類型的值,所以我們要把它們轉(zhuǎn)換成相應(yīng)的類型。

          提到幾個(gè)技巧:
          *public boolean isIndexed() {
          if (type == null) {
          return (false);
          //技巧一:判斷是否是一個(gè)Array類的方法
          } else if (type.isArray()) {
          return (true);
          //技巧二:判斷type是否是List的一個(gè)父類或者父接口,或者與List為同一個(gè)類
          //要注意如果List是另一個(gè)primitive的TYPE類,那么type必須也是這個(gè)類才會(huì)
          //返回true,否則都是false。注意long.TYPE與Long.class是不同的
          } else if (List.class.isAssignableFrom(type)) {
          return (true);
          } else {
          return (false);
          }
          }

          *//componentType為Array類所存儲(chǔ)的元素的類別
          Class componentType = indexedProperty.getClass().getComponentType();
          //生成一個(gè)新的Array
          Object newArray = Array.newInstance(componentType, (index + 1));
          System.arraycopy(indexedProperty, 0, newArray, 0, length);
          indexedProperty = newArray;
          set(name, indexedProperty);
          int newLength = Array.getLength(indexedProperty);
          for (int i = length; i < newLength; i++) {
          Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType));
          }

          三、initServlet()方法:
          這個(gè)方法主要是通過digester類解析web.xml,對(duì)String servletMapping屬性進(jìn)行初始化。對(duì)于
          digester說明如下:這是一個(gè)基于DOM的SAX實(shí)現(xiàn)的類,它是事件觸發(fā)的,根據(jù)xml文件的結(jié)構(gòu),
          每次讀到一個(gè)節(jié)點(diǎn)元素就會(huì)觸發(fā)一個(gè)事件。

          InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml");
          這是一個(gè)比較少見的方法。首先通過this.servletName = getServletConfig().
          getServletName()獲取servlet的名稱,然后根據(jù)
          if (servletName.equals(this.servletName)) {
          this.servletMapping = urlPattern;
          }
          來判斷當(dāng)前讀到的servlet名稱是否是我們運(yùn)行的servlet的名稱,如果是,就把url-pattern作為
          我們的servletMapping。

          四、getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this)
          把自己存儲(chǔ)到servletContext中,屬性名為Globals.ACTION_SERVLET_KEY。

          五、ModuleConfig moduleConfig = initModuleConfig("", config)
          這個(gè)方法使用由initOther()方法獲取的config值為要解析的xml路徑,用來初始化ModuleConfig。
          它首先采用與生成MessageResourcesFactory同樣的方法產(chǎn)生一個(gè)MessageResourcesFactory對(duì)象:
          MessageResourcesFactory為一個(gè)抽象類,每一個(gè)繼承它的類都要實(shí)現(xiàn)
          createModuleConfig(String prefix)方法。本程序使用的缺省的MessageResourcesFactory類為
          org.apache.struts.config.impl.DefaultModuleConfigFactory,它
          的createModuleConfig(String prefix)方法會(huì)生成一個(gè)ModuleConfigImpl類。

          ModuleConfigImpl類相當(dāng)于一個(gè)JavaBean,用來存放一個(gè)web模塊運(yùn)行時(shí)所需要的配置信息。當(dāng)
          然,一個(gè)web模塊可以擁有多個(gè)ModuleConfig,但是缺省的是prefix長(zhǎng)度為0的ModuleConifg。它
          的每個(gè)屬性幾乎都是由HashMap組成的,它通過一個(gè)configured布爾值來描述當(dāng)前的ModuleConfig
          是否已經(jīng)被初始化完畢,在每存放一個(gè)屬性的時(shí)候都會(huì)監(jiān)測(cè)這個(gè)值。如果初始化完畢而還要改變
          里面的屬性值,則會(huì)報(bào)出IllegalStateException("Configuration is frozen")異常,現(xiàn)在對(duì)它
          的屬性簡(jiǎn)單說明如下:
          * protected HashMap actionConfigs:
          這個(gè)HashMap用來存儲(chǔ)ActionConfig對(duì)象。
          * protected HashMap dataSources
          這個(gè)HashMap用來存儲(chǔ)DataSourceConfig對(duì)象。
          * protected HashMap exceptions
          這個(gè)HashMap用來存儲(chǔ)ExceptionConfig對(duì)象。
          * protected HashMap formBeans
          這個(gè)HashMap用來存儲(chǔ)FormBeanConfig對(duì)象。
          * protected HashMap forwards
          這個(gè)HashMap用來存儲(chǔ)ForwardConfig對(duì)象。
          * protected HashMap messageResources
          這個(gè)HashMap用來存儲(chǔ)MessageResourcesConfig對(duì)象。
          * protected ArrayList plugIns
          這個(gè)HashMap用來存儲(chǔ)PlugInConfig對(duì)象。
          * protected ControllerConfig controllerConfig
          ControllerConfig類
          * protected boolean configured
          標(biāo)志這個(gè)ModuleConfig是(true)否(false)配置完成。
          * protected String prefix
          用來標(biāo)志和區(qū)分ModuleConfig類,同時(shí)在使用上面的config類初始化相應(yīng)的資源以后,也是通
          過這個(gè)prefix來區(qū)分所屬的不同的web模塊。
          * protected String actionMappingClass = "org.apache.struts.action.ActionMapping"
          ActionMapping類名,缺省為org.apache.struts.action.ActionMapping。

          初始化ModuleConfig的方法如下:
          首先是使用getServletConfig().getInitParameter("mapping")來獲取設(shè)定的ActionMapping類
          名,然后通過initConfigDigester()方法來生成一個(gè)digester。最后用","分隔config,對(duì)每一
          塊調(diào)用parseModuleConfigFile(prefix, paths, config, digester, path)方法解析。注意,這
          個(gè)方法實(shí)際上只有兩個(gè)參數(shù)是有意義的:path為我們要解析的xml文件,config用來初始化完成
          后保存到servletContext中。

          如果ModuleConfig中存放的FormBeanConfig為Dydamic類型,那么就調(diào)用
          DynaActionFormClass.createDynaActionFormClass(FormBeanConfig)初始化
          DynaActionFormClass,并存放到DynaActionFormClass.dynaClasses 的 static HashMap中。這
          里的key為FormBeanConfig.getName() + moduleConfig.getPrefix()。

          如果當(dāng)前的ModuleConfig為缺省的ModuleConfig,那么將會(huì)調(diào)用如下幾個(gè)方法:
          defaultControllerConfig(config)
          defaultMessageResourcesConfig(config)
          defaultFormBeansConfig(config)
          defaultForwardsConfig(config)
          defaultMappingsConfig(config)
          在struts1.1以后,這個(gè)特例將會(huì)被廢棄:

          defaultControllerConfig(config)為ControllerConfig通過getInitParameter(s)方法初始化如
          下幾個(gè)屬性:bufferSize,content,locale(true/false),maxFileSize,nocache(true/false)
          ,multipartClass,tempDir。

          defaultMessageResourcesConfig(config)為MessageResourcesConfig通過getInitParameter(s)
          方法初始化如下幾個(gè)屬性:application,factory,null(true/false)。

          其它的幾個(gè)方法就是獲取不同的對(duì)象,然后把它們相應(yīng)的存儲(chǔ)到servlet中。關(guān)心如下:
          ActionFormBeans=>FormBeanConfig,ActionForwards=>ForwardConfig,
          ActionMappings=>ActionConfig。

          六、initModuleMessageResources(ModuleConfig config)
          通過存儲(chǔ)在ModuleConfig中的MessageResourcesConfig對(duì)象,逐個(gè)初始化MessageResource,
          然后再把初始化好的MessageResources存放到ServletContext中,attributeName為
          MessageResourcesConfig.getKey() + ModuleConfig.getPrefix()。

          七、initModuleDataSources(ModuleConfig config)
          通過存儲(chǔ)在ModuleConfig中的DataSourceConfig對(duì)象,逐個(gè)初始化DataSource。然后對(duì)于每一個(gè)
          DateSource通過BeanUtils.populate(ds, dscs[i].getProperties())方法初始化其屬性。再把初
          始化好的DateSource存放到ServletContext中,attributeName為
          DataSourceConfig.getKey() + ModuleConfig.getPrefix()。同時(shí)也存放到名位dataSources的
          FastHashMap中,key為DataSourceConfig.getKey()。

          這里還會(huì)根據(jù)生成的DateSource對(duì)象是否是GenericDataSource類型,如果是則調(diào)用
          GenericDataSource.open()方法。GenericDataSource是一個(gè)非常簡(jiǎn)單的數(shù)據(jù)庫連接池,它的
          open()方法用來初始化連接池,生成最小數(shù)目的GenericConnection,這里的open()方法根據(jù)
          String driver變量是否為null來判斷是否已經(jīng)被初始化過。需要仔細(xì)說明的是getConnection()
          方法,它首先從連接池中取出GenericConnection對(duì)象,然后檢查其是否是可鏈接的,如果是就
          返回,否則繼續(xù)取出,同時(shí)activeCount-1。如果沒有取到,則會(huì)檢查當(dāng)前可使用的
          GenericConnection是否達(dá)到最大值(activeCount < maxCount),如果沒有,調(diào)用
          createConnection()方法聲成一個(gè)新的GenericConnection,然后檢查其是否是可鏈接,如果可以
          則返回。returnConnection(GenericConnection conn)方法則是通過把GenericConnection放回到
          連接池,然后activeCount-1。

          這個(gè)方法中使用到了ServletContextWriter類,DateSource的log信息就通過這個(gè)類寫入。對(duì)這個(gè)
          類說明如下:
          它繼承自PrintWriter,而PrintWriter又繼承自Writer。Writer類所作的事情就是在同步的情況下
          調(diào)用abstract方法:abstract public void write(char cbuf[], int off, int len),這個(gè)方法
          將會(huì)根據(jù)調(diào)用者的需要由調(diào)用者實(shí)現(xiàn)。
          PrintWriter則首先通過ensureOpen()方法檢驗(yàn)這個(gè)類中是否有寫入的對(duì)象(Writer類或其子類),
          如果有則根據(jù)不同的情況調(diào)用這個(gè)寫入對(duì)象的write方法(out.write(....))。這個(gè)類的print(...)
          方法就是據(jù)不同的情況調(diào)用相應(yīng)的write(...)方法。而println(...)與之的區(qū)別就是每次多寫入一
          個(gè)換行字符串。還有一個(gè)區(qū)別是println(...)會(huì)根據(jù)是否需要autoflush進(jìn)行flush,而write(...)
          方法不會(huì)。
          ServletContextWriter類的作用是把字符寫入ServletContext中。ServletContextWriter類方法中
          真正實(shí)現(xiàn)了write方法:
          public void write(char c) {
          if (c == '\n')
          flush();
          else if (c != '\r')
          buffer.append(c);
          }
          public void flush() {
          if (buffer.length() > 0) {
          context.log(buffer.toString());
          buffer.setLength(0);
          }
          }

          八、initModulePlugIns(moduleConfig)
          通過存儲(chǔ)在ModuleConfig中的PlugInConfig對(duì)象,逐個(gè)初始化PlugIn對(duì)象,存放到一個(gè)數(shù)組中,
          然后再把這個(gè)數(shù)組存放到ServletContext中,attributeName為
          Globals.PLUG_INS_KEY + ModuleConfig.getPrefix()。

          對(duì)每一個(gè)生成的PlugIn對(duì)象通過
          BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties())方法初始化其屬性。然后
          再把PlugInConfig對(duì)象存放到由其生成的PlugIn對(duì)象中。

          最后,通過plugIns[i].init(this, (ModuleConfig) config)初始化這個(gè)plugIn對(duì)象。

          九、初始化結(jié)束
          完成了這個(gè)初始化以后,會(huì)調(diào)用ModuleConfig.freeze()令這個(gè)ModuleConfig變得不可改變。然后
          會(huì)遍歷ServletConfig中的initParameterNames,如果有以"config/"開頭的,則通過這個(gè)parameter
          的值繼續(xù)初始化其它的ModuleConfig,且這個(gè)ModuleConfig的prefix為"config/"后的字符串。

          同樣調(diào)用如下方法:
          initModuleMessageResources(moduleConfig);
          initModuleDataSources(moduleConfig);
          initModulePlugIns(moduleConfig);
          moduleConfig.freeze();

          最后調(diào)用destroyConfigDigester()釋放內(nèi)存。
          posted on 2006-09-14 10:08 風(fēng)之力 閱讀(72) 評(píng)論(0)  編輯  收藏

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 芦山县| 道孚县| 化州市| 江油市| 桂林市| 洛隆县| 盐源县| 上思县| 当雄县| 定安县| 罗江县| 南通市| 河北区| 西平县| 辽中县| 临桂县| 扎鲁特旗| 莆田市| 尉犁县| 上饶市| 德令哈市| 芜湖县| 乐业县| 云阳县| 乌拉特前旗| 平乐县| 德令哈市| 游戏| 竹山县| 桓台县| 上栗县| 怀远县| 灯塔市| 宁夏| 紫云| 平安县| 公安县| 游戏| 赤水市| 辽阳市| 元江|