【问题标题】:How to control Spring context initialization errors如何控制 Spring 上下文初始化错误
【发布时间】:2019-05-26 11:05:36
【问题描述】:

假设我们有一个 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,并且问题出现在上下文初始化期间。这是一个插件 - 在运行时提供。

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

【问题讨论】:

  • @RolandWeisleder I 不需要有条件地添加 bean。我想跳过一个错误的 bean,它在上下文初始化期间是已知的。这是插件 - 在运行时提供。
  • '他们的依赖项丢失'这是唯一的问题吗?如果不能,你能解释更多你想要达到的目标吗?
  • @slimane 一般来说,即使插件引入的 PluginInterface 实现之一无法初始化,我也想启动应用程序。
  • @piradian 那些 PluginInterface 是如何传递给应用程序的?它们是弹簧管理的 Beans 吗?

标签: java spring spring-boot plugin-architecture


【解决方案1】:

我终于找到了解决办法。它并不完美,但在大多数情况下都有效。
起初我意识到,在我的情况下,错误插件是指有 linkage 问题的插件,例如有人提供了没有运行时依赖的插件,或者插件版本与应用程序版本存在问题。
其次,我在 Spring 上下文初始化(准确地说是 bean 工厂)中发现了一个钩子,它允许在以下情况下注入代码: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 工厂 removing 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 的子类表明一个类对另一个类有某种依赖;但是,后一个类在前一个类编译后发生了不兼容的变化

.

我发现,这也表明缺乏依赖。 一开始我写道,这个解决方案并不完美。代码检查插件是否有无参数的构造函数来实例化它。如果插件没有 - 无法检查。所以我需要对插件添加额外的要求——它们必须有无参数的构造函数:)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-21
    • 2018-09-08
    • 2014-06-25
    • 1970-01-01
    • 2017-03-17
    • 2014-09-13
    • 2016-06-09
    • 2015-04-26
    相关资源
    最近更新 更多