激活后的 OSGI 声明式服务注入
OSGI Declarative Service Injection After Activation
我们如何确保在 activate()
调用之前注入所有依赖项?
private IMyDependency aInstance, bInstance, cInstance;
public void setDependency(IMyDependency depInstance) {
Bundle depBundle = FrameworkUtil.getBundle(depInstance.getClass());
logger.debug("Dependency {} from bundle {} retrieved", depInstance, depBundle.getSymbolicName());
if (A_BUNDLE_NAME.equals(depBundle.getSymbolicName())) {
aInstance = depInstance;
} else if (B_BUNDLE_NAME.equals(depBundle.getSymbolicName())) {
bInstance = depInstance;
} else if (C_BUNDLE_NAME.equals(depBundle.getSymbolicName())) {
cInstance = depInstance;
} else {
logger.error("Dependency {} from unknown bundle {}", depInstance, depBundle);
}
}
public void activate() {
Preconditions.checkNotNull(aInstance);
Preconditions.checkNotNull(bInstance);
Preconditions.checkNotNull(cInstance);
//...
}
IMyDependency 有多个实例,依赖基数为0..n
。
问题是,setDependency()
有时会在 activate()
方法之后调用。解决方法是更改依赖包的启动级别,但我们真的不想触及配置。
Plain OSGi 在管理依赖性方面非常坚定。你应该明确你的依赖链。您可以通过实施智能服务跟踪器来获得更多动态,该跟踪器会延迟您的初始化,直到您的所有依赖项都可用。但是,我建议您使用声明式服务来避免老式的依赖注入。
通过 Balazs' 注释,我能够将 0..n
依赖项更改为 3 个显式 1..1
依赖项:
<reference bind="setDependencyA" cardinality="1..1" interface="com.example.IMyDependency" name="DepA" policy="static" target="(dependencyType=A)"/>
<reference bind="setDependencyB" cardinality="1..1" interface="com.example.IMyDependency" name="DepB" policy="static" target="(dependencyType=B)"/>
<reference bind="setDependencyC" cardinality="1..1" interface="com.example.IMyDependency" name="DepC" policy="static" target="(dependencyType=C)"/>
依赖 class:
public void setDependencyA(IMyDependency depInstance) {
aInstance = depInstance;
}
public void setDependencyB(IMyDependency depInstance) {
bInstance = depInstance;
}
public void setDependencyC(IMyDependency depInstance) {
cInstance = depInstance;
}
而且我还必须更改依赖项的激活(不幸的是依赖项不使用 DS):
@Override
public void start(BundleContext context) throws Exception {
Hashtable<String, String> props = new Hashtable<>();
props.put("dependencyType", "A");
srvReg = context.registerService(IMyDependency.class.getName(), myInstance, props);
}
我想你想要的是一种插件系统。所以你知道界面但你不知道有多少impls但是你想确保在你加载之前所有的都被加载。
这在 OSGi 中并不容易。事实上,额外的服务可能会在运行时出现。因此,一种解决方案是简单地适应服务的所有变化。在某些情况下,这还不够。
例如,我曾想为我的应用程序提供安全插件。如果我的包在没有该安全插件的情况下启动是不可接受的,但它应该是可配置的。
所以我所做的是使用 OSGi 属性 为每个插件命名,并且在插件机制中我允许列出所有插件的名称以在配置 pid 中加载。该机制确保主应用程序仅在所有命名插件都存在时启动。不幸的是,这种机制并未内置到 DS 中,但您可以使用 bind 和 unbind 方法实现它。
现在对于带有注释的整个组件:
@Component public class MyComponent {
@Reference(target="(dependencyType=A)") IMyDependency aInstance;
@Reference(target="(dependencyType=B)") IMyDependency bInstance;
@Reference(target="(dependencyType=C)") IMyDependency cInstance;
@Activate public void activate() {
// all guaranteed set
}
}
实例组件:
@Component public class MyDependency implements IMyDependency {
...
}
您应该使用 configuration 创建 3 个实例。
我们如何确保在 activate()
调用之前注入所有依赖项?
private IMyDependency aInstance, bInstance, cInstance;
public void setDependency(IMyDependency depInstance) {
Bundle depBundle = FrameworkUtil.getBundle(depInstance.getClass());
logger.debug("Dependency {} from bundle {} retrieved", depInstance, depBundle.getSymbolicName());
if (A_BUNDLE_NAME.equals(depBundle.getSymbolicName())) {
aInstance = depInstance;
} else if (B_BUNDLE_NAME.equals(depBundle.getSymbolicName())) {
bInstance = depInstance;
} else if (C_BUNDLE_NAME.equals(depBundle.getSymbolicName())) {
cInstance = depInstance;
} else {
logger.error("Dependency {} from unknown bundle {}", depInstance, depBundle);
}
}
public void activate() {
Preconditions.checkNotNull(aInstance);
Preconditions.checkNotNull(bInstance);
Preconditions.checkNotNull(cInstance);
//...
}
IMyDependency 有多个实例,依赖基数为0..n
。
问题是,setDependency()
有时会在 activate()
方法之后调用。解决方法是更改依赖包的启动级别,但我们真的不想触及配置。
Plain OSGi 在管理依赖性方面非常坚定。你应该明确你的依赖链。您可以通过实施智能服务跟踪器来获得更多动态,该跟踪器会延迟您的初始化,直到您的所有依赖项都可用。但是,我建议您使用声明式服务来避免老式的依赖注入。
通过 Balazs' 注释,我能够将 0..n
依赖项更改为 3 个显式 1..1
依赖项:
<reference bind="setDependencyA" cardinality="1..1" interface="com.example.IMyDependency" name="DepA" policy="static" target="(dependencyType=A)"/>
<reference bind="setDependencyB" cardinality="1..1" interface="com.example.IMyDependency" name="DepB" policy="static" target="(dependencyType=B)"/>
<reference bind="setDependencyC" cardinality="1..1" interface="com.example.IMyDependency" name="DepC" policy="static" target="(dependencyType=C)"/>
依赖 class:
public void setDependencyA(IMyDependency depInstance) {
aInstance = depInstance;
}
public void setDependencyB(IMyDependency depInstance) {
bInstance = depInstance;
}
public void setDependencyC(IMyDependency depInstance) {
cInstance = depInstance;
}
而且我还必须更改依赖项的激活(不幸的是依赖项不使用 DS):
@Override
public void start(BundleContext context) throws Exception {
Hashtable<String, String> props = new Hashtable<>();
props.put("dependencyType", "A");
srvReg = context.registerService(IMyDependency.class.getName(), myInstance, props);
}
我想你想要的是一种插件系统。所以你知道界面但你不知道有多少impls但是你想确保在你加载之前所有的都被加载。
这在 OSGi 中并不容易。事实上,额外的服务可能会在运行时出现。因此,一种解决方案是简单地适应服务的所有变化。在某些情况下,这还不够。
例如,我曾想为我的应用程序提供安全插件。如果我的包在没有该安全插件的情况下启动是不可接受的,但它应该是可配置的。
所以我所做的是使用 OSGi 属性 为每个插件命名,并且在插件机制中我允许列出所有插件的名称以在配置 pid 中加载。该机制确保主应用程序仅在所有命名插件都存在时启动。不幸的是,这种机制并未内置到 DS 中,但您可以使用 bind 和 unbind 方法实现它。
现在对于带有注释的整个组件:
@Component public class MyComponent {
@Reference(target="(dependencyType=A)") IMyDependency aInstance;
@Reference(target="(dependencyType=B)") IMyDependency bInstance;
@Reference(target="(dependencyType=C)") IMyDependency cInstance;
@Activate public void activate() {
// all guaranteed set
}
}
实例组件:
@Component public class MyDependency implements IMyDependency {
...
}
您应该使用 configuration 创建 3 个实例。