OSGi 松耦合最佳实践
我想知道什么是将应用程序代码与框架代码解耦的最佳实践或模式,特别是关于 OSGi。
我将使用 Felix SCR 页面中的示例
示例服务是一个 Comparator
package sample.service;
import java.util.Comparator;
public class SampleComparator implements Comparator
{
public int compare( Object o1, Object o2 )
{
return o1.equals( o2 ) ? 0 : -1;
}
}
上面的代码不包含框架管道,它集中且简洁。使用 OSGi 时,要使其可供应用程序使用,需要将其注册到服务注册表。正如链接的 Felix 页面上所述,一种方法是使用服务组件运行时。
// OSGI-INF/sample.xml
<?xml version="1.0" encoding="UTF-8"?>
<component name="sample.component" immediate="true">
<implementation class="sample.service.SampleComparator" />
<property name="service.description" value="Sample Comparator Service" />
<property name="service.vendor" value="Apache Software Foundation" />
<service>
<provide interface="java.util.Comparator" />
</service>
</component>
一切都
Service-Component: OSGI-INF/sample.xml
很好,我的服务实现与 OSGI 完全没有耦合。
现在我想使用该服务...
package sample.consumer;
import java.util.Comparator;
public class Consumer {
public void doCompare(Object o1, Object o2) {
Comparator c = ...;
}
}
使用 SCR 查找策略,我需要添加仅框架方法:
protected void activate(ComponentContext context) {
Comparator c = ( Comparator ) context.locateService( "sample.component" );
}
使用 SCR 事件策略,我还需要添加仅框架方法:
protected void bindComparator(Comparator c) {
this.c = c;
}
protected void unbindComparator(Comparator c) {
this.c = null;
}
两者都不是非常繁重,尽管我认为您可能会最终会在类中重复相当多的此类代码,这使得过滤时产生更多噪音。
我看到的一种可能的解决方案是使用 OSGi 特定类通过更传统的方式在消费者和框架之间进行调解。
package sample.internal;
public class OsgiDependencyInjector {
private Consumer consumer;
protected void bindComparator(Comparator c) {
this.consumer.setComparator(c);
}
protected void unbindComparator(Comparator c) {
this.consumer.setComparator(null);
}
}
尽管我不确定您如何在 SCR 配置中进行安排。
还有 org.apache.felix.scr.annotations,但这意味着只有在使用 maven-scr-plugin 构建时它才有效。确实没那么糟糕,而且,AFAICT,它们不会对运行时产生影响。
那么,现在您已经阅读了所有内容,您建议使用 OSGi 提供的服务而不用框架代码“污染”应用程序代码的最佳方式是什么?
I'd like to know what is considered the best practices or patterns for decoupling application code from framework code, specifically regarding OSGi.
I'm going to use the example from the Felix SCR pages
The example service is a Comparator
package sample.service;
import java.util.Comparator;
public class SampleComparator implements Comparator
{
public int compare( Object o1, Object o2 )
{
return o1.equals( o2 ) ? 0 : -1;
}
}
The code above contains no framework plumbing, it's focused and concise. Making this available to the application, when using OSGi, involves registering it with a service registry. One way, as described on the Felix pages linked, is by using the Service Component Runtime.
// OSGI-INF/sample.xml
<?xml version="1.0" encoding="UTF-8"?>
<component name="sample.component" immediate="true">
<implementation class="sample.service.SampleComparator" />
<property name="service.description" value="Sample Comparator Service" />
<property name="service.vendor" value="Apache Software Foundation" />
<service>
<provide interface="java.util.Comparator" />
</service>
</component>
and
Service-Component: OSGI-INF/sample.xml
All nice and lovely, my service implementation has no coupling at all to OSGI.
Now I want to use the service...
package sample.consumer;
import java.util.Comparator;
public class Consumer {
public void doCompare(Object o1, Object o2) {
Comparator c = ...;
}
}
Using SCR lookup strategy I need to add framework-only methods:
protected void activate(ComponentContext context) {
Comparator c = ( Comparator ) context.locateService( "sample.component" );
}
Using SCR event strategy I also need to add framework-only methods:
protected void bindComparator(Comparator c) {
this.c = c;
}
protected void unbindComparator(Comparator c) {
this.c = null;
}
Neither are terribly onerous, though I think it's probable you'd end up with a fair amount of this type of code duplicated in classes, which makes it more noise to filter.
One possible solution I can see would be to use an OSGi specific class to mediate between the consumer, via more traditional means, and the framework.
package sample.internal;
public class OsgiDependencyInjector {
private Consumer consumer;
protected void bindComparator(Comparator c) {
this.consumer.setComparator(c);
}
protected void unbindComparator(Comparator c) {
this.consumer.setComparator(null);
}
}
Though I'm not sure how you'd arrange this in the SCR configuration.
There is also org.apache.felix.scr.annotations, though that means it'll all only work if you're building with the maven-scr-plugin. Not so bad really and, AFAICT, they impose no runtime implications.
So, now you've read all that, what do you suggest is the best way of consuming OSGi provided services without 'polluting' application code with framework code?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
1)我不认为绑定方法会污染你的代码,它们只是bean设置器(你也可以将它们称为setXXX,更传统)。您还需要这些来进行单元测试。
2)如果你使用bnd(在maven、ant、bndtools、eclipse插件等中)那么你也可以使用bnd注释。然后 bnd 会自动为你创建(总是可怕的)xml。
在您的清单中,只需添加:
这将由 bnd 拾取。因此您的域代码中没有 OSGi 代码。您可能会感到困惑,没有 unset 方法,但 bnd 的默认值是静态绑定。因此,在激活之前会调用 set 方法,并且在调用 unset 之前会先停用 set 方法。只要您的 Consumer 对象也是 µservice,您就是安全的。看看 bndtools,bnd 主页,以及我的博客,了解有关 µservices 的更多信息。
附言。您的示例是无效代码,因为如果 o1 != o2,o1 将回答大于和小于 o2,这是比较器合约不允许的,并且会使排序不稳定。
1) I do not think the bind methods are polluting your code, they are just bean setters (you can also call them setXXX to be more traditional). You will need those for unit testing as well.
2) If you use bnd (which is in maven, ant, bndtools, eclipse plugin, etc) then you can also use the bnd annotations. bnd will then automatically create the (always horrible) xml for you.
In your manifest, just add:
This will be picked up by bnd. So no OSGi code in your domain code. You might be puzzled there is no unset method but the default for bnd is static binding. So the set method is called before you're activated and you're deactivated before the unset would be called. As long as your Consumer object would be a µservice too, you're safe. Look at bndtools, the bnd home page, and my blogs for more info about µservices.
PS. Your sample is invalid code because o1 will answer the both greater than and lesser than o2 if o1 != o2, this is not allowed by the Comparator contract and will make sorts unstable.
我会写信告诉你我们在我的项目中是如何做到这一点的。作为 OSGi 容器,我们使用 Fuse ESB,尽管可以在 Apache Karaf 中找到。为了不污染我们的代码,我们使用 Spring DM (http://www.springsource.org/osgi),它极大地方便了与 OSGi 容器的交互。它“作为我们持续集成过程的一部分”(最新版本)针对 Equinox 3.2.x、Felix 1.0.3+ 和 Knopflerfish 2.1.x 进行了测试。
这种方法的优点:
它看起来怎么样?
此外,为了创建有效的 OSGi 包,我们使用 maven-bundle-plugin。
I'll write you how we do it in my project. As an OSGi container we are using Fuse ESB, although somewhere inside Apache Karaf can be found. To not pollute our code we use Spring DM (http://www.springsource.org/osgi), which greatly facilitates the interaction with OSGi container. It is tested "against Equinox 3.2.x, Felix 1.0.3+, and Knopflerfish 2.1.x as part of our continuous integration process" (the newest release).
Advantages of this approach:
How it looks?
Moreover, to create valid OSGi bundles we use maven-bundle-plugin.
与 aQute.bnd.annotations.component 中的注释相比,felix 注释的优点似乎是由 felix scr 插件自动创建绑定和取消绑定方法(您可以注释私有字段)。 felix 插件的缺点是它作用于 Java 源代码,因此不适用于用其他语言(例如 scala)创建的类文件。
The advantage of the felix annotations compared to the ones in aQute.bnd.annotations.component seems to be that bind and unbind methods are automatically created by the felix scr plugin (you can annotate a private field). The disadvantage of the felix plugin is that it acts on the Java sources and so doesn't work for class files created in other languages (such as scala).