如何在非服务普通Java类中读取OSGI配置?现有实现是否正确?
问题:非OSGi服务类中获取配置的实现是否正确?
我需要在未注册为服务的普通Java类中获取部分OSGI配置值,因此无法使用@Reference或@Inject注解。我尝试通过Bundle Context获取配置,当前代码可运行,但不确定该实现是否正确。相关代码如下:
获取配置的方法
public void getArticleName() { final BundleContext bundleContext = FrameworkUtil.getBundle(ArticleNameService.class).getBundleContext(); try { String articleName = (String) bundleContext.getService((bundleContext.getServiceReferences(ArticleNameService.class.getName(), " article.name "))[0]); LOG.info("articleName......"+ articleName); } catch (InvalidSyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
服务类代码
@Service(ArticleNameService.class) @Component( metatype = true) @Properties({ @Property( name = "article.name", unbounded = PropertyUnbounded.ARRAY, cardinality = Integer.MAX_VALUE, label = "article addrnameess"), }) public class ArticleNameServiceImpl implements ArticleNameService { private static final String ARTICLE_NAME = "article.name"; private String[] articleName; protected final void activate(final ComponentContext componentContext) { final Dictionary<String, Object> configurationProperties = componentContext.getProperties(); if (configurationProperties != null) { articleName = PropertiesUtil.toStringArray(configurationProperties.get(ARTICLE_NAME)); } } @Override public final String[] getArticeName() { return articleName; } }
请问这种实现方式是否正确?若不正确,正确的实现方案是什么?
回答
咱们先直接说结论:你的实现虽然当前能运行,但存在不少健壮性问题,在OSGi环境下很容易出bug,我先给你拆解问题,再给你两种靠谱的解决方案。
你的代码存在的问题
- 服务过滤语法错误+空指针风险:你在
getServiceReferences里写的" article.name "是无效的过滤表达式——而且你根本不需要用属性过滤,直接获取服务实例就行。另外直接取数组第0个元素[0],如果没有匹配的服务,直接就会抛出NullPointerException。 - 类型转换错误:你的
ArticleNameService返回的是String[],但你把整个服务实例强转成String,这肯定会抛ClassCastException,估计是你代码写错了。 - 服务引用泄漏:OSGi里获取服务后必须调用
ungetService释放,不然会导致服务无法正常销毁,引发内存泄漏。
正确的实现方案
方案一:安全使用Bundle Context获取服务
如果一定要用Bundle Context,得把代码改得更健壮:
import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceReference; import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class YourPlainClass { private static final Logger LOG = LoggerFactory.getLogger(YourPlainClass.class); public void getArticleName() { // 先获取对应的Bundle,避免空指针 Bundle bundle = FrameworkUtil.getBundle(ArticleNameService.class); if (bundle == null) { LOG.warn("ArticleNameService所在的Bundle未加载"); return; } BundleContext bundleContext = bundle.getBundleContext(); if (bundleContext == null) { LOG.warn("Bundle上下文不可用"); return; } ServiceReference<ArticleNameService> serviceRef = null; try { // 获取服务引用,不需要过滤条件 serviceRef = bundleContext.getServiceReference(ArticleNameService.class); if (serviceRef != null) { ArticleNameService service = bundleContext.getService(serviceRef); if (service != null) { String[] articleNames = service.getArticeName(); LOG.info("获取到的articleName: {}", Arrays.toString(articleNames)); } else { LOG.warn("无法获取ArticleNameService实例"); } } else { LOG.warn("当前没有可用的ArticleNameService服务"); } } catch (Exception e) { LOG.error("获取ArticleNameService失败", e); } finally { // 必须释放服务引用,避免泄漏 if (serviceRef != null) { bundleContext.ungetService(serviceRef); } } } }
方案二:用OSGi DS静态引用(更推荐)
如果你用的是Declarative Services 1.3+,可以用@Reference(static = true)来在普通类中绑定服务,不需要把类注册成组件,这是最符合OSGi规范的方式:
- 修改你的普通类,添加静态引用方法:
import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; public class YourPlainJavaClass { private static final Logger LOG = LoggerFactory.getLogger(YourPlainJavaClass.class); // 用volatile保证多线程可见性 private static volatile ArticleNameService articleNameService; // 静态绑定服务 @Reference(static = true) public static void setArticleNameService(ArticleNameService service) { articleNameService = service; } // 解绑服务 public static void unsetArticleNameService(ArticleNameService service) { if (articleNameService == service) { articleNameService = null; } } public void getArticleName() { if (articleNameService != null) { String[] articleNames = articleNameService.getArticeName(); LOG.info("获取到的articleName: {}", Arrays.toString(articleNames)); } else { LOG.warn("ArticleNameService服务未绑定"); } } }
- 确保你的项目构建配置(比如bnd.bnd)里开启DS扫描,添加:
-component: *
这样DS会自动处理这个静态引用的绑定,不需要把这个普通类注册成OSGi组件,完全适配你的场景。
内容的提问来源于stack exchange,提问作者ezhil
相关产品推荐
相关产品推荐

