Кажется, я делаю очень похожую вещь. Хотя в конечном итоге фреймворки компонентов, такие как 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/, который будет помещен в путь к классам. Этот файл является стандартной функцией Java (начиная с 1.3, я полагаю), и его можно запрашивать с помощью стандартного класса ServiceLoader. В этом случае вы должны использовать библиотеки платформы NetBeans только во время компиляции, поскольку они используются только для создания META-INF/служб. В конце концов, библиотеки можно было бы использовать и для более эффективных способов запроса зарегистрированных служб с помощью класса Lookup.
Между этими двумя решениями есть разница в дизайне. С помощью моей пользовательской аннотации я обнаруживаю классы: затем я использую их с отражением для создания экземпляров объектов. С помощью @ServiceProvider система автоматически создает объект-одиночку из класса. Таким образом, в первом случае я регистрирую классы для объектов, которые хочу создать, во втором случае я регистрирую фабрику для их создания. В этом случае кажется, что предыдущее решение требует на один проход меньше, и поэтому я использую его (обычно я часто использую @ServiceProvider).
Подводя итоги, были перечислены три решения:
- Используйте мой предоставленный ClassScanner с Spring. Требуется Spring во время выполнения.
- Используйте @ServiceProvider в коде и сканируйте с помощью ServiceLoader. Требуются две библиотеки платформы NetBeans во время компиляции и только среда выполнения Java во время выполнения.
- Используйте @ServiceProvider в коде и сканируйте с помощью Lookup. Требуются две библиотеки платформы NetBeans во время выполнения.
Вы также можете просмотреть ответы на этот вопрос.
person
Fabrizio Giudici
schedule
25.07.2011