Injecting from application.yml in camel processor - spring-boot

I'm trying to inject values from the application.properties file in a camel processor but it returns null. I also tried adding the #Component annotation but this breaks the application.
public class MyProcessor implements Processor {
#Value("${myProperty.path}")
public String path;
public void process(Exchange exchange) throws Exception {
System.out.println(path);
}
}
I'm new to both camel and spring. What could I do to read properties from the application.properties file in the processor class?

Property injection with #Value only works with container managed beans.
Therefore, you are on the right track with #Component, but I suspect (as mentioned in the comments) that your MyProcessor is not such a bean.
If you do one of these in your Camel route, then your Processor is NOT such a bean.
.process(MyProcessor.class)
.process(new MyProcessor())
Instead you have to annotate your Processor with #Component, hold an instance variable of it in your Camel Route class and then reference the instance.
.process(myProcessorInstance) <-- variable

Related

Someone can explain to me for a use case of springboot injection

I read these lines from a colleague's code:
#Bean(name = "mysql")
#ConfigurationProperties(prefix = "spring.mysql")
#Primary
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public ClassA classA () {
return new ClassA (this.mysqlDataSource());
}
#Bean
public ClassB classB () {
return new ClassB (this.mysqlDataSource());
}
I thought this will create 2 DataSources for Bean classA and classB. for injecting the datasource, we need something like:
#Bean
public ClassA classA (DataSource ds) {
return new ClassA (ds);
}
But Spring just create one datasource, and this.mysqlDataSource() returns the same one everytime. how does it happen? If I do need another DataSource, i need create it on the fly?
Spring says #Component and #Configuration has different meanings.
If you use #Configuration instead of #Component, CGLIB proxying will be used.
"The #Bean methods in a regular Spring component are processed differently than their counterparts inside a Spring #Configuration class. The difference is that #Component classes are not enhanced with CGLIB to intercept the invocation of methods and fields. CGLIB proxying is the means by which invoking methods or fields within #Bean methods in #Configuration classes creates bean metadata references to collaborating objects; such methods are not invoked with normal Java semantics but rather go through the container in order to provide the usual lifecycle management and proxying of Spring beans even when referring to other beans via programmatic calls to #Bean methods. In contrast, invoking a method or field in an #Bean method within a plain #Component class has standard Java semantics, with no special CGLIB processing or other constraints applying."
https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/core.html#spring-core
Alternatively, you can keep #ConfigurationProperties in the class level and remove #Bean from DataSource so that mysqlDataSource() will be treated as a regular method..
The method this.mysqlDataSource() returns the bean because Spring create a proxy for configuration class. You can see details here
By default Spring container creates bean with scope "singleton".
So you have single DataSource instance in container and this instance will be injected to ClassA and ClassB objects. If you want to have different instances you should change scope to "prototype".
You can use annotation #Scope("prototype") to do it.

How can I set the default behavior of lazy init in Spring Boot?

I am working on my first Spring Boot application and I have the following problem.
I want to set the that for default all beans are lazy loaded. I know that I can add the #Lazy to all my #Component beans but I want that for default all beans are setted at lazy...
In Spring Boot I don't have an XML configuration file or a configuration class but I only have an application.properties configuration file.
So, how can I set that the default behavior for all the bean is lazy=true
To implement a BeanFactoryPostProcessor that sets lazy initialization by default (which can be required if you are e.g. defining some of the beans dynamically, outside of your #Configuration class(es)), the following approach worked for me:
#Component
public class LazyBeansFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory ) throws BeansException {
for ( String name : beanFactory.getBeanDefinitionNames() ) {
beanFactory.getBeanDefinition( name ).setLazyInit( true );
}
}
}
This essentially puts the #Lazy annotation on all your #Component and #Services. You might want to invent a mechanism to annotate classes with #Eager if you go this route, or just hardwire a list in the LazyBeansFactoryPostProcessor above.
Further reading
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/BeanFactoryPostProcessor.html
Since the version 2.2.2.RELEASE of spring-boot you can use the property below in your application.properties file
spring.main.lazy-initialization=true
for further reading and a good example please refer to
https://www.baeldung.com/spring-boot-lazy-initialization
https://spring.io/blog/2019/03/14/lazy-initialization-in-spring-boot-2-2

Whats bean in spring and what is not

lets say I have code like this:
#Repository
public class Foo{
}
#Service
public class Boo{
#Autowired
private Foo foo;
}
so now what here are we calling bean? Bean is the object of Foo type of refrence "foo" BUT are Boo class annotated as Service and Foo as Repository ALSO beans? Ihve been using spring for a while now but this basic question makes me feel bad for not knowing...
In the context of Spring, A bean is a spring managed object. Here spring managed means an object created, initialised, managed, destroyed by Spring IoC container.
Whenever we mark a class with #Component, Spring IOC container will create object for your class and manage it, Whenever we can simply get it from ApplicationContext, or access it using #Autowired/#Resource/#Inject annotations
We can also use #Controller, #Repository, #Service, #ControllerAdvice, #Configuration,#Aspect in place of #Component to tell more specifically that our class is a service or a repository or an aspect etc.
We can also use #Bean annotation to create a bean from method return value
#Configuration
public class SolrConfig {
#Value("${spring.data.solr.host}") String solrUrl;
#Bean
public SolrServer solrServer() {
return new HttpSolrServer(solrUrl);
}
#Bean(name = "solrTemplate")
public SolrTemplate solrTemplate() {
return new SolrTemplate(new HttpSolrServer(solrUrl), RULE_ENGINE_CORE);
}
}
All of your application components (#Component, #Service, #Repository, #Controller etc.) will be automatically registered as Spring Beans
http://docs.spring.io/autorepo/docs/spring-boot/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
Defining Beans can be thought of as replacing the keyword new.
Further information can be found here which might be helpful for understanding Beans in Spring.

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();
}

Resources