Как контролировать ошибки инициализации контекста Spring

Допустим, у нас есть Spring bean:

@Component
class PluginsProviderImpl implements PluginsProvider {
    private final List<PluginInterface> plugins;
    public PluginsProviderImpl(List<PluginInterface> plugins){
        this.plugins = plugins;
    }
  //...
}

Реализации PluginInterface иметь зависимость во время выполнения от основной системы и предоставляются извне. Возможно, что иногда некоторые из них ошибочны (например, их зависимости отсутствуют). Если при инициализации контекста Spring возникает такая ошибка - все приложение не запускается (даже если сломанный плагин не требуется для его правильной работы).

Можно ли контролировать загрузку контекста Spring таким образом, чтобы при возникновении ошибки в одном из PluginInterface реализации, пропустить это и продолжить инициализацию?

ОБНОВЛЕНИЕ: Больше объяснения: мне не нужно добавлять бин условно. Я хочу пропустить ошибочный компонент, и проблема возникает во время инициализации контекста. Это плагин - предоставляется во время выполнения.

Еще одно объяснение: я хочу, чтобы приложение запускалось, даже если одна из представленных плагином реализаций PluginInterface не может быть инициализирована.

0 ответов

Я наконец нашел решение. Это не идеально, но работает в большинстве случаев.
Сначала я понял, что в моем случае ошибочный плагин означает плагин, у которого есть проблема со связыванием, например, кто-то предоставляет плагин без зависимостей времени выполнения или существует проблема с версией плагина в зависимости от версии приложения.
Во-вторых, я обнаружил ловушку в инициализации контекста 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, он также позволяет удалить определения bean-компонентов из фабрики bean-компонентов. В общем, это может быть небезопасная операция (в конце концов, удаленный бин может понадобиться другому бину), но я использую его только для определения экземпляров плагинов, которые по умолчанию независимы и самодостаточны. Хорошо, достаточно поговорить о коде, давайте посмотрим код...;)

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 указывают, что класс имеет некоторую зависимость от другого класса; однако последний класс несовместимо изменился после компиляции первого класса

,

Как я выяснил, это также указывает на отсутствие зависимости. В начале я написал, что это решение не идеально. Код проверяет, имеет ли плагин беспараметрический конструктор для его создания. Если у плагина его нет - проверить невозможно. Поэтому мне нужно объявить дополнительные требования к плагинам - они должны иметь конструктор без параметров:).

Другие вопросы по тегам