We ran into a situation where our application needed to create a bean only if we are running it for a particular vendor and a different bean for other vendors. These beans were for classes which didn't have control and there were no common interface to come up with factory like pattern. Also we could not leave both beans created together. In short we were left with choices to either create two different wars with specific spring context file, or put those two beans into separate context files and include them conditionally in base context file based on some configuration parameter at run time .
It took us a while to figure out but there seems to be multiple ways to achieve this. We are still stuck with spring 3.2 so won't discuss anything available in newer versions like this. Following are options we tried and worked for us, however we chose to go with "Using profiles" approach
It took us a while to figure out but there seems to be multiple ways to achieve this. We are still stuck with spring 3.2 so won't discuss anything available in newer versions like this. Following are options we tried and worked for us, however we chose to go with "Using profiles" approach
- Using profiles
- Using property placeholder
- By extending existing XSDs to have a "if else" kind of tags
Using profiles
Spring (3.2 onward) allows us to specify sub-sections in context file which are activated (or even present in final context files after resolving all imports) only if that profile is active.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd" >
<!-- here goes common beans -->
<beans profile="Prof_1">
<import resource="./first-config.xml" />
</beans>
<beans profile="Prof_2">
<import resource="./second-config.xml" />
</beans>
</beans>
One can activate multiple profile at same time or choose not to activate any. To activate there are multiple ways but to programtaically do this we need to add a initializer in web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.test.MyCustomInitializer</param-value>
</context-param>
MyCustomInitializer looks like following
public class MyCustomInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
try {
String activeProf;
// some logic to either read file/env variable/setting to determine which profile to activate
applicationContext.getEnvironment().setActiveProfiles( activeProf );
} catch (IOException e) {
e.printStackTrace();
}
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd" >
<!-- here goes common beans -->
<beans profile="Prof_1">
<import resource="./first-config.xml" />
</beans>
<beans profile="Prof_2">
<import resource="./second-config.xml" />
</beans>
</beans>
One can activate multiple profile at same time or choose not to activate any. To activate there are multiple ways but to programtaically do this we need to add a initializer in web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.test.MyCustomInitializer</param-value>
</context-param>
public class MyCustomInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
try {
String activeProf;
// some logic to either read file/env variable/setting to determine which profile to activate
applicationContext.getEnvironment().setActiveProfiles( activeProf );
} catch (IOException e) {
e.printStackTrace();
}
}
}
Using property placeholder
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd" >
<!-- here goes common beans -->
<import resource="./${identifier}-config.xml" />
</beans>
PlaceHolderConfigurer bean can't be used in the context itself as imports are resolved even before any bean gets created. So we again need to register a listener as we did in previous method and replace following line in initializer class (assume that we have property file from where we are picking "identifier" as either "first" or "second")
applicationContext.getEnvironment().getPropertySources().addFirst( new ResourcePropertySource( new ClassPathResource( "environment.properties" ) ) );
By extending existing XSDs to have a "if else" kind of tags
This allows us to have following kind of syntax in context
<condbean:cond test="${developmentmode}">
<bean id="industryDAO" class="robertmaldon.app.dao.HibernateIndustryDAO">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
</condbean:cond>
No comments:
Post a Comment