Java程序中的配置文件的存放和讀取

大家可能經常會遇到在Java程序中存取程序配置文件的需求,比如,爲了能夠和不同的數據庫連接,我們經常把數據庫連接的信息存放到屬性文件中,這些信息一般包括數據庫驅動程序類名、數據庫連接的URL,數據庫的用戶名和口令等等。爲了便於程序的安裝或部署,我們經常會把這些的配置文件存放到程序安裝的根目錄中。由於Java程序用包來分組類,有時候將這些配置文件放入到讀取它們的類所在的包目錄中會更好一些。比如,在下面的圖示中,將數據庫配置文件 database.properties放到數據庫讀取類所在的包目錄就是一種比較清晰的存儲方案:

                        /-----------其它包目錄
                              |
                              |
                              -------edu.ec.database
                                                 |
                                                 |---------------ConnectionPool(數據庫連接池類)
                                                 |
                                                 |---------------Dao(數據庫訪問對象類)
                                                 |
                                                 |---------------DaoFactory(Dao的工廠類)
                                                 |
                                                 |---------------database.properties(數據庫配置屬性文件)
                                                 |
                                                 |---------------RecordSet(記錄集類)
   
在這種存儲方案中,所有的與數據庫相關的類和配置文件都在同一個包目錄中;在開發過程中,配置文件和源文件也按採用這種方式進行組織,這樣會使得程序的數據庫訪問維護變得相當清晰明瞭。

大部分開發工具在編譯打包這樣的源文件組織時,會自動將相關配置文件和類文件放到生成的目標文件夾中或JAR文件中。一般情況下,我們在發佈自己的 Java程序時,都是以JAR或WAR形式將程序打包發佈,而對應的配置文件也會按照上述的目錄格式被放到了JAR或WAR文件中,這樣,就實現了配置文件和程序文件打包在一起發佈的目的。

現在的問題是,我們如何讀取位於程序安裝文件中的配置文件的信息?比如,在上面的圖中, ConnectionPool是一個數據庫連接池類,它需要在系統啓動時自動讀取存儲在database.properties中的數據庫連接和配置信息,以便設置相關的數據庫連接。這樣,我們 就需要在程序中測定目前程序安裝或部署的位置,以便讀取對應的屬性文件。

       在很多其他語言編寫的程序中,我們可以利用一些系統提供的API或一些全局對象獲取目前應用程序運行所在的目錄。比如VB,我們可以使用Application對象測定當前程序的安裝位置,在Java程序中如何完成類似的任務呢?

Java程序並沒有類似於VB那種全局對象,但如果我們觀察位於上述目錄結構中的database.properties文件,應該發現其處於應用程序的CLASSPATH中,這樣,我們就可以使用Java中的類裝載器中的相關的方法,讀出這些配置文件的信息,該類名爲ClassLoader。比如,在上例中,我們可以先得到ConnectinPool的類裝載器,然後測定ConnectionPool類所在的包路徑,然後利用 ConnectionPool所在的包目錄讀出database.properties文件的信息,對應的僞代碼如下:

            ClassLoader loader=ConnectionPool.class.getClassLoader();
            得到ConnectionPool所在的包名;
            將包名轉換爲對應的目錄名,存入一個名爲path的字符串變量中;
           調用loader的getResourceAsStream(path+"database.properties"),得到輸入流
          
          下面是一個可實際運行的樣例代碼片段,它可自動測定傳入的類所在的包目錄,返回傳入的屬性文件所代表的輸入流。它還有一個附加的功能:如果屬性文件直接放到了當前類所在的根目錄(比如位於JAR文件的根目錄或WAR文件的WEB-INF/classes目錄中)、系統的用戶目錄系統、系統其他的類路徑中時,它也可以找到;當然,如果還是找不到,它將返回null。具體的代碼如下:


        public class PropHelper{
                      /**
                       *guessPropFile:
                       *@param cls:和要尋找的屬性文件處於相同的包中的任意的類
                       *@param propFile:要尋找的屬性文件名
                       */
                   public   static java.io.InputStream guessPropFile(Class cls,String propFile){
                                try{
                                      //得到類的類裝載器
                                      ClassLoader loader=cls.getClassLoader();
                                     
                                      //先從當前類所處路徑的根目錄中尋找屬性文件
                                      java.io.InputStream in=loader.getResourceAsStream(propFile);
                                      if(in!=null) return in;
                                     
                                      //沒有找到,就從該類所處的包目錄中查找屬性文件
                                     Package pack=cls.getPackage();
                                     if(pack!=null){
                                              String packName=pack.getName();
                                              String path="";
                                              if(packName.indexOf(".") < 0 )
                                                 path=packName+"/";
                                              else{
                                                       int start=0,end=0;
                                                       end=packName.indexOf(".");
                                                       while(end!=-1){
                                                             path=path+packName.substring(start,end)+"/";
                                                             start=end+1;
                                                            end=packName.indexOf(".",start);
                                                        }
                                                       path=path+packName.substring(start)+"/";
                                              }
                                              in=loader.getResourceAsStream(path+propFile);
                                              if(in!=null) return in;
                                   }
                                  
                                  //如果沒有找到,再從當前系統的用戶目錄中進行查找
                                  java.io.File f=null;
                                  String curDir=System.getProperty("user.dir");
                                  f=new java.io.File(curDir,propFile);
                                  if(f.exists()) return new java.io.FileInputStream(f);
                                 
                                  //如果還是沒有找到,則從系統所有的類路徑中查找
                                  String classpath=System.getProperty("java.class.path");
                                  String[] cps=classpath.split(System.getProperty("path.separator"));
                                 
                                  for(int i=0;i < cps.length; i++){
                                           f=new java.io.File(cps[i],propFile);
                                           if(f.exists()) break;
                                           f=null;
                                   }
                                  if(f!=null) return new java.io.FileInputStream(f);
                                  return null;
                            }catch(Exception e){throw new RuntimeException(e);}
                  
                     }
        }
        
使用舉例:利用上述的方法,可在ConnectionPool中自動查找和ConnectionPool處於同一個包目錄中的database.properties的輸入流,並利用該輸入流讀入對應的屬性值的代碼如下:
       public class ConnectionPool{
            //靜態初始化器,將在ConnectionPools加載時自動執行
           static{
               java.util.Properties dbProp=new java.util.Properties();
               java.io.InputStream in=PropHelper.guessPropFile(edu.ec.database.ConnectionPool.class,"database.properties");
               if(in!=null) dbProp.load(in);
               //利用dbProp,爲相應的數據源對象設置相關的屬性,比如C3P0........
            }
     }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章