Spring Conditional Bean creation based on another bean - java

I'm trying to find a way to only create a bean if the value of another bean/property is true, using Spring 3.2 and XML configurations.
<bean id="isEnabled" class="java.lang.Boolean">
<bean factory-bean="configurationService" factory-method="getBooleanValue">
<constructor-arg index="0">
<util:constant static-field="org.code.ConfigurationKeys.ENABLED"/>
</constructor-arg>
</bean>
</bean>
<if isEnabled=true>
..... create some beans
</if>
I've seen some slightly similar examples using Spring EL but nothing that does this exactly...

You can use profiles.
<?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();
}
}
}

Why you don't use the factory to create the objects when are required and make them lazy.
<bean id="second "class="xxx.xxx.Class" lazy-init="true" scope="prototype"/>
There is no way to introduce if statement within the spring configuration, profiles could work but at more related to environment not a programmatic configuration.

Related

Is there any way to resolve placeholder in beanName?

<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.wqh">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:custom.properties</value>
</list>
</property>
</bean>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean class="com.wqh.demo.TestBean" name="${custom.beanName}" />
</beans>
When I use configuration like this, Spring will create BeanDefinition with beanName ${custom.beanName}, and will not resolve the placeholder inside it.
But I want to use the beanName which declared in custom.properties file, is there any way to achieve this requirement?
The following configuration would result in NoSuchBeanDefinitionException when attempting to get the bean from context.
<bean class="com.wqh.demo.TestBean" name="${custom.beanName}" />
However , the XML Template Proposals for attribute name shows the following
Attribute : name Can be used to create one or more aliases illegal in
an (XML) id. Multiple aliases can be separated by any number of
spaces, commas, or semi-colons (or indeed any mixture of the three).
Data Type : string
Based on this , the following work around is possible
Considering the properties file entry is :
custom.beanName=propBeanName
Provide the bean configuration with multiple alias names as follows
<bean class="com.wqh.demo.TestBean" name="testBeanName ${custom.beanName}" />
Now when you getBean() based on the name from the application context , it would retrieve the bean successfully
Sample code
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");
System.out.println(Arrays.asList(ctx.getAliases("testBeanName")));
TestBean bean = (TestBean)ctx.getBean("propBeanName");
System.out.println(bean);
}
would display the following in the console
[propBeanName]
com.wqh.demo.TestBean#4c60d6e9

Spring ClassPathXmlApplicationContext creating an empty instance of all beans in spring conf file?

I m playing with Spring.
I created a Spring beans file:
<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.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="hibernate.entities" />
<bean id="sicknessLeprosa" class="hibernate.entities.Sickness" init-method="print" scope="prototype">
<property name="nom" value="Leprosa" />
</bean>
<bean id="sicknessSclerosa" class="hibernate.entities.Sickness" init-method="print" scope="prototype">
<property name="nom" value="Sclerosa" />
</bean>
</beans>
When I call:
ApplicationContext context = new ClassPathXmlApplicationContext("Spring-Module-Annotations.xml");
Map<String, Sickness> SicknessList = context.getBeansOfType(Sickness.class);
System.out.println("Number of ISickness beans: " + SicknessList.size() );
for(Entry<String, Sickness> entry : SicknessList.entrySet() ){
System.out.print(entry.getKey() + ": ");
entry.getValue().print();
}
I get this output:
Number of ISickness beans: 3
sickness: undefined sickness
sicknessLeprosa: Leprosa
sicknessSclerosa: Sclerosa
Where does the first occurence come from?????????????
It looks it comes from :
new ClassPathXmlApplicationContext("Spring-Module-Annotations.xml");
which seems to create an instance of Sickness when invoked.
Thx in advance
As you are using component scanning this will scan your base package recursively and try to find classes annotated with stereotype annotations (such as #Component, #Service, #Repository...). I guess your Sickness class is annotated with one of these. In that case the container will initialize it and that's why you get the first bean. The other two beans are explicitly defined in the configuration class.
BTW, you don't need <context:annotation-driven/> if you are using <context:component-scan />

How to change spring profiles in Junit testing

I have used #Activeprofiles along with importing the spring profile containing .xml to context configuration but it does not seem to load the profile to the respective Junit class. below is the snippet of what I have done. The assert compare value is not been changed as per the profile that is set. Any tips to improve on to get the spring profile activated.
TEST CLASS.
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("unittest-hsql")
#ContextConfiguration(locations = {
"classpath:spring/Services.xml"
"classpath:spring/profiles/dev.xml"
})
public class TestSpringProfile
{
#Test
public void testGetCronExpression()
{
String expression = EventLimitation.getExpression();
assertThat(expression, is("20"));
}
}
Try to specify in the beans tag of your profile-specific configuration, the profile attribute:
<!--language:xml-->
<?xml version="1.0" encoding="UTF-8"?>
<beans profile="dev">
...
</beans>
That should work as long as you have in the file spring/profiles/dev.xml something like the below where you specify a profile named "unittest-hsql":
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<beans profile="unittest-hsql">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/hsqldb.profile.properties</value>
</list>
</property>
</bean>
</beans>
</beans>

How can i add a sub Tag to an Xml file already defined using Java?

I have an XML file that contains the configration of a Spring project and I want to dynamically add a new bean. I have to modify the initial xML file and add my new bean definition:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="config"
class="myclass">
<property name="configXml">
<value>config.xml</value>
</property>
</bean>
<-- here a want to add a new bean definition <bean>....</bean> -->
</beans>
Has anyone got an idea?
I'm still not 100% what you mean, but here's two cases to try.
If you want to specify properties of a bean based on some derived value you can use the Spring Expression Language
http://static.springsource.org/spring/docs/3.0.x/reference/expressions.html
<bean id="someBean" class="example.SomeBean">
<property name="foo" value="#{config.whatever}"/>
</bean>
If you want to something more complex you can construct a bean programatically using a factory bean
<bean id="someBean" class="example.SomeBeanFactory">
<property name="config" ref="config"/>
</bean>
with something like
class SomeBeanFactory implements FactoryBean<SomeBean> {
public void setConfig(MyClass config) { ... }
public SomeBean getObject() {
...
}
}

Exporting Spring #Bean objects using JMX

I'm using Spring's #Configuration, and I noticed that a #Bean does not get registered using JMX.
The bean is wired as
#Bean
protected CountingHttpInterceptor countingHttpInterceptor() {
return new CountingHttpInterceptor();
}
and the class definition is
#ManagedResource
public class CountingHttpInterceptor implements HttpRequestInterceptor, HttpResponseInterceptor { /* code here*/ }
This #Configuration file is processed after the main , XML-based, application context is built, and does not have the chance to take part in the discovery process which is activated using XML bean definitions ( org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource and frieds ).
How can I JMX-enable the beans from the #Configuration file?
Update: the xml configuration
<bean id="jmxExporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="assembler" ref="assembler"/>
<property name="namingStrategy" ref="namingStrategy"/>
<property name="autodetect" value="true"/>
</bean>
<bean id="jmxAttributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
<bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
Despite the temptations of the #Configuration- based approach, some things remain better done with XML config. In particular, the namespace-based config such as <context:mbean-export>. These essentially represent "macros" consisting of complex arrangements of interacting objects.
Now, you could replicate this logic in your #Configuration class, but it's really more trouble than it's worth. Instead, I suggest putting such system-level stuff into XML, and importing it from your #Configuration class:
#ImportResource("/path/to/beans.xml")
public class MyConfig {
#Bean
protected CountingHttpInterceptor countingHttpInterceptor() {
return new CountingHttpInterceptor();
}
}
and then in /path/to/beans.xml:
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
">
<context:mbean-export/>
</beans>
Everything you have is correct. In your #Configuration class you need to add one more annotation to export your MBeans, #EnableMBeanExport.
Your configuration class would look something like this...
#Configuration
#EnableMBeanExport
public class SpringConfiguration {
#Bean
protected CountingHttpInterceptor countingHttpInterceptor() {
return new CountingHttpInterceptor();
}
}

Categories