Java 如何强制执行 spring 配置类的加载顺序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24287024/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 11:18:37  来源:igfitidea点击:

How to enforce loading order of spring configuration classes?

javaspringmavenspring-boot

提问by Markus

I'm working with spring-boot on a multi module project (maven). Each module has it's own @Configuration class. Basically I do have the following layout

我正在一个多模块项目 (maven) 上使用 spring-boot。每个模块都有自己的@Configuration 类。基本上我有以下布局

Module foo-embedded (runs just calls the SpringApplication.run()) method:

模块 foo 嵌入(运行时只调用 SpringApplication.run())方法:

@Configuration
@EnableAutoConfiguration
@ComponentScan("de.foobar.rootpackage")
@Import({ApplicationConfig.class, RepositoryConfig.class, SecurityConfig.class})
public class FooApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(FooApplication.class, args);
    }
}

Module foo-common (contains all beans and spring-data-jpa initialization config)

模块 foo-common(包含所有 bean 和 spring-data-jpa 初始化配置)

@Configuration
@EnableJpaRepositories
@EnableTransactionManagement(entityManagerFactoryRef="entityManagerFactory")
public class RepositoryConfig {

    @Bean(destroyMethod = "shutdown")
    public DataSource getDataSource() {
        // returning a Hikari CP here
    }

    @Bean(name = "entityManagerFactory") // overriding spring boots default
    public EntityManagerFactory getEntityManagerFactory() {
        // returning a new LocalEntityManagerFactoryBean here
    }
}

Module foo-security (containing spring-securiy configuration and related domain classes), which has a maven dependency on foo-common

模块 foo-security(包含 spring-securiy 配置和相关域类),它对 foo-common 有 maven 依赖

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    // configuring HTTP security and defining my UserDetailsService Bean
}

When I start the application using the FooApplication class, everything works as expected. The above mentioned UserDetailsServiceImpl get's autowired with my UserRepository which is being created through the @EnableJpaRepositories annotation.

当我使用 FooApplication 类启动应用程序时,一切都按预期工作。上面提到的 UserDetailsS​​erviceImpl 使用我的 UserRepository 自动装配,它是通过 @EnableJpaRepositories 注释创建的。

Since I want to write some integration tests I've added a test clss to one of my modules.

因为我想编写一些集成测试,所以我在我的一个模块中添加了一个测试类。

Module foo-media (containing some domain related stuff plus test cases for that module)

模块 foo-media(包含一些域相关的东西以及该模块的测试用例)

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {RepositoryConfig.class, SecurityConfig.class})
@WebAppConfiguration
@IntegrationTest
public class DirectoryIntegrationTest {
    // my test code
}

When I run the test it seems that the SecurityConfiguration get's loaded before the RepositoryConfig.class does. Since the security config defined the UserServiceImpl which must be autowired, the test fails to start with a

当我运行测试时,似乎在 RepositoryConfig.class 之前加载了 SecurityConfiguration。由于安全配置定义了必须自动装配的 UserServiceImpl,因此测试无法以

NoSuchBeanDefinitionException telling me: No qualifying bean of type [com.foo.rootpackage.security.repository.UserRepository]

I already tried to add @DependsOn("UserRepository")at the bean definition of UserDetailsService, telling me that spring can't find a bean by that name.

我已经尝试@DependsOn("UserRepository")在 的 bean 定义中添加UserDetailsService,告诉我 spring 找不到该名称的 bean。

Any hints or help would be greatly appreciated! Thanks in advance!

任何提示或帮助将不胜感激!提前致谢!

---- EDIT(since I was asked to provide more code) ----

----编辑(因为我被要求提供更多代码)----

For testing I do not use the actual RepositoryConfig.class, but have a TestRepositoryConfig.class in the common module. Looking like this

对于测试,我不使用实际的 RepositoryConfig.class,而是在公共模块中有一个 TestRepositoryConfig.class。看起来像这样

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", basePackages = "de.foobar.rootpackage")
public class TestRepositoryConfig extends RepositoryConfig {

    @Bean
    @Override
    public DataSource getDataSource() {
        // returning the ds for testing
    }
}

采纳答案by Markus

So I was able to solve this. As it pointed out it had nothing to do with the loading order of the configuration classes (which was my first thought).

所以我能够解决这个问题。正如它指出的那样,它与配置类的加载顺序无关(这是我的第一个想法)。

As you may notice, the only Configuration that had a @ComponentScanannotation was the FooApplication.class Spring was not able to find the Repositories, as it didn't know where to look for. Providing the basePackages attribute like this:

您可能已经注意到,唯一具有@ComponentScan注释的Configuration是 FooApplication.class Spring 无法找到 Repositories,因为它不知道在哪里寻找。提供如下所示的 basePackages 属性:

@EnableJpaRepositories(basePackages = "de.foobar.rootpackage")

at the TestRepositoryConfig.class did the trick here.

在 TestRepositoryConfig.class 在这里做了伎俩。

回答by Jakub Kubrynski

You can use @Orderannotation on your configuration classes to define load ordering. But it's strange because Spring should resolve proper order - so please check if you property inject UserRepositoryin UserDetailsService

您可以@Order在配置类上使用注释来定义加载顺序。但这很奇怪,因为 Spring 应该解决正确的顺序 - 所以请检查您是否注入UserRepository了属性UserDetailsService