如何在JBoss Fuse Blueprint中使用多个property-placeholder?
我之前也碰到过这个问题,其实原因很明确:虽然Red Hat文档说允许多个cm:property-placeholder,但默认情况下每个占位符都用相同的${前缀和}后缀,而Aries Blueprint的校验逻辑不允许这种重复的前后缀组合,所以才会抛出Multiple placeholders with the same prefix and suffix are not allowed错误。
先回顾下你的场景:
你最初尝试用两个独立的占位符加载不同配置:
<cm:property-placeholder persistent-id="foo" /> <cm:property-placeholder persistent-id="bar" />
并且参考了Red Hat的文档说明:
在Blueprint中使用多个属性占位符
在Blueprint XML文件中定义多个属性占位符(即引用不同persistent ID的多个cm:property-placeholder元素)是合法的。但需要注意的是,若不同占位符中存在同名属性,会出现冲突,此时遵循以下优先级规则:
- 显式定义的属性设置(例如etc/PersistentID.cfg文件中的配置)优先级高于cm:default-properties元素中定义的默认属性设置。
- 若同一属性存在多个显式设置,Blueprint文件中最后一个属性占位符的设置优先级更高。
- cm:default-properties元素中定义的默认属性设置优先级最低。
但实际运行时出现了错误日志:
2018-05-15 17:43:59,065 | ERROR | /s42http1/deploy | BlueprintContainerImpl | 22 - org.apache.aries.blueprint.core - 1.8.0 | Unable to start blueprint container for bundle myBlueprint/0.0.1.SNAPSHOT org.osgi.service.blueprint.container.ComponentDefinitionException: Multiple placeholders with the same prefix and suffix are not allowed at org.apache.aries.blueprint.ext.PlaceholdersUtils.validatePlaceholder(PlaceholdersUtils.java:49)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.blueprint.compendium.cm.CmNamespaceHandler.parsePropertyPlaceholder(CmNamespaceHandler.java:318)[20:org.apache.aries.blueprint.cm:1.1.0] at org.apache.aries.blueprint.compendium.cm.CmNamespaceHandler.parse(CmNamespaceHandler.java:199)[20:org.apache.aries.blueprint.cm:1.1.0] at org.apache.aries.blueprint.parser.Parser.parseCustomElement(Parser.java:1369)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.blueprint.parser.Parser.loadComponents(Parser.java:427)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.blueprint.parser.Parser.populate(Parser.java:331)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.blueprint.container.BlueprintContainerImpl.doRun(BlueprintContainerImpl.java:350)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.blueprint.container.BlueprintContainerImpl.run(BlueprintContainerImpl.java:277)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.blueprint.container.BlueprintExtender.createContainer(BlueprintExtender.java:300)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.blueprint.container.BlueprintExtender.createContainer(BlueprintExtender.java:269)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.blueprint.container.BlueprintExtender.createContainer(BlueprintExtender.java:265)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.blueprint.container.BlueprintExtender.modifiedBundle(BlueprintExtender.java:255)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$Tracked.customizerModified(BundleHookBundleTracker.java:500)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$Tracked.customizerModified(BundleHookBundleTracker.java:433)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$AbstractTracked.track(BundleHookBundleTracker.java:725)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$Tracked.bundleChanged(BundleHookBundleTracker.java:463)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.aries.util.tracker.hook.BundleHookBundleTracker$BundleEventHook.event(BundleHookBundleTracker.java:422)[22:org.apache.aries.blueprint.core:1.8.0] at org.apache.felix.framework.util.SecureAction.invokeBundleEventHook(SecureAction.java:1127)[org.apache.felix.framework-4.4.1.jar:] at org.apache.felix.framework.util.EventDispatcher.createWhitelistFromHooks(EventDispatcher.java:696)[org.apache.felix.framework-4.4.1.jar:] at org.apache.felix.framework.util.EventDispatcher.fireBundleEvent(EventDispatcher.java:484)[org.apache.felix.framework-4.4.1.jar:] at org.apache.felix.
下面给你两种可行的解决办法:
方法一:为每个占位符设置唯一的前后缀
给每个cm:property-placeholder指定不同的placeholder-prefix和placeholder-suffix,这样就能区分不同配置的属性引用:
<cm:property-placeholder persistent-id="foo" placeholder-prefix="${foo:" placeholder-suffix="}" /> <cm:property-placeholder persistent-id="bar" placeholder-prefix="${bar:" placeholder-suffix="}" />
之后在Blueprint里引用属性时,要对应使用各自的前缀:
<bean id="fooService" class="com.example.FooService"> <property name="url" value="${foo:url}" /> </bean> <bean id="barService" class="com.example.BarService"> <property name="port" value="${bar:port}" /> </bean>
方法二:单个占位符加载多个配置文件(推荐)
这种方式更简洁,也能满足Red Hat文档里的优先级规则。你可以在单个cm:property-placeholder的persistent-id里用逗号分隔多个配置ID,这样会按顺序加载,后面的配置会覆盖前面的同名属性:
<cm:property-placeholder persistent-id="foo,bar" />
这种情况下,你直接用统一的${propertyName}格式引用属性就行,如果foo.cfg和bar.cfg有同名属性,bar.cfg的值会生效(因为它在列表中更靠后),完全符合文档里的优先级规则。
内容的提问来源于stack exchange,提问作者Yannick

