Usecases for Spring Profiles
We know that Spring profile is indeed an effective feature. I am summarizing the cases where I found them indispensible
Bypass web filter for local instances
There are few filters which apply only for production environment — Authentication filter, Monitoring filter that sends metrics to some monitoring system, etc
And I should be able to skip these for local/test environments.
-Dspring.profiles.active=testcase
So here we leave the decision of whether to bypass a filter or not to Spring, i.e. DelegatingFilterProxy
targetFilterLifecycle is the one which will delegate the init paramaters to the bean as well. Also, the bean name should match the filter-name (if you do not specify targetBeanName)
Coming to the part where we actually use the profile
const val AUTHENTICATION_FILTER_NAME = "AuthenticationFilter"const val TEST_PROFILE = "testcase"
const val NOT_TEST_PROFILE = "!" + TEST_PROFILE@Configuration
class WebConfig {
@Profile(NOT_TEST_PROFILE)
@Bean(name = arrayOf(AUTHENTICATION_FILTER_NAME))
fun getAuthenticationFilter(): AuthenticationFilter = AuthenticationFilter()@Profile(TEST_PROFILE)
@Bean(name = arrayOf(AUTHENTICATION_FILTER_NAME))
fun getByPassAuthenticationFilter(): Filter = ByPassFilter()
}
ByPassFilter would just the pass the request to the next filter in the chain
override fun doFilter(request: ServletRequest?, response: ServletResponse?, chain: FilterChain?) {
chain?.doFilter(request, response)
}
Overriding beans of same name
Refer my previous post to understand the project setup
In short, an application Foo, can be accessed heavy(i.e. you load Foo’s context in your application)/restfully.
So if Bar is a client application for Foo, when the context initializes, it should decide which bean to use for “fooService”. this heavy/light-weight decision can be set via property. And based on this property you can decide whether to override “fooService” with its light version
@Configuration
@Import(FooConfig.class)
public class BarConfig {
@Bean(name="fooService")
@Conditional(IsFooModeLightWeight.class)
public FooService getFooService() {
return com.foo.restful.FooLightWeightService();
}
}
If Bar is using Spring 5 and above, this will work flawlessly. But lets say Bar is using Spring 4 where this approach does not work.
You can refer more on this inconsistent behavior of Spring bean overriding across versions here
Guess what comes to rescue here. Profiles again.
@Profile("HEAVY")
@Configuration
@Import(FooConfig.class)
public class FooHeavyConfig {
}@Profile("LIGHTWEIGHT")
@Configuration
public class FooLightConfig{
@Bean(name="fooService")
public FooService getFooService() {
return com.foo.restful.FooLightWeightService();
}
}
And this is also one of the ways you can mention active profiles:
In my case, the mode — heavy/light-weight varies per pod. i.e. in one container I can configure to run in light-weight and in another, to run in heavy mode. Not just this service mode, all other such configuration comes from another a configuration service to Bar Application. So I need to set the active profile AFTER I have read these properties
This is where Spring’s ApplicationContextInitializer comes into play.
public class BarApplicationContextInitializer extends SomePropertySourceContextInitializer {@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
super.initialize(applicationContext);String fooServiceMode = applicationContext.getEnvironment()
.getProperty("fooServiceMode");if (fooServiceMode.equals("light-weight")) {
applicationContext.getEnvironment().addActiveProfile("LIGHTWEIGHT");
}
}
}
SomePropertySourceContextInitiliazer reads the properties and add to the application context. Once that is done, I am checking for the mode and setting the active profile accordingly.
For this to work in your web application, you need to add Spring context loader listener in your web.xml and also pass this initializer as context-param
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>
com.bar.BarApplicationContextInitializer
</param-value>
</context-param> <listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>