Java Web应用集成OSGI

对OSGI的简单理解

就像Java Web应用程序需要运行在Tomcat、Weblogic这样的容器中一样。程序员开发的OSGI程序包也需要运行在OSGI容器中。目前主流的OSGI容器包括:Apache Felix以及Eclipse Equinox。OSGI程序包在OSGI中称作Bundle。
Bundle的整个生命周期都交与OSGI容器进行管理。可以在不停止服务的情况下,对Bundle进行加载和卸载,实现热部署。
Bundle对于外部程序来说就是一个黑盒。他只是向OSGI容器中注册了供外部调用的服务接口,至于实现则对外部不可见。不同的Bundle之间的调用,也需要通过OSGI容器来实现。

Bundle如何引入jar

刚才说到Bundle是一个黑盒,他所有实现都包装到了自己这个“盒子”中。在开发Bundle时,避免不了引用一些比如Spring、Apache commons等开源包。在为Bundle打包时,可以将当前Bundle依赖jar与Bundle的源码都打包成一个包(all-in-one)。这种打包结果就是打出的包过大,经常要几兆或者十几兆,这样当然我们是不可接受的。下面就介绍一种更优的做法。

Bundle与OSGI容器的契约

Bundle可以在MANIFEST.MF配置文件中声明他要想运行起来所要的包以及这些包的版本 !!!而OSGI容器在加载Bundle时会为Bundle提供Bundle所需要的包 !!!在启动OSGI容器时,需要在OSGI配置文件中定义org.osgi.framework.system.packages.extra,属性。这个属性定义了 OSGI容器能提供的包以及包的版本。OSGI在加载Bundle时,会将他自己能提供的包以及版本与Bundle所需要的包以及版本列表进行匹配。如果匹配不成功则直接抛出异常:

Unable to execute command on bundle 248: Unresolved constraint in bundle com.osgi.demo2 [248]: Unable to resolve 248.0: missing requirement [248.0] osgi .wiring.package; (&(osgi.wiring.package=org.osgi.framework)(version>=1.8.0)(!(version>=2.0.0)))

也可能加载Bundle通过,但是运行Bundle时报ClassNotFoundException。这些异常都由于配置文件没配置造成的。理解了配置文件的配置方法,就能解决60%的异常。

Import-Package

在Bundle的Import-Package属性中通过以下格式配置:

<!--pom.xml--> <Import-Package> javax.servlet, javax.servlet.http, org.xml.sax.*, org.springframework.beans.factory.xml;org.springframework.beans.factory.config;version=4.1.1.RELEASE, org.springframework.util.*;version="[2.5,5.0]" </Import-Package>

包与包之间通过逗号分隔

可以使用*这类的通配符,表示这个包下的所有包。如果不想使用通配符,则同一个包下的其他包彼此之间可以使用;分隔。

如果需要指定包的版本则在包后面增加;version="[最低版本,最高版本]"。其中[表示大于等于、]表示小于等于、)表示小于。

org.osgi.framework.system.packages.extra

语法与Impirt-Package基本一致,只是org.osgi.framework.system.packages.extra不支持通配符。

错误的方式

org.springframework.beans.factory.*;version=4.1.1.RELEASE

正确的方式:

org.springframework.beans.factory.xml;org.springframework.beans.factory.config;version=4.1.1.RELEASE,

Class文件加载

在我们平时开发中有些情况下加载一个Class会使用this.getClassLoader().loadClass。但是通过这种方法加载Bundle中所书写的类的class会失败,会报ClassNotFoundException。在Bundle需要使用下面的方式来替换classLoader.loadClass方法

public void start(BundleContext context) throws Exception { Class classType = context.loadClass(name); }

Bundle中加载Spring配置文件时的问题

由于Bundle加载Class的特性,会导致在加载Spring配置文件时报错。所以需要将Spring启动所需要的ClassLoader进行更改,使其调用BundleContext.loadClass来加载Class。

String xmlPath = ""; ClassLoader classLoader = new ClassLoader(ClassUtils.getDefaultClassLoader()) { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { return currentBundle.loadClass(name); } catch (ClassNotFoundException e) { return super.loadClass(name); } } }; DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.setBeanClassLoader(classLoader); GenericApplicationContext ctx = new GenericApplicationContext(beanFactory); ctx.setClassLoader(classLoader); DefaultResourceLoader resourceLoader = new DefaultResourceLoader(classLoader) { @Override public void setClassLoader(ClassLoader classLoader) { if (this.getClassLoader() == null) { super.setClassLoader(classLoader); } } }; ctx.setResourceLoader(resourceLoader); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ctx); reader.loadBeanDefinitions(xmlPath); ctx.refresh();

Web应用集成OSGI

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/91563148a67240b128c55c7eca1eb6d6.html