注:在發(fā)完此文后,我驚奇的發(fā)現(xiàn)在新版Eclipse中(我用的是3.2M6)已經(jīng)不需要轉(zhuǎn)換ClassLoader。估計這是RCP的一個BUG,已經(jīng)被FIX。希望各位同學(xué)共同驗證一下,如果是這樣的話,這篇文章也就沒有什么意義了。
在RCP中使用Spring,最關(guān)鍵的一點在于spring配置文件的讀取,因為RCP使用自己的ClassLoader,所以用通常的方法是無法裝載Spring的配置文件。解決的思路是:在讀取Spring配置文件時將RCP的ClassLoader暫時換一下。
在這里我根據(jù)Spring配置文件在項目中的存放位置,給出兩種辦法。
一、配置文件存放在源代碼根目錄下。假設(shè)我有一個叫admin_console的項目,我把Spring的配置文件myspring.xml放在源代碼根據(jù)目錄src下,如下圖所示
admin_console
????? --src
?????????? --cn????? //包名
?????????????? --com
????????????????? --chengang?
????????????????????? ---......???? //源代碼類
????????? --myspring.xml????? //Spring配置文件,位于src目錄下和cn目錄平級
??????--bin
????? --lib
????? --icons
????? --properties
那么我們在RCP程序中可以這樣來裝載myspring.xml
????????ClassLoader?oldLoader?=?Thread.currentThread().getContextClassLoader();
????????try?{
????????????Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
????????????ctx?=?new?ClassPathXmlApplicationContext("/myspring.xml");
????????}?finally?{
????????????Thread.currentThread().setContextClassLoader(oldLoader);
????????}
二、配置文件存放在項目根目錄的某個子目錄下項目根目錄和源代碼根目錄是不同的兩個概念。如上圖的項目結(jié)構(gòu)中,src是源代碼根目錄,admin_console是項目根目錄,那么properties就是項目根目錄下的一個子目錄。
如果將myspring.xml放入到properties目錄中,以上的讀取代碼就沒用了,讀取方法如下:
????????ClassLoader?oldLoader?=?Thread.currentThread().getContextClassLoader();
????????try?{
????????????Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
ctx?=?new FileSystemXmlApplicationContext(ProjectUtil.toFullPath("properties/myspring.xml"));
????????}?finally?{
????????????Thread.currentThread().setContextClassLoader(oldLoader);
????????}
其中ProjectUtil.toFullPath是我自己寫的一個方法,主要是得到myspring.xml的絕對路徑,其代碼如下:
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
?
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.ui.plugin.AbstractUIPlugin;
?
import com.wxxr.management.admin.console.AdminConsolePlugin;
?
/**
?* 用于插件項目和非插件項目,提供兩者通用的方法接口
?* @author chengang 2006-3-30
?*/
public class ProjectUtil {
?
??? private static AbstractUIPlugin plugin = AdminConsolePlugin.getDefault();
?
??? private ProjectUtil() {}
?
??? /**
???? * 判斷當(dāng)前的運行狀態(tài)是否為插件方式
???? * @return true=插件方式運行
???? */
??? private static boolean isPlugin() {
??????? return plugin != null;
??? }
?
??? public static URL getURL(String path) {
??????? if (isPlugin())//如果是插件
??????????? return FileLocator.find(plugin.getBundle(), new Path(path), null);
??????? else
??????????? try {
??????????????? return new URL("file:" + path);
??????????? } catch (MalformedURLException e) {
??????????????? throw new RuntimeException(path + " is error", e);
??????????? }
??? }
?
??? public static InputStream getInputStream(String path) {
??????? URL url = getURL(path);
??????? try {
??????????? return url.openStream();
??????? } catch (IOException e) {
??????????? throw new RuntimeException(e);
??????? }
??? }
?
??? public static String toFullPath(String path) {
??????? if (isPlugin()) {
??????????? try {
??????????????? return FileLocator.toFileURL(ProjectUtil.getURL(path)).getPath();
??????????? } catch (IOException e) {
??????????????? throw new RuntimeException(path + " toFullPath is fault", e);
??????????? }
??????? } else {
??????????? return path;
??????? }
??? }
?
}
三、總結(jié)
上面兩種方式那一種更好呢?應(yīng)該是第二種。一般來說,源代碼的編譯文件會打成一個jar包(其實不打成一個JAR包也可以的,我在很多以前就嘗試過將class文件松散的部署,如果哪個類要修改,修改后就只部署覆蓋這個class,看起來也挺方便。不過這種方式不是最佳實踐,不推薦正式發(fā)布時使用,一不心可能引起依賴它的其他類出現(xiàn)問題。)。如果用第一種方式在項目打包后,myspring.xml會打包到j(luò)ar文件中,這樣不利于今后對myspring進行動態(tài)修改。如果用第二種就沒有這種缺點。
很多時候,在Eclipse開發(fā)環(huán)境中,運行RCP程序沒有問題。但導(dǎo)出項目后,在獨立的環(huán)境中卻報配置文件(不光是Spring)找不到的錯誤,解決的方法都基本與此相同。