我终于找到了解决办法。它并不完美,但在大多数情况下都有效。
起初我意识到,在我的情况下,错误的插件意味着插件有链接问题,例如有人提供的插件没有运行时依赖,或者插件版本与应用程序版本有问题。
其次,我在 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 工厂的 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 的子类表明一个类对另一个类有某种依赖;但是,后一个类在前一个类编译后发生了不兼容的变化
.
正如我发现的那样,它也表明缺乏依赖性。一开始我写道,这个解决方案并不完美。代码检查插件是否有无参数的构造函数来实例化它。如果插件没有 - 无法检查。所以我需要对插件添加额外的要求——它们必须有无参数的构造函数:)。