如何控制Spring上下文初始化错误

时间:2018-12-28 16:40:45

标签: java spring spring-boot plugin-architecture

假设我们有一个Spring bean:

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

PluginInterface的实现与核心系统具有运行时相关性,并在外部提供。有时,其中某些错误是可能的(例如,缺少依赖项)。如果在Spring上下文初始化时出现这样的错误-整个应用程序将无法启动(即使损坏的插件也不需要正常运行)。

是否可以通过这样的方式控制Spring上下文加载,如果在PluginInterface实现之一中发生错误,请跳过该错误并继续进行初始化?

更新:更多说明:我不需要有条件地添加bean。我想跳过一个错误的bean,并且在上下文初始化期间出现问题。这是一个插件-在运行时提供。

更多说明: 即使插件引入的插件之一的实现无法初始化,我也要启动应用程序。

1 个答案:

答案 0 :(得分:0)

我终于找到了解决方案。它并不完美,但在大多数情况下都可以使用。
刚开始,我意识到错误的插件意味着存在 linkage 问题的插件,例如有人提供没有运行时相关性的插件,或者插件版本与应用程序版本存在问题。
其次,我在Spring上下文初始化(准确地说是bean factory)中找到了一个钩子,该钩子可以在以下情况下注入代码: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可能需要删除的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的子类表明一个类对另一个类具有一定的依赖性。但是,在编译前一类后,后一类发生了不兼容的变化

正如我发现的,它也表明缺少依赖性。 一开始,我写道该解决方案并不完美。代码检查插件是否具有无参数构造函数以实例化它。如果插件没有一个-无法检查。因此,我需要对插件提出其他要求-它们必须具有无参数的构造函数:)。