Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy load spring bean

Tags:

java

spring

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
like image 294
Madhur Ahuja Avatar asked Apr 26 '18 10:04

Madhur Ahuja


People also ask

Are Spring beans lazy loaded?

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.

What is Lazy Bean in Spring?

@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.

How lazy loading works in Spring?

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.

Why bean factory is lazy loading?

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.


1 Answers

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.

like image 167
Cargeh Avatar answered Oct 19 '22 07:10

Cargeh