子在川上曰

  逝者如斯夫不舍昼夜
随笔 - 71, 文章 - 0, 评论 - 915, 引用 - 0
数据加载中……

在Eclipse RCP中使用Spring

注:在发完此文后,我惊奇的发现在新版Eclipse中(我用的是3.2M6)已经不需要转换ClassLoader。估计这是RCP的一个BUG,已经被FIX。希望各位同学共同验证一下,如果是这样的话,这篇文章也就没有什么意义了。


在RCP中使用Spring,最关键的一点在于spring配置文件的读取,因为RCP使用自己的ClassLoader,所以用通常的方法是无法装载Spring的配置文件。解决的思路是:在读取Spring配置文件时将RCP的ClassLoader暂时换一下。

在这里我根据Spring配置文件在项目中的存放位置,给出两种办法。

一、配置文件存放在源代码根目录下。

假设我有一个叫admin_console的项目,我把Spring的配置文件myspring.xml放在源代码根据目录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);
        }



二、配置文件存放在项目根目录的某个子目录下

项目根目录和源代码根目录是不同的两个概念。如上图的项目结构中,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() {}
 
    /**
     * 判断当前的运行状态是否为插件方式
     * @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;
        }
    }
 
}


三、总结

上面两种方式那一种更好呢?应该是第二种。一般来说,源代码的编译文件会打成一个jar包(其实不打成一个JAR包也可以的,我在很多以前就尝试过将class文件松散的部署,如果哪个类要修改,修改后就只部署覆盖这个class,看起来也挺方便。不过这种方式不是最佳实践,不推荐正式发布时使用,一不心可能引起依赖它的其他类出现问题。)。如果用第一种方式在项目打包后,myspring.xml会打包到jar文件中,这样不利于今后对myspring进行动态修改。如果用第二种就没有这种缺点。

很多时候,在Eclipse开发环境中,运行RCP程序没有问题。但导出项目后,在独立的环境中却报配置文件(不光是Spring)找不到的错误,解决的方法都基本与此相同。

posted on 2006-04-26 17:44 陈刚 阅读(7497) 评论(22)  编辑  收藏 所属分类: Eclipse

评论

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
ctx = new ClassPathXmlApplicationContext("/myspring.xml");
} finally {
Thread.currentThread().setContextClassLoader(oldLoader);
}
1,这端代码放到RCP的Application中可以吗?
2,ClassPathXmlApplicationContext在RCP开发中没有这个类,如果按同样的方法来配置Hibernate应该使用那个类呢?
3,恕我愚笨能给详细教教小弟配置一下吗?
2006-08-23 21:42 | qiuliang

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

其实我得,目的就是在RCP中配置Hibernate!spring我并不懂,陈先生,如果我问的不对还请你见凉,但是我真的很渴望知道怎么在RCP中配置和使用Hibernate!
2006-08-23 21:45 | qiuliang

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

@qiuliang
关于hibernate看这篇文章
http://www.blogjava.net/chengang/archive/2006/08/24/65484.html
2006-08-24 12:29 | 陈刚

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

这个问题貌似并没有在ECLIPSE3.2中被完美解决。事实上也不是一个需要被解决的BUG。我曾经碰到过相同的问题,特地下载了ECLIPSE3.2试验,但是并没有成功。
谈谈这个问题的根源:ECLIPSE的每个PLUGIN被自己专属的CLASSLOADER所加载,专属的CLASSLOADER也只能识别出当前PLUGIN下的类和配置文件,所以如果在当前PLUGIN下调用有依赖关系的另一个PLUGIN中的CLASS来动态加载当前PLUGIN下的类或者配置文件,那么错误就发生了,因为另一个PLUGIN的CLASSLOADER找不到它需要的类或者配置文件。
ECLIPSE 3.1起对这个问题有一个优雅的解决方式,就是在上述的两个PLUGIN的MANIFEST文件中配置注册关系。

陈师傅可以验证一下我的说法。有空我可以贴出例证代码。
2006-09-26 11:42 | JIAWEI

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

@JIAWEI
是的新版Eclipse已经解决这个问题。
2006-10-08 09:57 | 陈刚

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

没仔细研究过RCP的ClassLoader,不太理解陈师傅所说。

我在插件开发中是使用spring的方法类似陈师傅说的第二种:放在properties文件夹下。不过我没有用ProjectUtils这样的方法找绝对路径,我的方法是:
1、将properties文件夹加入到项目的Runtime ClassPath中
2、在getApplicatonContext时用ClassPathXmlApplicationContext就能去properties文件夹中去找;

BTW:
RCP的ClassLoader是不是就是在启动时自动装载所需类?
2007-03-24 10:45 | 乌黑的大白马

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

陈老师,请问下我用您的第二种方式的的话,那打包成Jar文件中会不会有什么错误,打包后应该只有一个bin目录,没有那个src和config(配置文件放这里),可以得到文件路径吗?
2007-08-08 11:33 | liuraoxing

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

打包后不会有 bin目录的,所以你打包的目录结构错了。
具体请参考<Eclipse从入门到精通>第二版的,第32章'RCP项目的打包和发行',重点看图32.13的下图
2007-08-10 08:37 | 陈刚

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

@乌黑的大白马
请看文章前面的红色文字,这篇文章实际已经没有意义。
2007-08-10 08:39 | 陈刚

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

为什么,我是安装陈老师说的那样,但是为什么还报这样的错误,能给予说明下吗?
!ENTRY org.eclipse.osgi 4 0 2007-09-21 09:14:12.015
!MESSAGE Application error
!STACK 1
org.eclipse.core.runtime.CoreException: Plug-in TestPlug was unable to load class testplug.Application.
at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.throwException(RegistryStrategyOSGI.java:165)
at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:149)
at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:759)
at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243)
at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:51)
at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:74)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:92)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:68)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:177)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.core.launcher.Main.invokeFramework(Main.java:336)
at org.eclipse.core.launcher.Main.basicRun(Main.java:280)
at org.eclipse.core.launcher.Main.run(Main.java:977)
at org.eclipse.core.launcher.Main.main(Main.java:952)
org.eclipse.core.runtime.CoreException[1]: java.lang.ClassNotFoundException: testplug.Application
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:402)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:347)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:83)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at org.eclipse.osgi.framework.internal.core.BundleLoader.loadClass(BundleLoader.java:278)
at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleHost.java:227)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadClass(AbstractBundle.java:1245)
at org.eclipse.core.internal.registry.osgi.RegistryStrategyOSGI.createExecutableExtension(RegistryStrategyOSGI.java:147)
at org.eclipse.core.internal.registry.ExtensionRegistry.createExecutableExtension(ExtensionRegistry.java:759)
at org.eclipse.core.internal.registry.ConfigurationElement.createExecutableExtension(ConfigurationElement.java:243)
at org.eclipse.core.internal.registry.ConfigurationElementHandle.createExecutableExtension(ConfigurationElementHandle.java:51)
at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:74)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:92)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:68)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:177)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.core.launcher.Main.invokeFramework(Main.java:336)
at org.eclipse.core.launcher.Main.basicRun(Main.java:280)
at org.eclipse.core.launcher.Main.run(Main.java:977)
at org.eclipse.core.launcher.Main.main(Main.java:952)
2007-09-21 09:15 | cj_829cai

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

@cj_829cai
unable to load class testplug.Application
这一句报错是关键。说明你的项目配置有问题,导致找不到Application类。
2007-09-24 08:46 | 陈刚

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

陈老师,有没有简单一点的例题?
如果有时间话能发一下吗?
为了这个问题.我还专门买了你的书第二版里可惜没有讲到.
caijin_001@163.com
2007-09-24 10:03 | cj_829cai

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

这次是找不到spring文件.一开始我以为是导包的问题,我确定包是没有问题的.我用一个测试类已经能正常使用spring.但是把这个正确的代码拷贝过来就出问题了
.我是按照你说的把类加载器转换了一下.还是出现这样的错误.急啊
!ENTRY org.eclipse.ui.workbench 4 0 2007-09-24 09:55:44.635
!MESSAGE Unable to create view ID MyCRP.view2: org/springframework/beans/factory/BeanFactory
!STACK 0
java.lang.NoClassDefFoundError: org/springframework/beans/factory/BeanFactory
at com.view.TestView.seleteD(TestView.java:71)
at com.view.TestView.createPartControl(TestView.java:55)
at org.eclipse.ui.internal.ViewReference.createPartHelper(ViewReference.java:332)
at org.eclipse.ui.internal.ViewReference.createPart(ViewReference.java:197)
at org.eclipse.ui.internal.WorkbenchPartReference.getPart(WorkbenchPartReference.java:566)
at org.eclipse.ui.internal.PartPane.setVisible(PartPane.java:290)
at org.eclipse.ui.internal.ViewPane.setVisible(ViewPane.java:525)
at org.eclipse.ui.internal.presentations.PresentablePart.setVisible(PresentablePart.java:140)
at org.eclipse.ui.internal.presentations.util.PresentablePartFolder.select(PresentablePartFolder.java:268)
at org.eclipse.ui.internal.presentations.util.LeftToRightTabOrder.select(LeftToRightTabOrder.java:65)
at org.eclipse.ui.internal.presentations.util.TabbedStackPresentation.selectPart(TabbedStackPresentation.java:394)
at org.eclipse.ui.internal.PartStack.refreshPresentationSelection(PartStack.java:1144)
at org.eclipse.ui.internal.PartStack.setSelection(PartStack.java:1097)
at org.eclipse.ui.internal.PartStack.showPart(PartStack.java:1311)
at org.eclipse.ui.internal.PartStack.createControl(PartStack.java:601)
at org.eclipse.ui.internal.PartStack.createControl(PartStack.java:532)
at org.eclipse.ui.internal.PartSashContainer.createControl(PartSashContainer.java:562)
at org.eclipse.ui.internal.PerspectiveHelper.activate(PerspectiveHelper.java:244)
at org.eclipse.ui.internal.Perspective.onActivate(Perspective.java:815)
at org.eclipse.ui.internal.WorkbenchPage.onActivate(WorkbenchPage.java:2436)
at org.eclipse.ui.internal.WorkbenchWindow$6.run(WorkbenchWindow.java:2616)
at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:67)
at org.eclipse.ui.internal.WorkbenchWindow.setActivePage(WorkbenchWindow.java:2597)
at org.eclipse.ui.internal.WorkbenchWindow.busyOpenPage(WorkbenchWindow.java:658)
at org.eclipse.ui.internal.Workbench.busyOpenWorkbenchWindow(Workbench.java:795)
at org.eclipse.ui.internal.Workbench.doOpenFirstTimeWindow(Workbench.java:1437)
at org.eclipse.ui.internal.Workbench.openFirstTimeWindow(Workbench.java:1388)
at org.eclipse.ui.internal.WorkbenchConfigurer.openFirstTimeWindow(WorkbenchConfigurer.java:190)
at org.eclipse.ui.application.WorkbenchAdvisor.openWindows(WorkbenchAdvisor.java:708)
at org.eclipse.ui.internal.Workbench.init(Workbench.java:1085)
at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1847)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:419)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
at mycrp.Application.run(Application.java:18)
at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:78)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:92)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:68)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:177)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.core.launcher.Main.invokeFramework(Main.java:336)
at org.eclipse.core.launcher.Main.basicRun(Main.java:280)
at org.eclipse.core.launcher.Main.run(Main.java:977)
at org.eclipse.core.launcher.Main.main(Main.java:952)
2007-09-24 10:20 | cj_829cai

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

谢谢陈老师.我在你的Hibernate中找到的答案,谢谢了
2007-09-25 11:29 | cj_829cai

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

@cj_829cai
举一推三,聪明
2007-09-26 01:12 | 陈刚

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

陈老是,还有一个问题。不知道RCP里面能不能使用SWT的shell窗体?
2007-09-29 18:07 | cj_829cai

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

@cj_829cai
你试过了吗
2007-09-29 20:54 | 陈刚

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

我试过了。是可以用的。但是有一个问题。就是shell窗口是没有办法,弹出需要在任务栏里点击才能现实出窗体。不知道这是什么原因?
2007-10-09 15:09 | cj_829cai

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

@cj_829cai
那是shell的参数没有设置好。参考一样本书SWT内容中关于shell的介绍。
2007-10-10 08:49 | 陈刚

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

陈老师。能否把TitleAreaDialog变成非模式的窗口?
2007-10-16 18:29 | cj_829cai

# re: 在Eclipse RCP中使用Spring  回复  更多评论   

com.wxxr.management.admin.console.AdminConsolePlugin;
请教这一个包名是什么? 我没找到相应的jar文件。
2008-06-18 19:04 | win

# re: 在Eclipse RCP中使用Spring解析问题  回复  更多评论   

你好,我用你的方法,文件找到了。可是解析出现了不少问题,求指导
2014-05-20 09:15 | 王传

只有注册用户登录后才能发表评论。


网站导航: