基于OSGi的企业级开发框架实践——全局参数工具

在接下去的几篇文章中,我们将通过一些最佳实践来继续讨论我们的开发框架。做为一个应用系统,全局参数配置是一个不可或缺的重要工具。它可以为系统初始化提供必须的参数,也可以为系统提供一些用户自定义的个性化参数,总之它关系到整个应用系统的正确运行。在上一篇文章中,我们已经认识了它,现在就让我们更加深入的了解它的庐山真面目。当你启动了开发框架的运行时环境后,会在Eclipse Console中看到如下图所示的日志信息:


(图一)

在开发框架运行时容器启动的时候会自动的加载指定目录下的system.properties文件,该文件就是默认的全局参数配置文件,比如上例中的D:\home\admin\share\data\helloworld\system.properties。在assembly目录下提供了一个system.properties文件的默认模板,请在第一次启动开发框架时复制该文件到指定的目录,比如上例中的D:\home\admin\share\data\helloworld目录,否则就会看到如上一篇文章中所介绍的异常信息。下图展示了system.properties文件提供的默认配置内容:


(图二)system.properties内容

该文件中定义了一些默认的全局参数,比如:应用程序的工作目录路径,服务器的ID标识,数据库驱动配置等(开发框架支持MySQL的Master/Slave模式的配置,从上图中就可以看到,我们配置了2个数据库的URL参数,后续文章将对数据库相关操作进行详细的讨论)。当然你也可以添加自定义的参数,只要符合Java属性文件的格式即可。接下去让我们来看看全局配置参数加载器是如何工作的。请在Eclipse IDE中打开core-platform Bundle的bundle-context-osgi.xml文件,如下图所示:


(图三)

开发框架提供了一个OSGi的服务定义,org.storevm.eosgi.properties.loader.SystemPropertiesLoader,该服务用于在Bundle启动时加载system.properties属性文件。另外该服务还有一个“location”属性,用于定义system.properties文件的路径(如果路径中不提供文件名,则使用默认的system.properties做为文件名)。现在我们的全局参数已经全部被加载了,接下去我们将在程序中读取这些全局参数。

请打开biz-share Bundle中的bundle-context-osgi.xml配置文件,如下图所示:


(图四)

在该文件中开发框架提供了全局配置参数加载器服务的引入配置,如下代码:

<osgi:reference id="propertiesLoader" interface="org.storevm.eosgi.properties.loader.PropertiesLoader"/>

该配置将core-platform Bundle中发布的OSGi服务引入到biz-share Bundle中,这样在biz-share Bundle中就可以使用这个OSGi服务了,请看bundle-context.xml配置文件中的代码,如下:

<!-- OSGi service annotation processor -->
<bean class="org.storevm.eosgi.core.annotation.OsgiServiceBeanPostProcessor" />
<bean class="org.storevm.eosgi.core.annotation.ServiceReferenceInjectionBeanPostProcessor" />
	
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="propertiesLoader"/>

前2个配置是关于OSGi的annotation配置,下一章我们将对此进行详细讨论,这里我们暂时忽略它。第三个bean配置是我们经常用到的Spring的占位符替换的Bean。它依赖于我们的全局配置参数加载器服务的Bean。只有当PropertiesLoader服务实例化之后PropertyPlaceholderConfigurer才会进行实例化,这样,我们就可以在Spring XML中使用如下的配置了:

<bean id="masterDataSource" class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean">
	<property name="uniqueResourceName" value="${unique.resource.name.one}" />
	<property name="url" value="${jdbc.url.one}" />
	<property name="driverClassName" value="${jdbc.driverClassName}" />
	<property name="user" value="${jdbc.username}" />
	<property name="password" value="${jdbc.password}" />
	<property name="poolSize" value="20" />
	<property name="borrowConnectionTimeout" value="60" />
	<property name="testQuery" value="select 1" />
</bean>

其中用${}表示的占位符就是配置在system.properties文件中的属性值。

这里可能会有人提出疑问,PropertyPlaceholderConfigurer不是也提供了一个locations属性值用于指定属性配置文件的路径吗?是的,但是这里存在一个问题,PropertyPlaceholderConfigurer只能读取本Bundle中的*.properties文件,如果其他Bundle要读取相同的参数值,则需要在其他Bundle中重新再定义一个相同的*.properties文件,这是由OSGi的Classloader隔离机制所导致的,而且属性文件被打包到Bundle中,我们就失去了在运行时修改配置参数的灵活性,所以开发框架提供了一个单独的OSGi服务用于加载全局的配置参数,这样所有的Bundle只要引入该OSGi服务就能读取统一的配置参数了。以下代码展示了如何在代码中使用PropertiesLoader服务:

/**
 * 
 * @author Administrator
 * @version $Id: SystemPropertiesLoaderTest.java, v 0.1 2013-2-26 下午6:33:40 Administrator Exp $
 */
@Component
public class SystemPropertiesLoaderTest {
    private static final Logger LOGGER = Logger.getLogger(SystemPropertiesLoaderTest.class);

    /* 全局参数加载器 */
    private PropertiesLoader    propertiesLoader;

    /**
     * 读取全局配置参数
     */
    @PostConstruct
    public void readProperties() {
        LogUtils.info(LOGGER, "读取全局配置参数, share.data.path={0}",
            propertiesLoader.getProperty("share.data.path"));
    }

    /**
     * Setter method for property <tt>propertiesLoader</tt>.
     * 
     * @param propertiesLoader value to be assigned to property propertiesLoader
     */
    public void setPropertiesLoader(PropertiesLoader propertiesLoader) {
        this.propertiesLoader = propertiesLoader;
    }

}

从代码中我们可以看到,在readProperties方法中我们读取了“share.data.path”配置参数,从图二中我们可以看到share.data.path的参数值。由于我们使用了annotation,所以无需再在spring的配置文件中注册bean。在我们的开发框架中,所有bean的配置均使用annotation完成。好了,我们可以启动Eclipse中的OSGi运行时容器了,点击工具栏上的运行按钮,并选择“helloworld”,如下图所示:

(图五)

Console中会显示大量的启动日志信息,如果运行正常,你将会看到如下图所示的信息,表示我们的测试成功了:

(图六)

从上图可知,程序读取的“share.data.path”配置参数值和我们在system.properties文件中配置的值完全一样。目前我们使用的是开发框架提供的运行时环境来进行代码测试,后续文章我们将介绍开发框架提供的另外一种集成测试方法。

接下来我们将在OSGi容器的运行过程中,修改system.properties中的参数值,然后看看,在biz-share Bundel中是否读取到了修改之后的新值。

请在Console中输入“ss”命令,然后会列出所有的Bundle,如下图所示:

(图七)

我们查找core-platform和biz-share Bundle的id值,如下图所示(可能你的id与图中的不一样,以实际情况为准):

(图八)

从上图可以看到,biz-share Bundle的id是116,而core-platform Bundle的id是118。我们修改system.properties文件中的“share.data.path”配置项,如下图:

(图九)

我们在Console中的“osgi>”提示符下继续输入如下命令:

stop 118

该命令将停止core-platform Bunlde的运行,在Console中会显示如下提示信息(由于篇幅关系,这里的提示信息进行的删减):

osgi> stop 118
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Unpublishing application context OSGi service for bundle Helloworld...
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Closing OsgiBundleXmlApplicationContext(bundle=helloworld-core-plat...
INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Destroying singletons in org.springframework.beans.factory.support...
INFO [org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean] - Unregistered service [ServiceRegistrationWrapper for {org.storevm...
INFO [org.springframework.osgi.extender.internal.activator.ContextLoaderListener] - Application context succesfully closed (OsgiBundleXmlApplica...

接着我们输入:

start 118

该命令将启动core-platform Bundle(stop和start是常用的重启Bundle的命令),在Console中会显示如下提示信息(由于篇幅关系,这里的提示信息进行的删减):

INFO [org.springframework.osgi.extender.support.DefaultOsgiApplicationContextCreator] - Discovered configurations {osgibundle:/META-INF/spring/*.xml}...
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Refreshing OsgiBundleXmlApplicationContext(bundle=helloworld-core-p...
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Application Context service already unpublished

osgi> INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from URL [bundleentry://118.fwk18644877/META-...
INFO [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from URL [bundleentry://118.fwk18644877/META-INF...
INFO [org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor] - No outstanding OSGi service ...
INFO [org.springframework.beans.factory.support.DefaultListableBeanFactory] - Pre-instantiating singletons in org.springframework.beans.factory.support...
INFO [org.storevm.eosgi.properties.loader.PropertiesLoader] - 读取到全局配置文件路径, location=D:\home\admin\share\data\helloworld\system.properties
INFO [org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean] - Publishing service under classes [{org.storevm.eosgi.properties.loa...
INFO [org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext] - Publishing application context as OSGi service with properties {org...
INFO [org.springframework.osgi.extender.internal.activator.ContextLoaderListener] - Application context successfully refreshed (OsgiBundleXmlApplicatio...

从中我们可以看到,system.properties被重新加载了。

接着我们重启biz-share Bundle,和之前重启core-platform Bundle一样的方法,输入如下两个命令:

stop 116
start 116

Console中会显示如下信息:

(图十)

我们在不停止OSGi容器的情况下,修改了全局配置参数,并让其他Bundle可以读取到修改之后的配置参数,这展现了OSGi在动态性方面的特性。在实际运行中,我们只需要重启core-platform Bundle就可以了,这里是为了演示修改前和修改后的参数值差异,所以重启了biz-share Bundle。

下一章中,我们将介绍开发框架提供的一些OSGi Annotations,它们可以帮助我们提高注册OSGi服务和使用OSGi的效率。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值