轉載自:http://tb.blog.csdn.net/TrackBack.aspx?PostId=1427475 Java
路徑問題最終解決方案
—
可定位所有資源的相對路徑定址
前言Java的路徑問題,非常難搞。最近的工作涉及到建立和讀取檔案的工作,這裡我就給大家徹底得解決Java路徑問題。我編寫了一個方法,比ClassLoader.getResource(String 相對路徑)方法的能力更強。它可以接受“../”這樣的參數,允許我們用相對路徑來定位classpath外面的資源。這樣,我們就可以使用相對於classpath的路徑,定位所有位置的資源!
Java路徑Java中使用的路徑,分為兩種:絕對路徑和相對路徑。具體而言,又分為四種:
一、URI形式的絕對資源路徑如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.bURL是URI的特例。URL的首碼/協議,必須是Java認識的。URL可以開啟資源,而URI則不行。URL和URI對象可以互相轉換,使用各自的toURI(),toURL()方法即可!
二、本地系統的絕對路徑D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.bJava.io包中的類,需要使用這種形式的參數。但是,它們一般也提供了URI類型的參數,而URI類型的參數,接受的是URI樣式的String。因此,通過URI轉換,還是可以把URI樣式的絕對路徑用在java.io包中的類中。
三、相對於classpath的相對路徑如:相對於file:/D:/java/eclipse32/workspace/jbpmtest3/bin/這個路徑的相對路徑。其中,bin是本項目的classpath。所有的Java源檔案編譯後的.class檔案複製到這個目錄中。
四、相對於目前使用者目錄的相對路徑就是相對於System.getProperty("user.dir")返回的路徑。對於一般項目,這是項目的根路徑。對於JavaEE伺服器,這可能是伺服器的某個路徑。這個並沒有統一的規範!所以,絕對不要使用“相對於目前使用者目錄的相對路徑”。然而:
預設情況下,java.io 包中的類總是根據目前使用者目錄來分析相對路徑名。此目錄由系統屬性 user.dir 指定,通常是 JAVA 虛擬機器的調用目錄。這就是說,在使用java.io包中的類時,最好不要使用相對路徑。否則,雖然在J2SE應用程式中可能還算正常,但是到了J2EE程式中,一定會出問題!而且這個路徑,在不同的伺服器中都是不同的!
相對路徑最佳實務
推薦使用相對於當前classpath的相對路徑因此,我們在使用相對路徑時,應當使用相對於當前classpath的相對路徑。ClassLoader類的getResource(String name),
getResourceAsStream(String name)等方法,使用相對於當前項目的classpath的相對路徑來尋找資源。讀取屬性檔案常用到的ResourceBundle類的getBundle(String path)也是如此。通過查看ClassLoader類及其相關類的原始碼,我發現,它實際上還是使用了URI形式的絕對路徑。通過得到當前classpath的URI形式的絕對路徑,構建了相對路徑的URI形式的絕對路徑。(這個實際上是猜想,因為JDK內部調用了SUN的原始碼,而這些代碼不屬於JDK,不是開源的。)
相對路徑本質上還是絕對路徑
因此,歸根結底,
Java
本質上只能使用絕對路徑來尋找資源。所有的相對路徑尋找資源的方法,都不過是一些便利方法。不過是
API
在底層協助我們構建了絕對路徑,從而找到資源的!
得到classpath和當前類的絕對路徑的一些方法 下面是一些得到classpath和當前類的絕對路徑的一些方法。你可能需要使用其中的一些方法來得到你需要的資源的絕對路徑。
1
,FileTest.class.getResource("")得到的是當前類FileTest.class檔案的URI目錄。不包括自己!如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/com/test/
2
,FileTest.class.getResource("/")得到的是當前的classpath的絕對URI路徑。如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
3
,Thread.currentThread().getContextClassLoader().getResource("")得到的也是當前ClassPath的絕對URI路徑。如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
4
,FileTest.class.getClassLoader().getResource("")得到的也是當前ClassPath的絕對URI路徑。如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
5
,ClassLoader.getSystemResource("")得到的也是當前ClassPath的絕對URI路徑。如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
我推薦使用
Thread.currentThread().getContextClassLoader().getResource("")
來得到當前的classpath的絕對路徑的URI標記法。
Web應用程式中資源的定址 上文中說過,目前使用者目錄,即相對於System.getProperty("user.dir")返回的路徑。對於JavaEE伺服器,這可能是伺服器的某個路徑,這個並沒有統一的規範!而不是我們發布的Web應用程式的根目錄!這樣,在Web應用程式中,我們絕對不能使用相對於目前使用者目錄的相對路徑。在Web應用程式中,我們一般通過ServletContext.getRealPath("/")方法得到Web應用程式的根目錄的絕對路徑。這樣,我們只需要提供相對於Web應用程式根目錄的路徑,就可以構建出定位資源的絕對路徑。這是我們開發Web應用程式時一般所採取的策略。
通用的相對路徑解決辦法Java中各種相對路徑非常多,不容易使用,非常容易出錯。因此,我編寫了一個便利方法,協助更容易的解決相對路徑問題。
Web應用程式中使用JavaSE啟動並執行資源定址問題在JavaSE程式中,我們一般使用classpath來作為存放資源的目的地。但是,在Web應用程式中,我們一般使用classpath外面的WEB-INF及其子目錄作為資源檔的存放地。在Web應用程式中,我們一般通過ServletContext.getRealPath("/")方法得到Web應用程式的根目錄的絕對路徑。這樣,我們只需要提供相對於Web應用程式根目錄的路徑,就可以構建出定位資源的絕對路徑。Web應用程式,可以作為Web應用程式進行發布和運行。但是,我們也常常會以JavaSE的方式來運行Web應用程式的某個類的main方法。或者,使用JUnit測試。這都需要使用JavaSE的方式來運行。這樣,我們就無法使用ServletContext.getRealPath("/")方法得到Web應用程式的根目錄的絕對路徑。而JDK提供的ClassLoader類,它的getResource(String name),
getResourceAsStream(String name)等方法,使用相對於當前項目的classpath的相對路徑來尋找資源。讀取屬性檔案常用到的ResourceBundle類的getBundle(String path)也是如此。它們都只能使用相對路徑來讀取classpath下的資源,無法定位到classpath外面的資源。
Classpath外設定檔讀取問題如,我們使用測試驅動開發的方法,開發Spring、Hibernate、iBatis等使用設定檔的Web應用程式,就會遇到問題。儘管Spring自己提供了FileSystem(也就是相對於user,dir目錄)來讀取Web設定檔的方法,但是終究不是很方便。而且與Web程式中的代碼使用方式不一致!至於Hibernate,iBatis就更麻煩了!只有把設定檔移到classpath下,否則根本不可能使用測試驅動開發! 這怎麼辦?
通用的相對路徑解決辦法面對這個問題,我決定編寫一個助手類ClassLoaderUtil,提供一個便利方法[
public
static URL getExtendResource(String relativePath)]。在Web應用程式等一切Java程式中,需要定位classpath外的資源時,都使用這個助手類的便利方法,而不使用Web應用程式特有的ServletContext.getRealPath("/")方法來定位資源。
利用classpath的絕對路徑,定位所有資源這個便利方法的實現原理,就是“利用classpath的絕對路徑,定位所有資源”。ClassLoader類的getResource("")方法能夠得到當前classpath的絕對路徑,這是所有Java程式都擁有的能力,具有最大的適應性!而目前的JDK提供的ClassLoader類的getResource(String 相對路徑)方法,只能接受一般的相對路徑。這樣,使用ClassLoader類的getResource(String 相對路徑)方法就只能定位到classpath下的資源。
如果,它能夠接受“../
”這樣的參數,允許我們用相對路徑來定位classpath
外面的資源,那麼我們就可以定位位置的資源!當然,我無法修改ClassLoader類的這個方法,於是,我編寫了一個助手類ClassLoaderUtil類,提供了[
public
static URL getExtendResource(String relativePath)]這個方法。它能夠接受帶有“../”符號的相對路徑,實現了自由尋找資源的功能。
通過相對classpath路徑實現自由尋找資源的助手類的原始碼:
後記ClassLoaderUtil類的public static URL getExtendResource(String relativePath),雖然很簡單,但是確實可以解決大問題。不過這個方法還是比較簡陋的。我還想在未來有空時,進一步增強它的能力。比如,增加Ant風格的匹配符。用**代表多個目錄,*代表多個字元,?代表一個字元。達到Spring那樣的能力,一次返回多個資源的URL,進一步方便大家開發。
總結:1,盡量不要使用相對於System.getProperty("user.dir")目前使用者目錄的相對路徑。這是一顆定時炸彈,隨時可能要你的命。2,盡量使用URI形式的絕對路徑資源。它可以很容易的轉變為URI,URL,File對象。3,盡量使用相對classpath的相對路徑。不要使用絕對路徑。使用上面ClassLoaderUtil類的public static URL getExtendResource(String relativePath)方法已經能夠使用相對於classpath的相對路徑定位所有位置的資源。4,絕對不要使用硬式編碼絕對路徑。因為,我們完全可以使用ClassLoader類的getResource("")方法得到當前classpath的絕對路徑。使用硬式編碼絕對路徑是完全沒有必要的!它一定會讓你死的很難看!程式將無法移植!如果你一定要指定一個絕對路徑,那麼使用設定檔,也比寫入程式碼要好得多!當然,我還是推薦你使用程式得到classpath的絕對路徑來拼資源的絕對路徑!--------------------------------------------------------------------------多時候程式需要讀取工程中的一些檔案(如xml、properties等等),而檔案的路徑指定往往會困擾我們的開發工作,尤其在web程式的開發中路徑問題尤為突出,下面就簡單介紹一下java中相對路徑的獲得方法。
- getRealPath():僅限使用在servlet中,可以動態獲得檔案的絕對路徑,部署在伺服器上的工程通常要查看部署後伺服器中的路徑。Java代碼
- this.getServletContext().getRealPath("WEB-INF/classes/com.demo/config.xml")
this.getServletContext().getRealPath("WEB-INF/classes/com.demo/config.xml")
- getResource/getResourceAsStream:Java代碼
- String path =”/xml/config.xml”;
- String fullPath=this.getClass().getClassLoader().getResource(path).getPath();
String path =”/xml/config.xml”; String fullPath=this.getClass().getClassLoader().getResource(path).getPath();
或是
Java代碼
- Class.class.getResource("/file/config.xml").getPath()
Class.class.getResource("/file/config.xml").getPath()
採納了 seaizon 的建議:
seaizon 寫道在使用Class.getResource/Class.getResourceAsStream 時, 資源路徑有兩種方式, 一種以 / 開頭,則這樣的路徑是指定絕對路徑, 如果不以 / 開頭, 則路徑是相對與這個class所在的包的。
在使用ClassLoader.getResource/ClassLoader.getResourceAsStream時, 路徑直接使用相對於classpath的絕對路徑。