Setting an active profile for AbstractAnnotationConfigDispatcherServletInitializer that can be use with @PropertySource?

时光毁灭记忆、已成空白 提交于 2020-01-01 12:38:30

问题


I'm using the AbstractAnnotationConfigDispatcherServletInitializer to configure my web application. I also have an @Configuration class I use for creating a few beans. In this class, I use the @PropertySource annotation to load a properties file for various settings (e.g. database connection details).

Currently, I use Maven profiles with Ant tasks to create the correct properties file(s) for my runtime environment. That is, I get Maven to move a "prod.properties" or "dev.properties" to "application.properties" (which the class uses) at build time. What I would like to do is use Spring profiles to eliminate this. I would like to be able to do the following:

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

I also want to set the profile without using any XML. So I would need to set the profile based on the presence of a system property. For example,

String currentEnvironment = systemProperties.getProperty("current.environment");
if (currentEnvironment == null) {
  ((ConfigurableEnvironment)context.getEnvironment()).setActiveProfiles("production");
} else {
  ((ConfigurableEnvironment)context.getEnvironment()).setActiveProfiles(currentEnvironment);
}

I am not sure where I could do this, though. According to an answer to a related question, this could be done in an override of the createRootApplicationContext method in my initializer class. But, that answer also relies on the configuration classes being loaded before setting the profile.

Is what I want to do possible? If so, how?


回答1:


Overriding createRootApplicationContext or createServletApplicationContext was not working for me. I was getting various errors like illegal state exceptions and "${spring.profiles.active}" not being resolvable. Digging through the inheritance tree for AbstractAnnotationConfigDispatcherServletInitializer I devised the following solution:

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);
  }
}

Now you can create a configuration class like the following and it will work just fine:

@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;
  }
}

Of course, you would set the "your.profile.property" via VM options (-Dyour.profile.property=dev) or container properties (e.g. Tomcat container properties).




回答2:


Instead of

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

You also could do

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

And use some maven plugin like 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>

And then run

mvn <lifecycle> -P production

There is some reason in favor of passing the active profile in a system property instead of a maven parameter?

With this configuration this solution worked for me:

@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
            }

        }
}

(*) This is an old plugin (release date 2009) so maybe we should find another that do the same work, but the idea is plugin for write properties + maven profiles.



来源:https://stackoverflow.com/questions/20617827/setting-an-active-profile-for-abstractannotationconfigdispatcherservletinitializ

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!