OSGI+Spring+Hibernate+...完美解决方案[非SpringDM]

                       OSGI+Spring+Hibernate+...完美解决方案[SpringDM]

 

导论

“世间本无事,庸人自扰之”

SpringDM就是一庸人!

 

最近,我做一个项目需要编写Eclipse的插件。我想在Eclipse插件中使用SpringHibernate。但却遇到了巨大的问题。

按照Spring组织的提示,我使用SpringDM1.02编写Spring程序(后来用SpringDM1.1SpringDM又叫SpringOSGI)。但是总是遇到种种问题。特别是Spring管理下的Hibernate,总是无法找到Hibernate的配置文件。

 

到了后来,连Eclipse也挂了。每次重设Eclipse的目标插件集合的时候,Eclipse都会死掉!

 

终于,我再也无法忍受SpringDM的折磨了!

我告诉自己,是时候反思了!

 

终于,我发现了SpringDM根本就是一个完全、彻底失败,无用的废物!

所有的问题都是SpringDM造成的!

而不是SpringDM所说的,OSGIHibernate等等其他软件造成的!

 

我终于发现了OSGI+Spring+Hibernate+...完美解决方案。

不敢藏私,拿出来给各位共享。

 

 

 

问题的根源—ClassLoader

OSGI提供了自己的ClassLoader。每一个OSGI的插件,都有一个独立的ClassLoader被使用。这样就实现了各个插件的独立性。

一个JVM上可以运行无数个OSGI插件,每一个OSGI插件都是独立的,除非发布为OSGI服务,或者发布自己Package

SpringDM的工作原理是这样子的:(我猜想的)

1SpringDM是一个OSGI插件。作为一个一般的插件进行部署。

2SpringDM一旦启动,就会探查所有其他Active状态的插件。

1MANIFEST.MF中没有Spring的头,如果有,执行该配置。

2)如果MANIFEST.MF中没有Spring的头,探查META-INF/spring/目录下有没有.xml文件,如果有,它也是一个SpringDM项目。

    SpringDM探查其他插件的资源,应该使用的是这些插件的BundleContext对象来实现的。

另外,SpringDM应该注册了OSGI的事件,这样,其他插件启动,关闭,都会通知SpringDM

 

如果SpringDM发现一个插件是SpringDM插件。就导入该插件中的Spring配置文件,创建ApplicationContext。然后,把该ApplicationContext作为OSGI服务发布。

如果插件中需要显式的getBean()方法获得对象,就使用它来获得。

 

SpringDM的问题

SpringDMDM----动态管理,这个设计似乎很巧妙!

刚开始我也被SpringDM的这一“强大功能”所折服。

但是,实际上,SpringDM的所有问题根源,就在这里!

 

SpringDM实际上是在SpringDM这个插件中为所有Spring插件创建ApplicationContext的!

显然,此时使用的ClassLoaderSpringDM这个插件的ClassLoader,而不是实际的Spring项目的插件的ClassLoader

并且,当前线程中的ClassLoader,也是SpringDM插件的ClassLoader

这样,就产生了问题。

如,Hibernate,它内部使用当前线程中的ClassLoader来载入配置文件。

Thread.currentThread().getContextClassLoader()

绝大部分现有Java代码都使用这样的代码来获得ClassLoader来载入资源,动态创建类。

现在,使用了SpringDM,所有这些代码的ClassLoader都会出现问题!!!都无法找到资源!!!

这些日子里,我都快被这样的问题给逼疯了!

SpringDM的谎言

SpringDM的官方文档,把这些问题的责任推给了HibernateOSGI。它说,由于OSGI环境下ClassLoader的特殊性,因此使用Thread.currentThread().getContextClassLoader()得到的ClassLoader都是不正确的。

应该使用BundleActivator接口的实现类的Classloader来得到正确的ClassLoader

 

Hibernate这样的设计时未考虑OSGI环境的类库,在OSGI环境中出错是不可避免的。

因此,Hibernate应该修改代码,以适应OSGI环境。

 

而我上了Hibernate网站,没有看到HibernateOSGI项目。

 

我看到有不少朋友在网上写了一些补丁代码,让HibernateSpringDM环境下正确运行。

看来,我们还应该忙着修改数以亿行计的Java代码,把获得ClassLoader的代码修改掉,否则在SpringDMOSGI环境下就会报错!

 

悲惨!!!如果真是如此,OSGI真的没有存在的价值了!!!

 

事实

但是,事实并非如此!!!

 

如果不使用SpringDM,直接在OSGI插件中使用Hibernate,或者Thread.currentThread().getContextClassLoader(),都可以正确载入资源。

因为,OSGI插件使用的ClassLoader是可以直接载入OSGI插件的所有资源的。并且,当前线程中的ClassLoader,也是当前OSGI插件使用的ClassLoader

 

    因此,SpringDM在说谎!

    SpringDM的动态创建ApplicationContext的机制是一系列问题的源泉。

 

OSGI+Spring+Hibernate+...完美解决方案[SpringDM]

只要不使用SpringDM的动态创建ApplicationContext机制,就可以避免上述种种问题。

试一下在BundleActivator接口中直接使用new  ClassPathXmlApplicationContext()的方式创建ApplicationContext

结果失败了!

 

通过Debug,我发现是Spring自己的一些特殊处理方式使它在OSGI环境下失败。

 

然后,我研究了SpringDM1.1API,找到了在插件中直接创建ApplicationContext的方法。

 

 

需要的环境

Springjar包,还有SpringDM1.1core和extenderjar包。以及相应的依赖jar包。

这些Jar包,可以放在插件中。

也可以发布为一个OSGI插件,让所有需要创建ApplicationContext的插件依赖它。

 

 

源代码

ConfigableClassPathDefaultConfigurationScanner

/**

 *

 */

package net.sf.oxmled.springOsgiNoDM;

 

import org.osgi.framework.Bundle;

import org.springframework.osgi.extender.support.scanning.DefaultConfigurationScanner;

import org.springframework.osgi.io.OsgiBundleResource;

 

/**

 * @author 沈东良 Edward Shen shendl_s@hotmail.com

 *

 */

public class ConfigableClassPathDefaultConfigurationScanner extends

                   DefaultConfigurationScanner {

        

         private String[] configFiles=null;

        

        

        

         @Override

         public String[] getConfigurations(Bundle bundle) {

                  

                   String[] myConfigFiles=new String[configFiles.length];

                  for(int i=0;i<configFiles.length;i++){

                           String configFile=this.getConfigFiles()[i];

                           if(configFile.indexOf(":")==-1){

                                     if(configFile.indexOf("/")!=0){

                                              configFile="/"+configFile;

                                     }

                                     configFile="classpath:"+configFile;

                                     

                           }

                           myConfigFiles[i]=configFile;

                  }              

                  this.setConfigFiles(myConfigFiles);

                                     return myConfigFiles;

        

                  

         }

 

         public String[] getConfigFiles() {

                   return configFiles;

         }

 

         public void setConfigFiles(String[] configFiles) {

                   this.configFiles = configFiles;

         }

 

         /**

          *

          */

         public ConfigableClassPathDefaultConfigurationScanner() {

                   // TODO Auto-generated constructor stub

         }

         public ConfigableClassPathDefaultConfigurationScanner(String[] configFiles) {

                   // TODO Auto-generated constructor stub

                   this.configFiles=configFiles;

         }

 

         /**

          * @param args

          */

         public static void main(String[] args) {

                   // TODO Auto-generated method stub

 

         }

 

}

 

StandAloneOsgiBundleXmlApplicationContext

/**

 *

 */

package net.sf.oxmled.springOsgiNoDM;

 

import org.springframework.context.ApplicationContext;

import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext;

 

/**

 * @author 沈东良 Edward Shen shendl_s@hotmail.com

 *

 */

public class StandAloneOsgiBundleXmlApplicationContext extends

                  OsgiBundleXmlApplicationContext {

         private String[] customConfigLocations=new String[]{};

        

    @Override

         public String[] getDefaultConfigLocations(){

                  

             return this.getCustomConfigLocations();

                  

         }

         /**

          *

          */

         public StandAloneOsgiBundleXmlApplicationContext() {

                   // TODO Auto-generated constructor stub

         }

 

         /**

          * @param parent

          */

         public StandAloneOsgiBundleXmlApplicationContext(ApplicationContext parent) {

                   super(parent);

                   // TODO Auto-generated constructor stub

         }

 

         /**

          * @param configLocations

          */

         public StandAloneOsgiBundleXmlApplicationContext(String[] configLocations) {

                   super(configLocations);

                   this.setCustomConfigLocations(configLocations);

                   // TODO Auto-generated constructor stub

         }

 

         /**

          * @param configLocations

          * @param parent

          */

         public StandAloneOsgiBundleXmlApplicationContext(String[] configLocations,

                            ApplicationContext parent) {

                   super(configLocations, parent);

                   this.setCustomConfigLocations(configLocations);

                   // TODO Auto-generated constructor stub

         }

 

         /**

          * @param args

          */

         public static void main(String[] args) {

                   // TODO Auto-generated method stub

 

         }

         public String[] getCustomConfigLocations() {

                   return customConfigLocations;

         }

         public void setCustomConfigLocations(String[] customConfigLocations) {

                   this.customConfigLocations = customConfigLocations;

         }

 

}

 

 

StandAloneOsgiApplicationContextCreator

 

/**

 *

 */

package net.sf.oxmled.springOsgiNoDM;

 

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.osgi.framework.Bundle;

import org.osgi.framework.BundleContext;

import org.springframework.osgi.context.DelegatedExecutionOsgiBundleApplicationContext;

import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext;

import org.springframework.osgi.extender.OsgiApplicationContextCreator;

import org.springframework.osgi.extender.support.ApplicationContextConfiguration;

import org.springframework.osgi.extender.support.scanning.ConfigurationScanner;

import org.springframework.osgi.util.OsgiStringUtils;

import org.springframework.util.ObjectUtils;

 

 

/**

 * @author 沈东良 shendl_s@hotmail.com

 *

 */

public class StandAloneOsgiApplicationContextCreator implements

                   OsgiApplicationContextCreator {

         /** logger */

         private static final Log log = LogFactory.getLog(StandAloneOsgiApplicationContextCreator.class);

        

         private boolean publishContextAsService=true;

         private ConfigurationScanner configurationScanner ;

         private String[] configFiles=null;

        

         public String[] getConfigFiles() {

                   return configFiles;

         }

         public void setConfigFiles(String[] configFiles) {

                   this.configFiles = configFiles;

         }

         /**

          *

          */

         public StandAloneOsgiApplicationContextCreator() {

                  

         }

public StandAloneOsgiApplicationContextCreator(String[] configFiles) {

                   this.setConfigFiles(configFiles);

         }

 

 

         /* (non-Javadoc)

          * @see org.springframework.osgi.extender.OsgiApplicationContextCreator#createApplicationContext(org.osgi.framework.BundleContext)

          */

         public DelegatedExecutionOsgiBundleApplicationContext createApplicationContext(

                            BundleContext bundleContext) throws Exception {

                   // TODO Auto-generated method stub

                   Bundle bundle = bundleContext.getBundle();

                   this.configurationScanner=new ConfigableClassPathDefaultConfigurationScanner(configFiles);

                   ApplicationContextConfiguration config = new ApplicationContextConfiguration(bundle, configurationScanner);

                   if (log.isTraceEnabled())

                            log.trace("Created configuration " + config + " for bundle "

                                               + OsgiStringUtils.nullSafeNameAndSymName(bundle));

 

                   // it's not a spring bundle, ignore it

                  

 

                   log.info("Discovered configurations " + ObjectUtils.nullSafeToString(config.getConfigurationLocations())

                                     + " in bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "]");

 

      String[] strs=config.getConfigurationLocations();

                   DelegatedExecutionOsgiBundleApplicationContext sdoac = new OsgiBundleXmlApplicationContext(

                                     config.getConfigurationLocations());

                   sdoac.setBundleContext(bundleContext);

                   sdoac.setPublishContextAsService(this.isPublishContextAsService());

                   sdoac.refresh();

                   return sdoac;

         }

 

         /**

          * @param args

          */

         public static void main(String[] args) {

                   // TODO Auto-generated method stub

 

         }

 

        

 

         public boolean isPublishContextAsService() {

                   return publishContextAsService;

         }

 

         public void setPublishContextAsService(boolean publishContextAsService) {

                   this.publishContextAsService = publishContextAsService;

         }

 

}

 

 

OSGI插件中创建ApplicationContext的演示

 

/**

 *

 *@author 沈东良 Edward Shen shendl_s@hotmail.com

 *

 */

public class Activator implements BundleActivator {

    private ClassLoader classLoader = this.getClass().getClassLoader();

    private Log log = LogFactory.getLog(this.getClass());

    private DelegatedExecutionOsgiBundleApplicationContext applicationContext;

 

    public DelegatedExecutionOsgiBundleApplicationContext getApplicationContext() {

       return applicationContext;

    }

 

    /*

     * (non-Javadoc)

     *

     * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)

     */

    public void start(BundleContext context) {

       System.out.println("com.withub.gis.client.service Startup!!");

 

       String[] configFiles = new String[] { "spring/bean.xml",

              "/spring/client.xml", "classpath:spring/osgi.xml" };

 

       StandAloneOsgiApplicationContextCreator standAloneOsgiApplicationContextCreator = new StandAloneOsgiApplicationContextCreator(

              configFiles);

       try {

           this.applicationContext = standAloneOsgiApplicationContextCreator

                  .createApplicationContext(context);

           applicationContext.refresh();

       } catch (Exception e1) {

           // TODO Auto-generated catch block

           e1.printStackTrace();

       }

 

       System.out.println("Spring ApplicationContext  Startup!!");

 

    }

 

    /*

     * (non-Javadoc)

     *

     * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)

     */

    public void stop(BundleContext context) throws Exception {

       System.out.println("com.withub.gis.client.service Shutdown!!");

 

    }

 

    public ClassLoader getClassLoader() {

       return classLoader;

    }

 

    public void setClassLoader(ClassLoader classLoader) {

       this.classLoader = classLoader;

    }

 

}

 

 

解释

1,上面的演示代码中,我是用spring目录下的文件作为Spring的配置文件。

我不建议你在META-INF/spring/目录下编写spring配置文件。否则,如果你的插件的部署环境中有SPringDM存在,那么就会创建2ApplicationContext


2
,现在,你的Spring配置文件中如果使用了Hibernate,也仍然是正确无误的!

 

 

 

目前,使用的spring配置文件不能使用Ant风格的通配符。

如果有哪位朋友对Spring源代码有研究,希望能加上这个功能。我还有项目要完成,目前只能做到这个程度了。

 

 

    我的代码使用了SPringDM提供的API。这是一个简单的办法。因为Spring内部的源代码太多,太复杂了。我现在的这个办法是最简单的。

 

由于我使用了SPringDM的代码,因此,你应该了解SPringDM的一些资源搜索的策略。

 

 

OSGi Search Strategy Prefix Explanation

Class Space classpath: Searches the bundle classloader

(the bundle, all imported packages

and required bundles). Forces the

bundle to be resolved. This method

has similar semantics to

Bundle#getResource(String)

Class Space classpath*: Searches the bundle classloader

(the bundle and all imported

packages and required bundles).

Forces the bundle to be resolved.

This method has similar semantics

to Bundle#getResources(String)

JAR File (or JarSpace) osgibundlejar: Searches only the bundle jar.

Provides low-level access without

Bundles and Application Contexts

Spring Dynamic Modules(1.1.0) 8

OSGi Search Strategy Prefix Explanation

requiring the bundle to be

resolved.

Bundle Space osgibundle: Searches the bundle jar and its

attached fragments (if there are

any). Does not create a class loader

or force the bundle to be resolved.

 

如果提供的SPring资源没有前缀,我就会为你增加classpath:前缀。

经过试验,只有classpath:前缀的资源才可以被正确导入。

 

如,下面的配置

<context:property-placeholder location=" spring/jdbc.properties"/>

在一般的Spring程序中是可用的。(当然,spring目录下有jdbc.properties这个文件)

 

    但是在OSGI环境下,就会报错,告诉我找不到spring/jdbc.properties这个资源。

 

    必须加上classpath:前缀,

<context:property-placeholder location="classpath:/spring/jdbc.properties"/>

    现在就正确了!

 

 

结论

一直都很喜欢Spring,很喜欢Spring团队那帮人。

但是,这次我被SPringDM给搞惨了!

 

SpringDM确实是Spring团队里某个鸟人拍拍屁股想出来的歪点子。

 

现在Spring还在力推SpringDM这个先天畸形的怪胎。哎!!!

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值