If a bean is lazily loaded, Will all the beans defined inside the lazily loaded beans will be lazily loaded? (even though they are not defined @Lazy)
Here is the test project: https://github.com/madhur/conditional-property-test/blob/master/src/main/java/com/example/demo/EventPublishService.java
I have these beans:
@Service
@ConditionalOnProperty(
name = {"publish.feed.events"},
havingValue = "true"
)
public class EventPublishService {
@Autowired
private KafkaPublisher kafkaPublisher;
}
////////////////////
@Service
public class EventService {
@Autowired
@Lazy
private EventPublishService eventPublishService;
@Value("${publish.feed.events:false}")
private boolean isPublishEvents;
}
/////////////////////////
@Component
public class KafkaPublisher {
@Value("${kafka.producer.financial_feed.topic}")
private String financeFeedTopic;
}
Any my application.properties has just one property,
publish.feed.events=false
Since , I am not loading EventPublishService
beans(because the property is false), I expect the dependency bean KafkaPublisher
should not be loaded as well. However, I get the error upon startup, which means the KafkaPublisher
bean is getting loaded, even though EventPublishService
bean is not getting loaded.
How can I ensure that KafkaPublisher
bean should not be loaded?
And thus property should not be mandatory required for anyone not requiring EventPublishService
bean?
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'kafkaPublisher': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'kafka.producer.financial_feed.topic' in value "${kafka.producer.financial_feed.topic}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:379) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1344) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:578) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:501) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:760) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:869) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759) [spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395) [spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:327) [spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1255) [spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1243) [spring-boot-2.0.1.RELEASE.jar:2.0.1.RELEASE]
at com.example.demo.DemoApplication.main(DemoApplication.java:17) [main/:na] Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'kafka.producer.financial_feed.topic' in value "${kafka.producer.financial_feed.topic}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:172) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:237) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:211) ~[spring-core-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175) ~[spring-context-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:839) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1086) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:91) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:373) ~[spring-beans-5.0.5.RELEASE.jar:5.0.5.RELEASE]
... 16 common frames omitted
By default, Spring “application context” eagerly creates and initializes all 'singleton scoped' beans during application startup itself. It helps in detecting the bean configuration issues at early stage, in most of the cases.
@Lazy annotation indicates whether a bean is to be lazily initialized. It can be used on @Component and @Bean definitions. A @Lazy bean is not initialized until referenced by another bean or explicitly retrieved from BeanFactory . Beans that are not annotated with @Lazy are initialized eagerly.
By default in Spring, all the defined beans, and their dependencies, are created when the application context is created. In contrast, when we configure a bean with lazy initialization, the bean will only be created, and its dependencies injected, once they're needed.
Lazy Loading With BeanFactory. Here, the Student object is not initialized. In other words, only the BeanFactory is initialized. The beans defined in our BeanFactory will be loaded only when we explicitly call the getBean() method.
As stated in the doc,
Beans that are singleton-scoped and set to be pre-instantiated (the default) are created when the container is created
thus it doesn't matter if you put @Lazy
on @Autowired
, the bean will be created at startup as long as the bean itself is not defined lazy:
@Component
public class KafkaPublisher {
...
}
If you want this bean to be lazily initialized, put @Lazy
on it. If you want the process of bean injection to be lazy, put @Lazy
on @Autowired
, as simple as that.
Lazily initializing a set of related beans could be achieved by declaring them inside a @Configuration
class and marking configuration @Lazy
. However, note that ALL of the declared beans will be initialized as soon as @Configuration
class is initialized.
@Lazy
@Configuration
@ComponentScan(...) // will be lazily initialized with config
public class LazyConfiguration {
@Bean
public SomeBean beanName() { // will be lazily initialized with config
return new SomeBean();
}
@Bean
public OtherBean beanName() { // will be lazily initialized with config
return new OtherBean();
}
}
However, when using @ComponentScan
, make sure the beans (i.e @Service
, @Component
, etc) you want to initialize lazily are not already scanned by some other context. If they are, add them as exclusions to scanning for that context.
Outdated answer down below, for historical purposes (before OP completely changed the question):
Declaring beans inside beans is not considered to be a good practice, but if you mark a @Configuration
as lazy, then indeed all the beans inside of it will be lazily initialized, as stated in @Lazy
's javadoc:
If Lazy is present on a @Configuration class, this indicates that all @Bean methods within that @Configuration should be lazily initialized.
If by "beans defined inside" you actually mean injection points, such as @Autowired
, javadoc has the answer for that as well:
Lazy annotation may also be placed on injection points marked with Autowired or Inject: In that context, it leads to the creation of a lazy-resolution proxy for all affected dependencies, as an alternative to using ObjectFactory or Provider.
However, the actual autowired bean will be initialized eagerly (if not marked as @Lazy as well). It's the injection itself that will be lazy in this case.
Otherwise, please clarify what you mean.
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