如何从配置类中实现Spring活动配置文件的实用设置?

4
在我正在进行的项目中,我们有一些旧的依赖项,定义了它们自己的 Spring Beans,但需要从主应用程序进行初始化。所有这些 Bean 都是使用 Spring Profiles 构建的,即对于生产代码是“default”,对于测试代码是“test”。我们希望不再使用 Spring Profiles,而是简单地使用 @import 显式地连接我们的上下文。
这个想法是封装所有这些旧的依赖项,使得其他组件不需要关心 Spring Profiles。因此,从测试的角度来看,应用程序上下文设置可以描述如下:
@ContextConfiguration(classes = {TestContext.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class MyTest { 
  //tests
}

TestContext进一步指向两个类,其中一个封装了旧的依赖项:

@Configuration
@Import(value = {OldComponents.class, NewComponents.class})
public class TestContext {
  //common spring context
}

为了封装旧组件对配置文件的需求,OldComponents.class如下所示:
@Configuration
@Import(value = {OldContext1.class, OldContext2.class})
public class OldComponents {

  static {
    System.setProperty("spring.profiles.active", "test");
  }

}

问题在于静态块似乎没有及时执行。运行mvn clean install时,测试会抛出IllegalStateException,因为无法加载ApplicationContext。我已经验证了静态块的执行,但是似乎OldContext1和OldContext2(这取决于配置文件)已经在此时加载,这意味着为时已晚。
令人沮丧的是,IntelliJ可以正常运行测试,但Maven却不行。有没有办法强制使用这些配置文件而又保持封装性?我尝试创建一个中间上下文类,但它并没有解决问题。
如果我们在测试类上使用@ActiveProfiles注解,则可以正常运行,但这有点违背初衷。当然,我们希望在生产环境中实现相同的效果,这意味着如果无法封装配置文件的需求,则需要在web.xml中进行配置。

是一个Web应用程序吗?如果是的话,你可以在web.xml中设置环境。 - Gerard Ribas
是的,这是一个Web应用程序。我们想要实现的目标不是在web.xml中进行设置,而是将分析封装到特定的上下文类中。 - Jarle Svendsrud
4个回答

0
在类中使用@profile注释来加载以下配置:
@Configuration
@Profile("test")
public class UseTestProfile {
}

并在属性文件或运行时参数中设置spring.profiles.active属性的值。


0

您需要确保环境首先生效。这是我所做的:

@Component
public class ScheduledIni {

    @Autowired
    private Environment env;

    @PostConstruct
    public void inilizetion() {
        String mechineName = env.getProperty("MACHINE_NAME");
        if ("test".equals(mechineName) || "production".equals(mechineName) {
            System.setProperty("spring.profiles.default", "Scheduled");
            System.setProperty("spring.profiles.active", "Scheduled");
        }
    }
}

在调度程序中添加注释ProfileDependsOn以使其正常工作。
@DependsOn("scheduledIni")
@Profile(value = { "Scheduled" })
@Component

0
如果您的配置类继承自AbstractApplicationContext,则可以调用以下方法:
getEnvironment().setActiveProfiles("your_profile");

例如:

public class TestContext extends AnnotationConfigWebApplicationContext {

      public TestContext () {
            getEnvironment().setActiveProfiles("test");
            refresh();
      }

}

希望能有所帮助。

不幸的是,它没有成功。看起来Maven坚持在Java代码执行之前完全加载和评估上下文。 - Jarle Svendsrud

0

看起来在执行OldComponents中的静态块之前,OldContext1OldContext2已经被类加载和初始化了。

虽然我无法解释为什么您的IDE和Maven之间存在差异(这需要对Spring 3.x上下文初始化、Maven Surefire插件、SpringJunit4ClassRunner和内部IntelliJ测试运行器有一些或全部深入了解),但我可以建议尝试这个方法?

@Configuration
@Import(value = {UseTestProfile.class, OldContext1.class, OldContext2.class})
public class OldComponents {
    // moved the System.setProperty call to UseTestProfile.class
}

并且

@Configuration
public class UseTestProfile {
    static {
        System.setProperty("spring.profiles.active", "test");
    }
}

如果我正确理解了你的问题,那么类UseTestProfile应该首先被加载(你可能想探索一种保证这一点的方法?),而导入列表中的另外两个类应该具有它们需要正确初始化的系统设置。
希望这可以帮助到你...

谢谢回复!这是几个小时前同事建议的。不幸的是,它没有起作用。他的推理方式和你的完全一样。 - Jarle Svendsrud
听起来你有一些聪明的同事 ;) 什么没有起作用?导入类的加载顺序不一致吗? - vikingsteve
我只知道它没有起作用。我不确定为什么Maven会选择做它所做的事情,但很明显它在执行任何Java代码之前加载和评估所有内容。在这种情况下,真是遗憾。我必须对项目进行时间限制,所以除非在这里有什么解决方案,否则我很难找到解决办法。 - Jarle Svendsrud
在配置类中使用@Profile,您可以为每个上下文指定配置文件。 - VinuBibin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接