Как контролировать ошибки инициализации контекста Spring - PullRequest
0 голосов
/ 28 декабря 2018

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

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

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

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

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

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

1 Ответ

0 голосов
/ 18 апреля 2019

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

.

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

...