放翁(文初)的一畝三分地

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            210 隨筆 :: 1 文章 :: 320 評論 :: 0 Trackbacks
           

                 工作忙,有些許時(shí)間沒有更新Blog了,這次在開發(fā)監(jiān)控模塊的時(shí)候遇到了這個(gè)問題,整個(gè)問題定位過程真是走了不少路,所以覺得有必要記錄下來分享一下。在我看來很多時(shí)候結(jié)果也許就很簡單一個(gè)原因,但是開發(fā)人員卻要探究很久,也許在找到了其他可實(shí)現(xiàn)業(yè)務(wù)邏輯方法的情況下,就會放棄尋找原因,這期間我也是一樣。

          問題初現(xiàn):

                 在服務(wù)集成平臺中需要新增一塊寫入數(shù)據(jù)庫的邏輯,因此考慮最簡便就是弄個(gè)SpringBeanFactory來搞定這一切,誰知道,問題就這么出現(xiàn)了。很簡單,通過SpringClassPathXmlApplicationContext來構(gòu)建BeanFactory,下面的語句大家應(yīng)該很熟悉:

          ctx = new ClassPathXmlApplicationContext("/spring/sip-*.xml");

                 通過通配符來載入ClassPath下的所有的符合規(guī)則的spring配置文件。然后在Eclipse中作完單元測試和集成測試,一切正常。然后用我們內(nèi)部的打包部署,而這些配置文件都被打在Jar中作為lib庫依賴。結(jié)果啟動以后,在分析完日志需要寫入到數(shù)據(jù)庫的時(shí)候出現(xiàn)異常:

          Could not resolve bean definition resource pattern [/spring/sip-*.xml]; nested exception is java.io.FileNotFoundException: class path resource [spring/] cannot be resolved to URL because it does not exist

          就提示來說,就是沒有找到spring這個(gè)目錄,也就是在ClassPath下面就沒有找到資源。

          第一次試圖解決問題:

          以前調(diào)整過Jboss關(guān)于ClassLoader的配置,即自上而下搜索還是自下而上搜索,以及是否采用Web容器的ClassLoader,開始懷疑是否是這種修改造成的問題。修改了沒有問題,然后就設(shè)置斷點(diǎn)跟蹤SpringClassPathXmlApplicationContext的構(gòu)造過程,發(fā)現(xiàn)Spring在分析此類通配類型的過程中,首先將前面的文件目錄和后面的具體通配文件分開,先定位文件目錄資源,也就是在定位文件目錄資源的過程中,找不到spring目錄,而出現(xiàn)了那個(gè)異常。看了代碼中也有對Jar的處理,但是在處理之前就出現(xiàn)了問題。

          自己做了嘗試,將spring目錄和其內(nèi)容解壓到WebClasses目錄下運(yùn)行正常,或者解壓到war下面也是正常的,這些地方其實(shí)都是ClassPath可以找到的,但是lib目錄下的jar也應(yīng)該是可以找到的。在仔細(xì)跟蹤了代碼中最后載入這些資源的ClassLoader內(nèi)的數(shù)據(jù),所有的Jar都是包含在內(nèi)的。

          由于工作太多,因此將原有的打包模式作了修改,每次打包將這部分配置解壓到war下面,這樣就找到了可解決方案了,因此細(xì)致的緣由也就沒有再去追究。(如果不是后面再次遇到,這個(gè)問題就會在此了結(jié))

          問題再現(xiàn):

                 監(jiān)控模塊中需要新增一塊寫入數(shù)據(jù)庫的邏輯,在單元測試和集成測試通過的情況下出現(xiàn)了問題,由于此次是普通的J2SE的應(yīng)用,所有的配置和依賴都打入在了Jar中,所以問題和前次一樣。

                 這次決定花一些時(shí)間好好找到問題所在,首先覺的Spring的資源載入應(yīng)該不會不支持從Jar中載入,這是最基本的功能,因此再次打開了Spring的源碼。

          問題二次定位:

          先看看ClassPathXmlApplicationContext的類圖結(jié)構(gòu):




              關(guān)鍵方法就是getResource方法,ClassPathXmlApplicationContext的資源定位就是采用了DefaultResourceLoadergetResource方法。內(nèi)部也沒有做太多的工作,其實(shí)就是如下的代碼:

          try

          {

                                // Try to parse the location as a URL...

                                URL url = new URL(location);

                                returnnew UrlResource(url);

                     }

                     catch (MalformedURLException ex)

          {

                           // No URL -> resolve as resource path.

                           return getResourceByPath(location);

                }

          上面的代碼都是標(biāo)準(zhǔn)的j2se的代碼.作為URL通過字符串來構(gòu)造,通常需要能夠首先獲得URL的資源全路徑,而在當(dāng)前情況下發(fā)現(xiàn)到獲取資源的時(shí)候location還是保持了spring/的狀態(tài),而沒有被替換成為所在jar的資源全路徑,那么就先作以下測試:

              新建簡單的項(xiàng)目,然后在項(xiàng)目中加入包含spring配置的jar,然后作單元測試,測試代碼如下:

          URL url = Thread.currentThread().getClass().getResource("/spring/");

              未獲取到URL,出現(xiàn)異常。

          URL url = Thread.currentThread().getClass().getResource("/spring/sip-analyzer-dataSource.xml");

                 正常獲取到了URL

          由此看來應(yīng)該是在獲取Jar中的目錄資源路徑的時(shí)候出現(xiàn)問題導(dǎo)致后續(xù)載入出現(xiàn)問題,嘗試直接傳入具體的文件名:

          ctx = new ClassPathXmlApplicationContext("/spring/sip-analyzer-dataSource.xml");

          發(fā)現(xiàn)還是出現(xiàn)問題,在new URL的時(shí)候傳入的是沒有翻譯過的文件名,考慮在傳入的過程中就直接替換成為資源路徑,因此寫了一個(gè)簡單的方法:

          publicstatic String[] getRealClassPath(String[] locationfile)

                {

                     String[] result = locationfile;

                           for(int i = 0 ; i < locationfile.length; i++)

                           {

                                try

                                {

                                      URL url = Thread.currentThread().getClass().getResource(locationfile[i]);

                                      String file = url.getFile();

                                      if (file.indexOf(".jar!") > 0)

                                            result[i] = new StringBuffer("jar:").append(file.substring(0,file.indexOf(".jar!")+".jar!".length()))

                                                       .append(locationfile[i]).toString();

                                }

                                catch(Exception ex)

                                {}

                           }

                    

                     returnresult;

          }

          在將構(gòu)造工廠類修改為:

          ctx = new ClassPathXmlApplicationContext(BaseUtil.getRealClassPath(new String[]{"/spring/sip-analyzer-dataSource.xml"}));

          運(yùn)行測試,正常啟動,這也就是又變成最原始的文件羅列的模式。問題雖然找到了解決方案,但是始終覺得很別扭,同時(shí)對于無法在Jar中載入配置資源的情況我一直都還是覺得應(yīng)該不是Spring的問題。

          峰回路轉(zhuǎn):

          晚上到家還是有點(diǎn)不死心,就直接建了個(gè)項(xiàng)目作單元測試,然后將一個(gè)自己建立的Jar加入到Classpath下面,作單元測試,結(jié)果大吃一驚。

          URL url = Thread.currentThread().getClass().getResource("/test/");

          URL url = Thread.currentThread().getClass().getResource("/test/test.txt");

          都正常獲取到了資源,這完全推翻了我早先認(rèn)為在Jar中無法獲得目錄作為資源的問題。然后把公司里面的項(xiàng)目重新打包然后加入到ClassPath下,驗(yàn)證spring的目錄,出錯,目錄無法獲取,此時(shí)我確定看來應(yīng)該不是應(yīng)用的問題,而是環(huán)境問題。檢查了兩個(gè)Jar,看似沒有什么區(qū)別,將公司項(xiàng)目的Jar中的spring目錄拷貝到測試的jar中,然后作測試,可以找到目錄。那么問題完全定位到了Jar本身。通過RAR的壓縮工具看了一下兩個(gè)Jar的信息,除了顯示所謂的壓縮平臺不同(一個(gè)是DOS,一個(gè)是Unix)其他沒有任何區(qū)別。然后自己用RAR打了一個(gè)Jar以及在linux下打了一個(gè)Jar做了測試,兩個(gè)Jar內(nèi)的目錄都是正常可以被獲取。

          無意中我換了一下需要獲取的目錄名稱,也就是說在公司項(xiàng)目中有多個(gè)目錄在jar中,這次換成為ibatis目錄,正常獲取,看來不是Jar的格式。回想了一下,公司的打包工具是自己人寫的,其中提供了一個(gè)特性,如果一個(gè)項(xiàng)目內(nèi)部的一些配置信息是需要讓調(diào)用它的第三方在編譯期配置,那么可以通過在第三方項(xiàng)目構(gòu)建的過程中,動態(tài)的生成配置文件然后植入到被依賴的jar中。而spring這個(gè)目錄中由于那些數(shù)據(jù)庫的配置都是需要動態(tài)配置的,因此spring的那個(gè)目錄是后期被寫入的,而ibatis是早先就固化在項(xiàng)目中的。

          由于我們的JarMETA-INF中都有INDEX.LIST文件,過去遇到過在JAR中自己手工放入一些文件由于沒有修改INDEX.LIST而導(dǎo)致雖然文件已經(jīng)存在但是不會被發(fā)現(xiàn),于是打開公司項(xiàng)目中的Jar,果然INDEX.LIST中只有ibatis,而沒有spring,看來是我的同事在寫入的時(shí)候沒有將INDEX.LIST更新。立刻將INDEX.LIST作了更新,測試spring目錄,結(jié)果依然出錯。看來這還不是問題的根本。

          立刻問了我們開發(fā)打包工具的同事,向他們要寫入Jar的代碼,對方的回答是就是采用簡單的JarOutputStream來寫入,沒有什么特殊的。那我就開始懷疑是否是因?yàn)椴捎眠@種方式寫入到Jar中的目錄在被資源定位的時(shí)候會出現(xiàn)問題。于是寫了下面的代碼:

          JarOutputStream jos;

                     try

                     {

                           jos = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(file)));

                           String f = "spring/sip-analyzer-dataSource.xml";

                           File source = new File(f);

                           JarEntry je = new JarEntry(f);

                           jos.putNextEntry(je);

                           BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:/work/sip3/analyzer/src/conf.test/spring/sip-analyzer-dataSource.xml"));

                           int i = 0;

                           while ((i=bis.read())!=-1)

                           {

                                jos.write(i);

                           }

                           bis.close();

                           jos.closeEntry();

                           jos.close();

          } catch  ...

          結(jié)果創(chuàng)建出來的Jar中的spring目錄無法被資源定位,同樣在這個(gè)Jar中直接拖入一個(gè)目錄test,然后刷新測試,test目錄可以被定位。

          后續(xù)

                 就到了這個(gè)階段來看如果以上面這種方式寫入,對于目錄資源定位的卻存在問題。這個(gè)問題還沒有最終的肯定的結(jié)論,在我現(xiàn)在所有的試驗(yàn)來看,不論是否有INDEX.LIST,或者INDEX.LIST,如果用程序?qū)懭氲?/span>Jar中,目錄作為資源定位都會出現(xiàn)問題(起碼是上面那種普通寫入方式)。

                 這種情況可能是由于這種寫法還有一些其他需要配置的,例如寫入到META-INF/INDEX.LIST中,或者就是J2SE現(xiàn)在API存在的一個(gè)問題。不過不管是什么問題,起碼值得引起重視,特別是現(xiàn)在類似于Spring框架載入Jar目錄下的配置。

          posted on 2008-05-28 16:42 岑文初 閱讀(3851) 評論(2)  編輯  收藏

          評論

          # re: Java 載入Jar內(nèi)資源問題的探究 2008-05-29 17:51 yeshucheng
          代碼字體太小,呵呵。希望以后調(diào)整下。整體不錯  回復(fù)  更多評論
            

          # re: Java 載入Jar內(nèi)資源問題的探究 2011-05-19 16:35 多福多壽
          額鵝鵝鵝鵝鵝鵝鵝鵝鵝鵝鵝鵝鵝鵝鵝  回復(fù)  更多評論
            


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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 塘沽区| 宝兴县| 洞头县| 隆子县| 眉山市| 浙江省| 六枝特区| 黄浦区| 灵石县| 乡城县| 谷城县| 平凉市| 七台河市| 宁陵县| 徐闻县| 慈利县| 德昌县| 甘谷县| 泾源县| 北票市| 灵石县| 卢龙县| 祥云县| 阿克苏市| 玉树县| 高州市| 龙江县| 顺平县| 通江县| 原阳县| 固镇县| 渭南市| 阳春市| 临西县| 龙陵县| 道真| 赣榆县| 萍乡市| 勐海县| 疏勒县| 南昌县|