Я наконец нашел решение.Он не идеален, но работает в большинстве случаев.
Сначала я понял, что в моем случае ошибочный плагин означает плагин, который имеет проблему linkage , например, кто-то предоставляет плагин без зависимостей времени выполнения или существуетпроблема с версией плагина в сравнении с версией приложения.
Во-вторых, я обнаружил ловушку в инициализации контекста Spring (точнее, фабрике бинов), которая позволяет внедрять код, когда: All bean definitions will have been loaded, but no beans will have been instantiated yet. This allows for overriding or adding properties even to eager-initializing beans.
- независимо от информации документации Spring, он также позволяет удаление определений бобов с фабрики бобов.В общем, это может быть небезопасная операция (в конце концов, удаленный бин может понадобиться другому бину), но я использую его только для определения экземпляров плагинов, которые по умолчанию независимы и самодостаточны.Хорошо, достаточно поговорить о коде, давайте посмотрим код ...;)
public class PluginQualifierProcessor implements BeanFactoryPostProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(PluginQualifierProcessor.class);
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
String[] beanNamesForType = beanFactory.getBeanNamesForType(PluginInterface.class);
List<String> beans = Arrays.asList(beanNamesForType)
.stream()
.collect(Collectors.toList());
for (String beanName : beans) {
BeanDefinition bean = beanFactory.getBeanDefinition(beanName);
if (!bean.hasConstructorArgumentValues()) {
String className = bean.getBeanClassName();
try {
tryToInstatiate(className);
// we are interested only in runtime linkage errors that can happen if plugin is erroneous
} catch (LinkageError e) {
LOGGER.error("plugin {} is erroneous. It will be discarded from context. {}", className, e);
((BeanDefinitionRegistry) beanFactory).removeBeanDefinition(beanName);
}
}
}
}
private void tryToInstatiate(String className) {
try {
Class<?> beanClass = Class.forName(className);
beanClass.newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
LOGGER.debug("skip exception while creating instance of {}. {}", className, e.getMessage());
}
}
}
Фрагмент ключа:
catch (LinkageError e) {
((BeanDefinitionRegistry) beanFactory).removeBeanDefinition(beanName);
}
Мы ловимLinkageError (не исключение!), Потому что мы ищем неработающие реализации и, как сказано в документации Java
Подклассы LinkageError указывают, что класс имеет некоторую зависимость от другого класса;однако последний класс несовместимо изменился после компиляции первого класса
.
Как я выяснил, это также указывает на отсутствие зависимости.В начале я написал, что это решение не идеально.Код проверяет, имеет ли плагин беспараметрический конструктор для его создания.Если у плагина его нет - проверить невозможно.Поэтому мне нужно объявить дополнительные требования к плагинам - они должны иметь конструктор без параметров :).