How to create programmatically a spring PublishSubscribeChannel - spring

I want to create programmatically the following XML config
<int:channel id="sample">
</int:channel>
What i can do is the following
PublishSubscribeChannel channel = new PublishSubscribeChannel();
But there is no method to assign the id.

Actually <int:channel> produces DirectChannel bean.
And if you want to do it programatically and have entire Messaging Infrastructure you should configure it as bean anyway:
#Configuration
public class MyConfiguration {
#Bean
public MessageChannel sample() {
return new DirectChannel();
}
}
The id attribute is a key feature of Spring IOC container and, of course, it isn't responcibility of concrete class.
Seems to me you should to take a look into the new stuff of Spring Integration 4.0.

The "id" is specific to a Spring application context and helps identify each instance that is being defined inside the application context.
What you have in there translates, more "verbosely" to this Spring config:
<bean id="sample" class="org.springframework.integration.channel.DirectChannel" />
The "id" identifies, like a name, a class DirectChannel instance within the context of a Spring ApplicationContext. The default channel used for a "int:channel" definition is the DirectChannel class, not PublishSubscribeChannel (DirectChannel).
But, apart from the channel itself, Spring Integration creates some other beans behind the scene.

Another way of wiring/injecting DirectChannel Bean: For Spring 3.2.3.RELEASE version
Code in Controller/Config Loader file:
#Configuration
public class ConfigControllerLoader {
#Autowired
#Qualifier("direct-channel-bean")
DirectChannel drtChannel;
}
Context file bean definition:
<int:channel id="direct-channel-bean"></int:channel>

Related

Bean overriding in Spring context that uses both annotation and xml config

There is a spring project A which is completely annotation based.
I need to override some beans conditionally in project B which is a legacy application using Spring 4.1.3 and uses xml based config.
There is FooConfig which is configuring beans using #ComponentScan. This config is a third party code for me. i.e I do not have access for this
#ComponentScan(basePackages = {"com.foo.bean"})
#Configuration
public class FooConfig {
}
I have created a BarConfig at my end, which imports this FooConfig and overrides some beans based on a condition. This is achieved using #Conditional
#Configuration
#Import(FooConfig.class)
public class BarConfig {
#Bean(name="helloService")
#Conditional(IsSpanishCondition.class)
public HelloService getHelloService() {
return new HelloService() {
#Override
public String getGreeting(String name) {
return "Hola "+name;
}
};
}
}
And I have included BarConfig in my application-context.xml
<context:annotation-config/>
<bean class="com.foo.config.BarConfig"/>
While this approach works flawlessly in Spring 5.1.2.RELEASE, it does not work in Spring 4.1.3.RELEASE
00:14:20.617 [main] INFO org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader - Skipping bean definition for [BeanMethod:name=getHelloService,declaringClass=com.foo.config.BarConfig]: a definition for bean 'helloService' already exists. This top-level bean definition is considered as an override.
Also, I have observed the same issue in Spring 4 in a completely annotation based context as well. i.e. it is not because of xml and annotation config mix but due to the Spring versions used here
Questions
What changed in Spring 5?
Is there any rule of thumb while working with a Spring application that uses both xml and annotation config especially when it comes to overriding the beans?
Also FTR, these are the solutions that worked
1.Overriding the beans using BeanPostProcessor
2.Using profiles. But this wouldn't work for complicated conditions.
#Profile("ENGLISH")
#Configuration
#Import(FooConfig.class)
public class EnglishConfig {
}
#Profile("SPANISH")
#Configuration
public class SpanishConfig {
#Bean(name="helloService")
public HelloService getHelloService() {
return new HelloService() {
#Override
public String getGreeting(String name) {
return "Hola "+name;
}
};
}
}
The issue here is that you are trying to override a xml bean from a #Configuration class, now I'm not 100% sure, but in spring 4 a xml bean still had precedence in choosing a bean, so the #Configuration beans would not get permission to overwrite the xml bean. Which was resolved in spring 5.
Your approach to use BeanPostProcessor is i guess the only viable solution for this.
I'm thinking maybe you could use a different bean name, implement your own behaviour and use #Qualifier annotation to choose which bean will get selected?

Spring Boot - bean definition

I'm looking into using Spring Boot for a new application but I'm having trouble figuring out the best approach to create the application beans.
At a high-level, this would be a web application that can have one or more beans of the same type - each with different property values. If I need to add a new bean of the same type, I should only have to configure it. Typically, if I was using Spring MVC, I would just define each bean in application context and load in the property values via a context file. Spring Boot prefers to do away with xml config, but I'm not sure how to translate the bean definitions into a Spring Boot solution. How do I still take advantage of IoC using Spring Boot.
Actually this has nothing to do with Spring Boot. As you mentioned, it supports both Java and XML bean configurations.
You can easily create multiple beans out of the same class using Java configuration.
XML config like:
<bean id="first" class="com.foo.MyClass" />
<bean id="second" class="com.foo.MyClass" />
translates into:
#Configuration
class MyConfiguration {
#Bean
MyClass first() {
return new MyClass();
}
#Bean
MyClass second() {
return new MyClass();
}
}
with Maciej Walkowiak's answer, it is also recomended to write it like this:
#Configuration
class MyConfiguration {
#Bean
#Qualifier("first")
MyClass first() {
return new MyClass();
}
#Bean
#Qualifier("second")
MyClass second() {
return new MyClass();
}
}
then later when you autowire you can use:
#Autowired
#Qualifier("second")
private MyClass myClass;

Register spring bean as an apache camel route builder with java config

apache camel documentation describes how to register a route builder with #Component and SpringRouteBuilder and then jumps to the xml code to do
<camelContext xmlns="http://camel.apache.org/schema/spring">
<!-- and then let Camel use those #Component scanned route builders -->
<contextScan/>
</camelContext>
How can I do the same with java config? I've got
package x.y.camel;
#Component
public class MyRouteBuilder extends SpringRouteBuilder {...}
and
#EnableWebMvc
#EnableAutoConfiguration
#ComponentScan(basePackages = {"x.y"})
public class Application implements WebApplicationInitializer {
#Bean
public SpringCamelContext camelContext(ApplicationContext applicationContext) throws Exception {
SpringCamelContext camelContext = new SpringCamelContext(applicationContext);
return camelContext;
}
The component is picked up by spring and created, that part is fine. I can register the route by camelContext.addRoutes(new MyRouteBuilder());. The only bit is missing is how to tell camel context to pick up the route if it's managed as a spring bean.
Your approach does not work, because you don't create your camel context with the CamelContextFactoryBean. This is where the logic is hidden that looks for Spring Bean Camel Routes in your classpath.
The easiest solution to the problem is to add a xml-based Spring context configuration that references this factory bean!
Alternatively, you can try calling the factory bean from your Application class (see this link: FactoryBeans and the annotation-based configuration in Spring 3.0), but calling a factory bean from a #Configuration class is tricky, because they are both part of mechanisms that are not build for compatibility. Especially, since CamelContextFactoryBean is also implementing InitialisingBean.
It turns out I was pretty close to the solution. All I had to do is to add a ComponentScan annotation on my CamelConfiguration class that I already had.
#Configuration
#ComponentScan("x.y.camel")
public class CamelConfig extends CamelConfiguration {
}
And then remove public SpringCamelContext camelContext(ApplicationContext applicationContext) from my Application class.
That's it - the RouteBuilder is picked up automatically.

Spring create bean only if condition is met

Assume there are two implementations of a single interface, and these beans are declared as beans in the spring configuration xml. Now, I would need only one implementation of the interface based on the system property. And, I don't wanna create the second implementation of the bean. How can I do this? I looked at this blog but then below snippet of the code from this blog uses "new" operate to create the beans. In my case the beans are declared in the spring configuration file.
http://www.intertech.com/Blog/spring-4-conditional-bean-configuration/
#CONFIGURATION
PUBLIC CLASS MYCONFIGURATION {
#BEAN(NAME="EMAILERSERVICE")
#CONDITIONAL(WINDOWSCONDITION.CLASS)
PUBLIC EMAILSERVICE WINDOWSEMAILERSERVICE(){
RETURN NEW WINDOWSEMAILSERVICE();
}
#BEAN(NAME="EMAILERSERVICE")
#CONDITIONAL(LINUXCONDITION.CLASS)
PUBLIC EMAILSERVICE LINUXEMAILERSERVICE(){
RETURN NEW LINUXEMAILSERVICE();
}

Is "jmsListenerContainerFactory" the default factory used by SimpleJmsListenerEndpoint

I am working with Spring JMS 4.1 to register messages listeners
In my xml configuration file, I have defined a bean named "jmsListenerContainerFactory":
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory"...p:concurrency="3-5".../>
First question : with Spring 4.x version, isn't it better to declare this factory this way : <jms:listener-container ... />
Second and main question : as stated in official doc (24.6.1) : by default, the infrastructure looks for a bean named jmsListenerContainerFactory as the source for the factory to use to create message listener containers. Is it also the case when programmatically registering endpoints this way:
.
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setDestination("...");
endpoint.setMessageListener(message -> {...});
registrar.registerEndpoint(endpoint);
or do we have to set it explicitly this way for example : registrar.setContainerFactoryBeanName("jmsListenerContainerFactory");
Thanks
I don't understand your first question; in the first case, you are defining a DefaultJmsListenerContainerFactory (a factory that creates listener containers) whereas the XML is creating a listener container directly.
The factory is useful if you need to create lots of containers with similar properties.
For simple container configuration, when you are not using annotated listeners, it certainly might be simpler to use traditional XML, or #Bean definitions for the container.
For the second question, the default registrar is already populated with the container factory bean name when it is passed into the configureListeners method; you don't have to set it.
SimpleJmsListenerEndpoint always looks for named bean "jmsListenerContainerFactory". so even there is no explicitly setting:
registrar.setContainerFactoryBeanName("jmsListenerContainerFactory");
JmsListenerEndpoint still can find the JmsListenerContainerFactory if there exists bean "jmsListenerContainerFactory".
Mean that in case you need to apply JmsListenerContainerFactory with different bean name, then setting on method registrar.setContainerFactoryBeanName("") doesn't effect at all.
Code below for the work and not working cases:
// config factory class
#Bean(name = "customJMSListenerContainerFactory")
public DefaultJmsListenerContainerFactory
listenerQueueFactory() {
DefaultJmsListenerContainerFactory factory = new
DefaultJmsListenerContainerFactory();
//more configs...
return factory;
}
// on consumer class
#Configuration
#EnableJms
public class MyConsumer implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
registrar.registerEndpoint(endpoint);
// this setting will not work => Spring JMS bug
registrar.setContainerFactoryBeanName("customJMSListenerContainerFactory");
// but this setting works
registrar.setContainerFactory(listenerQueueFactory());
}
}
This is bug of spring jms.

Resources