大部分的java程序應用於UNIX/Linux系統,而絕大部分的開發是在Windows下。雖然,java可以運行在anywhere, 但畢竟還有很多環境配置問題。
例如在UNIX下,你需要將某些配置文件的路徑寫入到另一個配置文件。 也許有很多局限,使你必須寫入絕對路徑。
在config.properties裡寫入
logs = /logs/app/db/logs.properties
configs=/usr/WebSphere/AppServer/installedApps/appname/earname/warname/WEB-INF/properties/myconfig.properties
在開發階段,你是否願意在你的Windows開發機上建立上面這樣的目錄,或者逐個修改這個路徑呢? 尤其在已有的系統下,為了開發新的功能,構築開發環境時,這種配置文件路徑的修改是相當花時間的。 並且,在Release時,你必須要使用Ant工具批量修改這些配置文件。 但我可以說,大部分項目只有給生產和系統集成測試環境才會配置Ant工具。而在低級別的測試環境下,你只能手動更改。 那麼如何才能不修改任何文件可以再windows本地調試並運行呢?
一下,我給出一個小小方案。
1. 重寫java.io.File類!
先不要向我丟香蕉皮, 重寫java.io.File並不一定要變動rt.jar文件。 jvm支持pretend,也就是偽裝,我可以把我重寫的java.io.File在運行期時代替rt.jar原有的java.io.File類。 想了解更詳細的信息可以在 JAVA_HOME裡找這個文件:[ JAVA_HOME]\bin\client\Xusage.txt
-Xbootclasspath/p:<directories and zip/jar files separated by ;>
prepend in front of bootstrap class path
在調試時,我就是要用這個參數。假設,我把重寫的java.io.File類文件打包為filemap_1_4.jar。調試時,我就可以運行 java -Xbootclasspath/p:D:\MyProject\FileMap/filemap_1_4.jar -cp ...
這樣,在我調用的所有類裡,涉及到文件或文件系統功能時,都調用D:\MyProject\FileMap/filemap_1_4.jar 下面的java.io.File而不是rt.jar.
2. 功能實現
2.1 文件目錄映射關系
為了增加一些靈活性, 我使用一個目錄映射文件,來定義UNIX/LINUX文件路徑和Windows文件路徑的映射關系。
例如,filemap.properties
/usr/WebSphere/AppServer/installedApps/appname/earname/warname/=C:/MyProject/
/logs/app/db/=c:/MyProject/logs
當程序要讀取/usr/WebSphere/AppServer/installedApps/appname/earname/warname/WEB-INF/properties/myconfig.properties
文件時,java.io.File會映射到C:/MyProject/WEB-INF/properties/myconfig.properties。
2.2 java.io.File更改
增加一個靜態變量 private static HashMap filemaps=null;用來保存映射關系。
增加一個私有方法 initmaps初始化 filemaps
/** * read filemap.propreties to initialize file map. */ private void initmaps(){ if(filemaps==null){ filemaps=new HashMap(); String filemap=System.getProperty("filemap"); //獲得filemap.properties文件路徑, 需要在jvm運行時傳入-Dfilemap=[filemap.properties全路徑名],不要試圖使用 classloader.getResource(), 因為getResource裡也會使用java.io.File,會產生jvm異常。 if(filemap==null || filemap=="") return; this.path = fs.normalize(filemap); //准備讀取filemap.properties文件。因為使用FileInputStream時,需要傳入一個java.io.File對象,在這暫 時把this.path設為filemap.properties的路徑。 this.prefixLength = fs.prefixLength(this.path); Properties pro=new Properties(); try { pro.load(new FileInputStream(this)); //讀取filemap.properties. Enumeration enumeration=pro.propertyNames(); while(enumeration.hasMoreElements()){ String sourcepath=(String)enumeration.nextElement(); String targetpath=pro.getProperty(sourcepath); filemaps.put(sourcepath, targetpath); //保存到filemaps靜態對象裡。 } } catch(FileNotFoundException e1){ return; } catch(IOException e2){ return; } } } 我們還需要一個私有方法轉換路徑。 /** * Get Virutal Path string * @param name 原UNIX/Linux路徑。 * @return 新windows路徑。 */ private String getVirtualPath(String name){ Iterator sources=filemaps.keySet().iterator(); while(sources.hasNext()){ String source=(String)sources.next(); if(name.startsWith(source)==true){ //當原路徑包含filemaps裡某一個source路徑時,將原路徑轉換為新的target路徑。 String target=(String)filemaps.get(source); name=target+name.substring(source.length()); } } return name; } 好了,現在准備在java.io.File裡調用這兩個方法 /** * Creates a new <code>File</code> instance by converting the given * pathname string into an abstract pathname. If the given string is * the empty string, then the result is the empty abstract pathname. * * @param pathname A pathname string * @throws NullPointerException * If the <code>pathname</code> argument is <code>null</code> */ public File(String pathname) { //new function initmaps(); if (pathname == null) { throw new NullPointerException(); } //new function pathname=getVirtualPath(pathname); this.path = fs.normalize(pathname); this.prefixLength = fs.prefixLength(this.path); } public File(String parent, String child) { //new function initmaps(); if (child == null) { throw new NullPointerException(); } //new function child=getVirtualPath(child); parent=getVirtualPath(parent); if (parent != null) { if (parent.equals("")) { this.path = fs.resolve(fs.getDefaultParent(), fs.normalize(child)); } else { this.path = fs.resolve(fs.normalize(parent), fs.normalize(child)); } } else { this.path = fs.normalize(child); } this.prefixLength = fs.prefixLength(this.path); } public File(File parent, String child) { //new function initmaps(); child=getVirtualPath(child); if (child == null) { throw new NullPointerException(); } if (parent != null) { if (parent.path.equals("")) { this.path = fs.resolve(fs.getDefaultParent(), fs.normalize(child)); } else { String parentpath=getVirtualPath(parent.path); this.path = fs.resolve(parent.path, fs.normalize(child)); } } else { this.path = fs.normalize(child); } this.prefixLength = fs.prefixLength(this.path); }2.3 打包
將java.io.File編譯並打包成jar文件。 filemap_1_4.jar
2.4 設置調試環境。
在你需要調試環境裡不需要把這個jar文件發入classpath,但需要在VM arguments裡加上-Xbootclasspath/p:C:\MyProject\filemap_1_4.jar -Dfilemap=C:\MyProject\filemap.properties
3 測試
編寫測試程序
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; public class Test { /** * @param args */ public static void main(String[] args) { FileReader filereader; try { //打印/usr/WebSphere/AppServer/InstallApp/Test.java filereader = new FileReader( "/usr/WebSphere/AppServer/InstallApp/Test.java"); BufferedReader bufferedreader = new BufferedReader(filereader); String line=null; while((line=bufferedreader.readLine())!=null) System.out.println(line); //遍歷/usr/WebSphere/AppServer/InstallApp/Test.java所在的目錄下所有文件,並打印文件名。 File fl=new File("/usr/WebSphere/AppServer/InstallApp/Test.java"); String path=fl.getParent(); String[] files=new File(path).list(); for(int i=0;i<files.length;i++){ System.out.println(files[i]); } } catch (Exception e) { e.printStackTrace(); } }