Кажется, я делаю очень похожую вещь. Хотя в конечных компонентных инфраструктурах, таких как OSGi и платформа NetBeans (которые могут использоваться также на стороне сервера), я работаю над решением, которое я использую, и я использую его для других проектов, они платят своей сложностью, когда вы используете больше функций. которые они предлагают, помимо поиска зарегистрированных компонентов (например, принудительной проверки зависимостей, проверки версий, изоляции модулей и т. д.).
Но для сканирования связанных классов есть более простое решение, основанное на сканировании аннотаций. В моем проекте с Vaadin я создаю пользовательский интерфейс, ссылающийся на «абстрактные» имена компонентов, которые должны соответствовать фактическим классам Java, которые могут быть предоставлены пользователем.
Java-классы, реализующие компонент, отмечены пользовательской аннотацией: например,
@ViewMetadata(typeUri="component/HtmlTextWithTitle", controlledBy=DefaultHtmlTextWithTitleViewController.class)
public class VaadinHtmlTextWithTitleView extends Label implements HtmlTextWithTitleView
Затем я ищу аннотированные классы в пути к классам с помощью 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, реализованная поверх Spring:
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 вместо файлов .nbm, характерных для платформы NetBeans).
Обычно вы используете аннотацию @ ServiceProvider . Он запускается во время компиляции (с Java 6) и генерирует файл META-INF / services / description, который будет помещен в путь к классам. Этот файл является стандартной функцией Java (начиная с версии 1.3, я считаю) и может быть запрошен стандартным классом ServiceLoader . В этом случае вы будете использовать библиотеки платформы NetBeans только во время компиляции , потому что они используются только для генерации META-INF / services. В конце концов, библиотеки можно было бы использовать и для улучшения способов запроса зарегистрированных служб с помощью Класса поиска .
.
Между этими двумя решениями есть конструктивная разница. С моей собственной аннотацией я открываю классы: затем я использую их с отражением для создания объектов. С помощью @ServiceProvider система автоматически создает экземпляр «одиночного» объекта из класса. Таким образом, в первом случае я регистрирую классы для объектов, которые я хочу создать, во втором случае я регистрирую фабрику для их создания. В этом случае кажется, что для первого решения требуется на один проход меньше, и поэтому я его использую (обычно я часто использую @ServiceProvider).
Подводя итог, были перечислены три решения:
- Используйте мой прилагаемый ClassScanner с Spring. Требуется весна во время выполнения.
- Используйте @ServiceProvider в коде и сканируйте с помощью ServiceLoader. Требуются две библиотеки платформы NetBeans во время компиляции и только среда выполнения Java во время выполнения.
- Используйте @ServiceProvider в коде и сканируйте с помощью Lookup. Требуются две библиотеки платформы NetBeans во время выполнения.
Вы также можете посмотреть ответы на на этот вопрос .