在接下去的几篇文章中,我们将通过一些最佳实践来继续讨论我们的开发框架。做为一个应用系统,全局参数配置是一个不可或缺的重要工具。它可以为系统初始化提供必须的参数,也可以为系统提供一些用户自定义的个性化参数,总之它关系到整个应用系统的正确运行。在上一篇文章中,我们已经认识了它,现在就让我们更加深入的了解它的庐山真面目。当你启动了开发框架的运行时环境后,会在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"/>
<!-- 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"/>
<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>
这里可能会有人提出疑问,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; } }
(图五)
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
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
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...
接着我们重启biz-share Bundle,和之前重启core-platform Bundle一样的方法,输入如下两个命令:
stop 116 start 116
(图十)
我们在不停止OSGi容器的情况下,修改了全局配置参数,并让其他Bundle可以读取到修改之后的配置参数,这展现了OSGi在动态性方面的特性。在实际运行中,我们只需要重启core-platform Bundle就可以了,这里是为了演示修改前和修改后的参数值差异,所以重启了biz-share Bundle。
下一章中,我们将介绍开发框架提供的一些OSGi Annotations,它们可以帮助我们提高注册OSGi服务和使用OSGi的效率。