看來我在做一件非常相似的事情。雖然最終組件框架(如OSGi和NetBeans平臺(也可以用於服務器端))是我已經使用並用於其他項目的可行解決方案,但它們在使用更多功能時付出了複雜性他們提供,除了搜索註冊組件(例如強制執行依賴性檢查,版本檢查,模塊隔離等)。
但對於掃描捆綁類有一個簡單的解決方案,基於註釋掃描。在我與Vaadin的項目中,我創建了一個引用「抽象」組件名稱的用戶界面,這些用戶界面必須與用戶可能提供的實際Java類匹配。
實現組件的Java類標有一個特製的註解:例如
@ViewMetadata(typeUri="component/HtmlTextWithTitle", controlledBy=DefaultHtmlTextWithTitleViewController.class)
public class VaadinHtmlTextWithTitleView extends Label implements HtmlTextWithTitleView
然後我在classpath中搜索註釋類與ClassScanner:
final ClassScanner classScanner = new ClassScanner();
classScanner.addIncludeFilter(new AnnotationTypeFilter(ViewMetadata.class));
for (final Class<?> viewClass : classScanner.findClasses())
{
final ViewMetadata viewMetadata = viewClass.getAnnotation(ViewMetadata.class);
final String typeUri = viewMetadata.typeUri();
// etc...
}
這是我對ClassScanner全面執行,在上面實施春:
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;
public class ClassScanner
{
private final String basePackage = "it"; // FIXME
private final ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
@Nonnull
public final Collection<Class<?>> findClasses()
{
final List<Class<?>> classes = new ArrayList<Class<?>>();
for (final BeanDefinition candidate : scanner.findCandidateComponents(basePackage))
{
classes.add(ClassUtils.resolveClassName(candidate.getBeanClassName(), ClassUtils.getDefaultClassLoader()));
}
return classes;
}
public void addIncludeFilter (final @Nonnull TypeFilter filter)
{
scanner.addIncludeFilter(filter);
}
}
這是非常簡單,但有效。請注意,由於Java ClassLoaders的工作原理,您必須指定至少一個包來搜索。在我的示例中,我硬連接頂層包「it」(我的東西是「it.tidalwave。*」),很容易將這些信息放入可配置的屬性中,最終指定多個包。
另一種解決方案可以通過只是使用從NetBeans平臺兩個庫使用。我強調這個概念,它不會將整個平臺導入到您的項目中,包括類加載器設施等,但僅使用兩個jar文件。因此它不是侵入性的。這些庫是org-openide-util.jar和org-openide-util-lookup.jar(我再次強調,您可以使用簡單的.jar文件,而不是特定於NetBeans平臺的.nbm文件)。基本上,你會使用@ServiceProvider annotation。它在編譯過程中被觸發(使用Java 6)並生成一個META-INF/services/description文件,該文件將放置在類路徑中。這個文件是Java的一個標準功能(我相信1.3),可以用標準類ServiceLoader查詢。在這種情況下,僅在編譯期間使用NetBeans平臺庫,因爲它們僅用於生成META-INF /服務。最終,通過Lookup class,這些庫也可用於更好地查詢註冊服務。
這兩種解決方案之間存在設計差異。用我的自定義註釋,我發現類:然後我用它們與反射來實例化對象。通過@ServiceProvider,系統會自動從類中實例化一個'singleton'對象。因此,在前一種情況下,我爲我想創建的對象註冊類,在第二種情況下,我註冊了一個工廠來創建它們。在這種情況下,前面的解決方案似乎需要少一段時間,這就是爲什麼我使用它(通常,我使用@ServiceProvider很多)。
綜上,三個解決方案已經列舉:
- 使用我提供ClassScanner春暖花開。在運行時需要Spring。
- 在代碼中使用@ServiceProvider並使用ServiceLoader進行掃描。在編譯時需要兩個NetBeans平臺庫,而運行時需要Java運行時。
- 在代碼中使用@ServiceProvider並使用Lookup進行掃描。運行時需要兩個NetBeans Platform庫。
您也可以查看this question的答案。
我嘗試了其中幾種方法,在我的確切場景中,他們以某種方式失敗。我實際上能夠完成我最終需要使用OSGI。 – broschb