Saturday, May 12, 2018

Lazy load spring bean

Leave a Comment

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

1 Answers

Answers 1

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 Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment