背景:
某日臨近下班,一個(gè)同事欲任何類(lèi)中獲取項(xiàng)目絕對(duì)路徑,不通過(guò)Request方式獲取,可是始終獲取不到預(yù)想的路徑。于是晚上回家google了一下,誤以為是System.getProperty('java.class.path')-未實(shí)際進(jìn)行測(cè)試,早上來(lái)和同事溝通,提出了使用這個(gè)內(nèi)置方法,結(jié)果人家早已驗(yàn)證過(guò),該方法是打印出CLASSPATH環(huán)境變量的值。
于是乎,繼續(xù)google,找到了Class的getResource與getResourceAsStream兩個(gè)方法。這兩個(gè)方法會(huì)委托給ClassLoader對(duì)應(yīng)的同名方法。以為這樣就可以搞定(實(shí)際上確實(shí)可以搞定),但驗(yàn)證過(guò)程中卻發(fā)生了奇怪的事情。
軟件環(huán)境:Windows XP、Resin、Tomcat6.0、Myeclipse、JDK1.5
發(fā)展:
我的驗(yàn)證思路是這樣的: 1、定義一個(gè)Servlet,然后在該Servlet中調(diào)用Path類(lèi)的getPath方法,getPath方法返回工程classpath的絕對(duì)路徑,顯示在jsp中。 2、另外在Path類(lèi)中,通過(guò)Class的getResourceAsStream讀取當(dāng)前工程classpath路徑中的a.txt文件,寫(xiě)入到getResource路徑下的b.txt。
由于時(shí)間匆忙,代碼沒(méi)有好好去組織。大致能看出上述兩個(gè)功能,很簡(jiǎn)單不做解釋。
Path.javapublic class Path { public String getPath() throws IOException{ InputStream is = this.getClass().getResourceAsStream('/a.txt'); File file = new File(Path.class.getResource('/').getPath()+'/b.txt'); OutputStream os = new FileOutputStream(file); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = is.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); is.close(); return this.getClass().getResource('/').getPath(); }}
PathServlet.javapublic class PathServlet extends HttpServlet { private static final long serialVersionUID = 4443655831011903288L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Path path = new Path(); request.setAttribute('path', path.getPath()); PrintWriter out = response.getWriter(); out.println('Class.getResource('/').getPath():' + path.getPath()); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }}
在此之前使用main函數(shù)測(cè)試Path.class.getResource('/').getPath()打印出預(yù)想的路徑為:/D:/work/project/EhCacheTestAnnotation/WebRoot/WEB-INF/classes/
于是將WEB應(yīng)用部署到Resin下,運(yùn)行定義好的Servlet,出乎意料的結(jié)果是:/D:/work/resin-3.0.23/webapps/WEB-INF/classes/ 。特別奇怪,怎么會(huì)丟掉項(xiàng)目名稱(chēng):EhCacheTestAnnotation呢?
還有一點(diǎn)值得注意,getPath方法中使用getResourceAsStream('/a.txt')卻正常的讀到了位于下圖的a.txt。
然后寫(xiě)到了如下圖的b.txt中。代碼中是這樣實(shí)現(xiàn)的:File file = new File(Path.class.getResource('/').getPath()+'/b.txt');本意是想在a.txt文件目錄下再寫(xiě)入b.txt。結(jié)果卻和料想的不一樣。
請(qǐng)注意,區(qū)別還是丟掉了項(xiàng)目名稱(chēng)。
寫(xiě)的比較亂,稍微總結(jié)下:
程序中使用ClassLoader的兩個(gè)方法:getResourceAsStream和getResource。但是事實(shí)證明在WEB應(yīng)用的場(chǎng)景下卻得到了不同的結(jié)果。大家別誤會(huì)啊,看名字他們兩個(gè)方法肯定不一樣,這個(gè)我知道,但是getResourceAsStream總會(huì)獲取指定路徑下的文件吧,示例中的參數(shù)為'/a.txt',正確讀取“/D:/work/resin-3.0.23/webapps/EhCacheTestAnnotation/WEB-INF/classes/ ”下的a.txt,可是將文件寫(xiě)到getResource方法的getPath返回路徑的b.txt文件。兩個(gè)位置的差別在項(xiàng)目名稱(chēng)(EhCacheTestAnnotation)。
這樣我暫且得出一個(gè)結(jié)論:通過(guò)getResourceAsStream和getResource兩個(gè)方法獲取的路徑是不同的。但是為什么呢?
于是查看了ClassLoader的源碼,貼出getResource和getResourceAsStream的源碼。
public URL getResource(String name) { URL url; if (parent != null) { url = parent.getResource(name); } else { url = getBootstrapResource(name); } if (url == null) { url = findResource(name); } return url; } public InputStream getResourceAsStream(String name) { URL url = getResource(name); try { return url != null ? url.openStream() : null; } catch (IOException e) { return null; } }
從代碼中看,getResourceAsStream將獲取URL委托給了getResource方法。天啊,這是怎么回事兒?由此我徹底迷茫了,百思不得其解。
但是沒(méi)有因此就放棄,繼續(xù)回想了一遍整個(gè)過(guò)程:
1、在main函數(shù)中,測(cè)試getResource與getResourceAsStream是完全相同的,正確的。 2、將其部署到Resin下,導(dǎo)致了getResource與getResourceAsStream獲取的路徑不一致。
一個(gè)閃光點(diǎn),是不是與web容器有關(guān)啊,于是換成Tomcat6.0。OMG,“奇跡”出現(xiàn)了,真的是這樣子啊,換成Tomcat就一樣了??!和預(yù)想的一致。
在Tomcat下運(yùn)行結(jié)果如下圖:
對(duì),這就是我想要的。
因此我對(duì)Resin產(chǎn)生了厭惡感,之前也因?yàn)樵?span>Resin下程序報(bào)錯(cuò),在Tomcat下正常運(yùn)行而糾結(jié)了好久。記得看《松本行弘的程序世界》中對(duì)C++中的多繼承是這樣評(píng)價(jià)的(大概意思):多重繼承帶來(lái)的負(fù)面影響多數(shù)是由于使用不當(dāng)造成的。是不是因?yàn)閷?duì)Resin使用不得當(dāng)才使得和Tomcat下得到不同的結(jié)果。
最終,在查閱Resin配置文件resin.conf時(shí)候在
class-loader> compiling-loader path='webapps/WEB-INF/classes'/> library-loader path='webapps/WEB-INF/lib'/> class-loader>
其中的compiling-loader很可能與之有關(guān),遂將其注釋掉,一切正常。擔(dān)心是錯(cuò)覺(jué),于是將compiling-loader的path屬性改成:webapps/WEB-INF/classes1,然后運(yùn)行pathServlet,b.txt位置如下圖:
確實(shí)與compiling-loader有關(guān)。
結(jié)局:
終于通過(guò)將
疑問(wèn):
但是沒(méi)有這樣就結(jié)束,我繼續(xù)對(duì)getResource的源碼進(jìn)行了跟進(jìn),由于能力有限,沒(méi)有弄清楚getResource的原理。
最終留下了兩個(gè)疑問(wèn):
1、如果追蹤到getResource方法的最底層(也許是JVM層面),它實(shí)現(xiàn)的原理是什么? 2、為何Resin中
在這里也請(qǐng)明白人指明。
|
|