Spring lazy loading - loading a single bean loads all @Lazy beans of this class

I declared two beans of the same type of class. Initialized them as @Lazy . @Autowiring one bean of them automatically initialized another bean. I was surprised to see this behavior. Just curious to learn more about the mechanism.

code

 //bean public class HelloWorld { public HelloWorld(String msg){ System.out.println( msg + ", " + this); } } @Configuration @Lazy public class SpringAppContext { @Bean(name="helloworld1") public HelloWorld helloworld1(){ return new HelloWorld("helloworld1"); } @Bean(name="helloworld2") public HelloWorld helloworld2(){ return new HelloWorld("helloworld2"); } } @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes={SpringAppContext.class}) public class SpringBeanLazyLoadTest { @Autowired private HelloWorld helloworld2; @Test // this test is lame but just trying out. public void print(){ System.out.println("Autowired: " + helloworld2); } } 

Output

 helloworld2, my.entp.spring.HelloWorld@3a9bba helloworld1, my.entp.spring.HelloWorld@163f7a1 // why was helloworld1 initialized? Autowired: my.entp.spri ng.HelloWorld@3a9bba 

If you observe the output, you may notice that helloworld1 bean is initialized when helloworld2 is @Autowired .

I tested by removing @Autowired and getting the expected results: did not initialize any of the beans.

+7
source share
4 answers

With the direct use of the @Autowired injection byType . In other words, the container sees

  @Autowired private HelloWorld helloworld2; 

and tries to find a bean of type HelloWorld in the ApplicationContext for input.

The process of resolving the bean to be entered consists in obtaining the entire beans candidate, which consists of creating beans. So beans @Lazy doesn't change anything. They must still be created in order to be administered.

To clarify M. Deinum to the question, you gave your names beans. For instance,

  @Bean(name="helloworld1") 

When the injection process takes place, Spring will find all the candidate beans that can be injected. If there are more than one, he will filter them and try to find the best candidate. If it cannot, it will throw exceptions. One of the steps to finding the best candidate is to compare the name of the bean with the name of the target field. After your match, a bean named helloworld2 will be selected.

By removing @Autowired , beans is never requested with ApplicationContext and therefore never initialized.

+9
source

From Spring docs

However, when a lazily-initialized bean is a dependency of a single-tone bean that does not initialize in a lazy form, ApplicationContext creates a lazy-initialized bean at startup, since it must satisfy single-color dependencies. A lazy initialized bean is inserted into a singleton bean elsewhere, which is not a lazy initialized bean.

In your test case, the lazy bean is eagerly initialized, since the default test tool of Spring is to prepare the instance of the test class completely (by injecting all the dependencies with impatience), and then pass it to JUnit. The exact place where this happens is DependencyInjectionTestExecutionListener

 protected void injectDependencies(final TestContext testContext) throws Exception { Object bean = testContext.getTestInstance(); AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext().getAutowireCapableBeanFactory(); beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false); beanFactory.initializeBean(bean, testContext.getTestClass().getName()); testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE); } 
+2
source

Old post, but still. Now you can do this:

  @Bean(name="helloworld1", autowire=Autowire.BY_NAME) public HelloWorld helloworld1(){ return new HelloWorld("helloworld1"); } @Bean(name="helloworld2", autowire=Autowire.BY_NAME) public HelloWorld helloworld2(){ return new HelloWorld("helloworld2"); } 

And / or @Qualifier :

  @Autowired @Qualifier("helloworld2") private HelloWorld hello; 
+1
source

It seems to have been a Spring bug at the time if helloworld1 had a bean initialized. @Lazy will play a role in deciding when spring beans are initialized regardless of where it is called from (tests or otherwise).

Just tried this with Spring 5.1.0 and it only properly initializes the helloworld2 component.

If you @Lazy annotate @Lazy , then both helloworld1 and helloworld2 will be initialized. This is because Spring will create instances of all fans that will be used as part of the refresh phase. After all invokeBeanFactoryPostProcessors Spring are detected at the invokeBeanFactoryPostProcessors stage, Spring calls preInstantiateSingletons for all beanFactory in beanFactory which initialize all beanFactory components (some Lazy beans too, if necessary, as part of the desired bean initialization).

If your case, since the top-level SpringAppContext bean is lazy, this also applies to beans like HelloWorld .

If you delete @Autowired , @Autowired will not be created and will not be needed for automatic posting and therefore will not be initialized.

0
source

All Articles