Spring DeferredImportSelector 运行 在任何配置之前

Spring DeferredImportSelector run before any Configurations

我正在尝试创建类似@Enable...自动配置的东西。我想为具有广泛 spring 配置的自定义库创建这样的注释,但需要提供 2 个 beans,并基于这些初始化各种上下文。它确实初始化了数组中返回的所有@configuration classes 中的bean,但我还想根据已经注册的bean 执行一些自定义配置逻辑。现在的javadoc https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ImportSelector.html

表示

ImportSelectors are usually processed in the same way as regular @Import annotations, however, it is also possible to defer selection of imports until all @Configuration classes have been processed (see DeferredImportSelector for details).

所以我求助于 DeferredImportSelector,因为在所有 @Configuration bean 之后据说 Selector 是 运行,所以我可以做条件 Bean。现在 javadoc 在这里很清楚 (https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/DeferredImportSelector.html)

A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional

所以这对我来说是完美的。好吧,直到事实证明无论我做什么,导入选择器 selectImports 方法都是 运行 总是在所有 @Configuration beans 之前。

我发现也许主 @Configuration bean 总是最后一个,javadoc 实际上提到了所有导入的 @Configuration bean。看来也并非如此。我一直在使用调试器检查优先级,但这是我做的测试代码,它非常简单:

除系统输出外什么都不做的导入选择器:

public class TestImportSelector implements DeferredImportSelector{

 @Override
 public String[] selectImports(AnnotationMetadata arg0) {
     System.out.println("ImportSelector");
     return new String[0];
 }
}

配置class(导入以检查提到的任何想法是否有效)

@Configuration
public class ImportedTestContext {
    @Bean
    public String testBeanString2(){
        System.out.println("bean2");
        return "string2";
    }
}

主要上下文class(也尝试交换导入的classes)

@Configuration
@Import({TestImportSelector.class, ImportedTestContext.class})
public class TestMainContext {
    @Bean
    public String testBeanString(){
        System.out.println("bean1");
        return "string";
    }
}

最后是我的主class

public class Test {    
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(TestMainContext.class);
    }    
}

Sooo,作为你运行的主力。你总是得到相同的输出

ImportSelector
bean2
bean1

导入选择器无论如何总是第一个。另外,我试图像 javadoc 所说的那样乱用 Ordered 接口

Implementations can also extend the Ordered interface or use the Order annotation to indicate a precedence against other DeferredImportSelectors

但似乎甚至没有调用 getOrder 方法。好吧,这可能是因为它说它只会再次检查其他 DeferredImportSelectors(并且有 none 但值得尝试)

我已经使用 spring-context 4.3.2.RELEASE 完成了此操作,因为这是我的项目中使用的,但只是为了确保也使用 5.0.5.RELEASE 进行了测试。 完全一样的结果。

所以我相信我不明白关于 ImportSelector、DeferredImportSelector、spring 的某些事情,或者 javadoc 有可能没有说实话或者我误解了它....

如果有任何帮助或建议,我将不胜感激....

只是说清楚:基于那个 DeferredImportSelector,我希望它实现 BeanFactoryAware(这部分有效,Spring 确实注入了 BeanFactory),它会检查已经定义了哪些 bean(就像那些有趣的测试string beans),并基于它告诉 spring 应该加载哪些额外的配置。基于 javadoc,这就是它的用途......

看起来 DeferredImportSelector 的文档有点不清楚。 运行 测试和检查代码后,发现 deferredImport 而不是 ImportSelector.

因此,如果您使用 DeferredImportSelector,您可以 select 配置 class(es) 导入将是 deferred.

A selectImports 方法将正常执行 - 在配置文件期间 resolving/parsing,所以 - 使用 BeanFactory 检查其他 bean 的定义是否已经加载肯定不是一个好主意(有些人可能还没有)。

最好的方法是将此逻辑放入 @Conditional 注释系列(在目标配置 class 内)并确保在所有用户定义的配置之后处理它。