如何为AbstractAnnotationConfigDispatcherServletInitializer设置一个活动配置文件,以便可以与@PropertySource一起使用?

4
我正在使用AbstractAnnotationConfigDispatcherServletInitializer来配置我的Web应用程序。我还有一个@Configuration类,用于创建一些bean。在这个类中,我使用@PropertySource注释来加载属性文件以获取各种设置(例如数据库连接详细信息)。
目前,我使用Maven配置文件和Ant任务来为运行时环境创建正确的属性文件。也就是说,我让Maven将“prod.properties”或“dev.properties”移动到构建时类所使用的“application.properties”。我想要做的是使用Spring配置文件来消除这个步骤。我希望能够做到以下几点:
@PropertySource( value = "classpath:/application-${spring.profiles.active}.properties")

我也希望能够不使用任何XML来设置配置文件。因此,我需要根据系统属性的存在设置配置文件。例如:
String currentEnvironment = systemProperties.getProperty("current.environment");
if (currentEnvironment == null) {
  ((ConfigurableEnvironment)context.getEnvironment()).setActiveProfiles("production");
} else {
  ((ConfigurableEnvironment)context.getEnvironment()).setActiveProfiles(currentEnvironment);
}

我不确定在哪里可以做到这一点。根据一个相关问题的答案,这可以在我的初始化类中覆盖createRootApplicationContext方法来完成。但是,该答案还依赖于在设置配置文件之前加载配置类。

我想做的是否可能?如果是,怎么做?


@Charles,你为什么删除了[spring-3.2]标签?这很重要。在3.2之前,主要的初始化类是不存在的。 - James Sumners
1
当现有版本标签已经涵盖了相同的领域时,应避免使用新版本标签。 [spring-3]标签wiki摘要表示它适用于Spring的所有3.x版本,包括3.2。新功能不是必须在此处严格要求新标签的东西,因为它们不会破坏足够多的东西。 (还要记住主要版本标签的问题计数--少于600。分裂是不好的。) - Charles
1
请重新阅读您的链接。“在我看来,唯一应该使用c#-4.0标签的问题是那些专门询问4.0中添加的功能细节的问题。”-- 我正在询问一个_特定于3.2_的功能。 - James Sumners
1
createRootApplicationContext 中,配置类被注册,但上下文尚未刷新。 - Sotirios Delimanolis
@SotiriosDelimanolis 谢谢您澄清这一点,但我无法通过覆盖该方法来使其工作。 - James Sumners
2个回答

6

覆盖createRootApplicationContextcreateServletApplicationContext对我没有起作用。我遇到了各种错误,比如非法状态异常和无法解析"${spring.profiles.active}"。通过查看AbstractAnnotationConfigDispatcherServletInitializer的继承树,我想出了以下解决方案:

public class ApplicationInitializer
  extends AbstractAnnotationConfigDispatcherServletInitializer
{
  @Override
  public void onStartup(ServletContext context) throws ServletException {
    super.onStartup(context);

    String activeProfile = System.getProperty("your.profile.property");
    if (activeProfile == null) {
      activeProfile = "prod"; // or whatever you want the default to be
    }

    context.setInitParameter("spring.profiles.active", activeProfile);
  }
}

现在你可以创建一个如下的配置类,它将完美地工作:
@Configuration
@PropertySource( value = "classpath:application-${spring.profiles.active}.properties" )
public class MyAppBeans {
  @Autowired
  private Environment env;

  @Bean
  public Object coolBean() {
    String initParam = this.env.getProperty("cool.bean.initParam");
    ...
    return coolBean;
  }
}

当然,您可以通过VM选项(-Dyour.profile.property=dev)或容器属性(例如Tomcat容器属性)来设置“your.profile.property”。请注意,不要删除HTML标签。

我认为它没有起作用的原因是,尽管上下文没有刷新,但您的@Configuration类的元数据已加载(我需要进一步研究此问题)。如果未声明@PropertySource的占位符,则无法解析它,而这是在类注册之后才发生的。 - Sotirios Delimanolis
在采用这种解决方案之前,我重新实现了默认方法并添加了我的配置检测,设置了活动配置文件,注册了我的配置类,并刷新了上下文。但Spring并没有接受它。 - James Sumners

0

不要使用

@PropertySource( value = "classpath:application-${spring.profiles.active}.properties" )

你也可以这样做

@PropertySource( value = "classpath:application.properties" )

并使用一些Maven插件,例如properties-maven-plugin (*)

   <build>
      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>properties-maven-plugin</artifactId>
         <version>1.0-alpha-2</version>
         <executions>
            <execution>
               <phase>generate-resources</phase>
               <goals>
                  <goal>write-active-profile-properties</goal>
               </goals>
               <configuration>
                  <outputFile>src/main/resources/application.properties</outputFile>
               </configuration>
            </execution>
         </executions>
      </plugin>
   </build>
   <profiles>
      <profile>
         <id>production</id>
         <properties>
            <profiles>prod</profiles>
            <propertyOne>...</propertyOne>
            <propertyTwo>...</propertyTwo>
         </properties>
      </profile>
      <profile>
         <id>development</id>
         <properties>
            <profiles>dev</profiles>
            <propertyOne>...</propertyOne>
         </properties>
      </profile>
   </profiles>

然后运行

mvn <lifecycle> -P production

有一些理由支持将活动配置文件传递给系统属性而不是Maven参数吗?

使用此配置,此解决方案 对我有效:

@Configuration
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
    {
    protected WebApplicationContext createRootApplicationContext() {
        WebApplicationContext context = super.createRootApplicationContext();
        ((ConfigurableEnvironment) context.getEnvironment()).setActiveProfiles(profiles());
        return context;
    }
        public String[] profiles() {
            InputStream input = getClass().getClassLoader()
                    .getResourceAsStream("application.properties");
            Properties properties = new Properties();
            try {
                properties.load(input);
            return properties.getProperty("profiles").split(",");;
            } catch (IOException e) {
                e.printStackTrace();
                String[] defaultProfiles = {"dev"};
                return defaultProfiles;
                // I really think that here we shouldn't return a default profile
            }

        }
}

(*) 这是一个旧的插件(发布日期为2009年),所以也许我们应该找另一个能完成相同工作的插件,但是这个想法是为了编写属性和Maven配置文件而设计的插件。

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