我想知道什么是将应用程序代码与框架代码分离的最佳实践或模式,特别是有关OSGi的最佳实践或模式。
我将使用example from the Felix SCR pages
该示例服务是一个比较器
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提供的服务的最佳方法是什么?
最佳答案
1)我不认为bind方法会污染您的代码,它们只是bean的setter方法(您也可以将它们称为setXXX来使其更传统)。您还将需要那些用于单元测试。
2)如果您使用bnd(在maven,ant,bndtools,eclipse插件等中),那么您也可以使用bnd批注。然后,bnd会自动为您创建(总是可怕的)xml。
package sample.service;
import java.util.Comparator;
import aQute.bnd.annotations.component.*;
@Component
public class SampleComparator implements Comparator {
public int compare( Object o1, Object o2 ) {
return o1.equals( o2 ) ? 0 : -1;
}
}
@Component
class Consumer {
Comparator comparator;
public void doCompare( Object o1, Object o2 ) {
if ( comparator.compare(o1,o2) )
....
}
@Reference
protected setComparator( Comparator c ) {
comparator = c;
}
}
在 list 中,只需添加:
Service-Component: *
这将由bnd接听。因此,您的域代码中没有OSGi代码。您可能会感到困惑,没有未设置的方法,但是bnd的默认设置是静态绑定(bind)。因此,在激活之前会先调用set方法,然后在调用unset之前将其停用。只要您的Consumer对象也将是µservice,那么您就很安全。查看bndtools,bnd主页和我的blogs以获取有关µservices的更多信息。
PS。您的示例是无效代码,因为如果o1!= o2,o1会同时回答大于和小于o2的问题,因此比较器契约(Contract)不允许这样做,这会使排序变得不稳定。