简体   繁体   中英

What is the best practice for handling multiple profiles in Spring with java config?

On a project I'm currently working on we have the need for multiple profiles, ie "default" and "test". To solve this, we've implemented a main context class, ApplicationContext.java, with 2 public static inner classes: one of them defines the default profile, the other defines the test profile. Our web.xml is set to target ApplicationContext.java.

Code as follows:

@Configuration
//import common beans
public class ApplicationContext {

  @Configuration  
  @Profile("default")
  public static class DefaultContext {
    //default beans
  }  

  @Configuration
  @Profile("test")
  public static class TestContext {
    //test beans
  }

}

My problem with this is that the main context class, ApplicationContext.java, is in the production environment (ie src/main/java) with references to files in the test environment. If there is a better way to define these profiles without introducing this dependency from production code to test code, that would of course be preferable.

We`ve tested these cases with a jetty instance in a test class, started from a main method. This instance is run with the following command:

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

If all the beans are common between your profiles (that is, both DefaultContext and TestContext contains the same bean definitions), define an interface for the dependencies, eg:

public interface SystemConfiguration {

    public DataSource getDataSource();
    public SomeService getService();

}

Then implement each profile with this interface:

@Profile("production")
@Configuration
public class ProductionConfiguration implements SystemConfiguration {
    public DataSource getDataSource() {
         // create and return production datasource
    }

    public SomeService getService() {
        // Create and return production service
    }
}

And then do the same for test.

@Profile("test")
@Configuration
public class TestConfiguration implements SystemConfiguration {
    public DataSource getDataSource() {
         // create and return dummy datasource
    }

    public SomeService getService() {
        // Create and return dummy service
    }
}

Then you can inject this into your main configuration:

@Configuration
public class ApplicationContext {
    @Autowired
    private SystemConfiguration systemConfiguration;

}

The solution we ended up with utilizes Spring`s @ComponentScan annotation. The various application contexts are defined in multiple maven modules. However, by sharing the same package naming (ie com.company.application.context) this annotation finds the contexts across both test and production directories.

The resulting code:

@ComponentScan("com.company.application.context")
@Configuration
public class ApplicationContext { }

All production contexts and test contexts are found automatically, assuming maven dependencies and package naming is correct. The production context looks like this:

@Configuration
@Profile("default")
//Import contexts from other modules
public class ProductionContext { }

Likewise for test context. Running Jetty from a main method with the following line correctly loads the test context and ignores the "default" beans:

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

This solution avoids any direct references from production to test code, although maven dependencies are necessary.

Use the features of Maven to seperate main and test application contexts.

For example if your main application context lives in

src/main/webapp/WEB-INF/myapp-config.xml

you can put a test application context in

src/test/webapp/WEB-INF/myapp-config.xml

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM