I have a Spring test-configuration class which is supposed to override an existing in bean in xml-config. But my problem is that the xml bean overrides the bean annotated with primary in my test-config. I have tried naming the test-bean with a different name but that has not worked for me either.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {CamelJmsTest.TestConfig.class})
public class CamelJmsTest {
@Configuration
@ImportResource("classpath:production-beans-camel-jms.xml")
public static class TestConfig {
@Primary
@Bean
public JmsTemplate jmsTemplate() {
return new JmsTemplate(new ActiveMQConnectionFactory("", "", ACTIVE_MQ_HOST));
}
@Primary
@Bean // ideally i just want this bean to override the bean imported from the xml config
public RouteConfigBuilder routeConfig() {
return RouteConfigBuilder.builder().autoStart(true).build();
}
@Primary
@Bean
public RouteBuilder routeBuilder(@Value("${amq.endpoint}") String endpoint,
@Autowired Processor processor) {
return new RouteBuilder(routeConfig(), "", endpoint, processor);
}
}
private static final String ACTIVE_MQ_HOST = "vm://activeMQ";
@BeforeClass
public static void setActiveMQ() {
System.setProperty("amq.endpoint", ACTIVE_MQ_HOST);
}
@Autowired
JmsTemplate jmsTemplate;
@Test
public void postJmsMessage() {
jmsTemplate.send("queue/test", new MessageCreator() {
@Override
public Message createMessage(Session session) throws
JMSException {
return session.createTextMessage("Hello World");
}
});
try {
for (int i = 0; i < 100; i++) {
Thread.sleep(100);
}
} catch (Exception ignored) {
}
}
}
Here is the relevant xml config:
<bean id="routeConfig" class="routing.RouteConfigBuilder" init-method="builder">
<constructor-arg name="redeliveryDelay" value="${<props>.redeliveryDelay}" />
<constructor-arg name="maximumRedeliveries" value="${<props>.maximumRedeliveries}" />
<constructor-arg name="autoStart" value="false" />
</bean>
<bean id="routeBuilder" class="routing.RouteBuilder">
<constructor-arg ref="routeConfig" />
<constructor-arg name="routeId" value="ROUTE_ID_1"/>
<constructor-arg name="endpoint" value="${amq.endpoint}" />
<constructor-arg name="processor" ref="myProcessor" />
</bean>
This is the log output. When running the test:
Overriding bean definition for bean 'routeConfig' with a different
definition:
replacing [Root bean: class [null]; scope=; abstract=false;
lazyInit=false; autowireMode=3; dependencyCheck=0;
autowireCandidate=true; primary=true;
factoryBeanName=CamelJmsTest.TestConfig;
factoryMethodName=routeConfig; initMethodName=null;
destroyMethodName=(inferred);
defined in CamelJmsTest]
with [Generic bean: class [RouteConfigBuilder]; scope=;
abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0;
autowireCandidate=true; primary=false; factoryBeanName=null;
factoryMethodName=null; initMethodName=builder;
destroyMethodName=null;
defined in class path resource [production-beans-camel-jms.xml.xml]]
As you can see my primary bean in the test-configuration is overridden and I want to use my test-config bean in the test.
I am using Spring 4.3.x.
I've read a lot of related posts, but none of the gave me the answer. Any help would be appreciated.
@Bean methods may also be declared within classes that are not annotated with @Configuration. For example, bean methods may be declared in a @Component class or even in a plain old class. In such cases, a @Bean method will get processed in a so-called 'lite' mode.
Bean aliasing allows us to override already configured beans and to substitute them with a different object definition. This is most useful when the bean definitions are inherited from an external resource, which is out of our control. In the following example, I will show you how bean aliasing works.
Bean Overriding Spring beans are identified by their names within an ApplicationContext. Therefore, bean overriding is a default behavior that happens when we define a bean within an ApplicationContext that has the same name as another bean. It works by simply replacing the former bean in case of a name conflict.
Spring @Configuration annotation is part of the spring core framework. Spring Configuration annotation indicates that the class has @Bean definition methods. So Spring container can process the class and generate Spring Beans to be used in the application.
The name change appeared to work my simplified version.
I don't think it matters but the construction style of RouteConfigBuilder and RouteBuilder appears different between the Java and the XML.
Tested with 4.3.20
package com.stackoverflow.q53757986;
import static org.junit.Assert.*;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
@SuppressWarnings("javadoc")
public class CamelJmsTest {
static final String ACTIVE_MQ_HOST = "vm://activeMQ?broker.persistent=false";
/** The Constant SPRING_CLASS_RULE. */
@ClassRule
public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule();
/** The spring method rule. */
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
@BeforeClass
public static void setActiveMQ() {
System.setProperty("amq.endpoint", ACTIVE_MQ_HOST);
}
@Autowired
JmsTemplate jmsTemplate;
@Autowired
RouteConfigBuilder routeConfig;
@Autowired
RouteBuilder routeBuilder;
@Test
public void postJmsMessage() {
assertEquals("java", this.routeConfig.source);
assertEquals("java", this.routeBuilder.source);
this.jmsTemplate.send("queue/test", (MessageCreator) session -> session.createTextMessage("Hello World"));
try {
for (int i = 0; i < 100; i++) {
Thread.sleep(100);
}
} catch (Exception ignored) {}
}
@Configuration
@ImportResource("classpath:production-beans-camel-jms.xml")
static class TestConfig {
@Primary
@Bean
public JmsTemplate jmsTemplate() {
return new JmsTemplate(new ActiveMQConnectionFactory("", "", ACTIVE_MQ_HOST));
}
@Primary
@Bean // ideally i just want this bean to override the bean imported from the xml config
public RouteConfigBuilder routeConfig2() {
return new RouteConfigBuilder("java");
}
@Primary
@Bean
public RouteBuilder routeBuilder2() {
return new RouteBuilder(routeConfig2(), "java");
}
}
}
<?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="routeConfig" class="com.stackoverflow.q53757986.RouteConfigBuilder">
<constructor-arg name="source" value="xml" />
</bean>
<bean id="routeBuilder" class="com.stackoverflow.q53757986.RouteBuilder">
<constructor-arg name="routeConfig" ref="routeConfig" />
<constructor-arg name="source" value="xml" />
</bean>
</beans>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With