Original website: Spring/SpringBoot -- deferred loading / Lazy loading / deferred initialization / @ Lazy annotation -- use / usage / principle_ CSDN blog
brief introduction
explain
- Delayed initialization is often referred to as "lazy loading".
- Definition of delayed initialization: a Bean is not initialized at startup and is not initialized until it is used.
- By default, beans are initialized at startup.
Deferred load usage
Method 1: add @ Lazy annotation on @ Component class
@Lazy @Component public class XXXX { ... }
Method 2: @ Lazy annotation is added when @ Bean is configured in @ Configuration class
@Configuration public class XXXX { @Lazy @Bean public XXX getXXX() { return new XXX(); } }
Method 3: @ ComponentScan configuration
@ComponentScan(value = "XXX.XXX", lazyInit = true) @Configuration public class XXXX { ... }
Method 4: directly configure the tag attribute in the XML file
<bean id="XXX" class="XXX.XXX.XXXX" lazy-init="true"/>
Global and local delay initialization
The above configuration methods are local delay initialization, and the global configuration delay initialization methods are as follows:
Method 1: application.yml
spring.main.lazy-initialization=true
Method 2: directly configure the tag attribute in the XML file
<beans ... default-lazy-init="true"/>
Method 3: start the main program
@SpringBootApplication public class DemoSpringbootApplication { @Lazy public static void main(String[] args) { SpringApplication sa = new SpringApplication(DemoSpringbootApplication.class); sa.setLazyInitialization(true); sa.run(args); } }
Method 4: start the main program
@SpringBootApplication public class DemoSpringbootApplication { @Lazy public static void main(String[] args) { SpringApplicationBuilder sab = new SpringApplicationBuilder(DemoSpringbootApplication.class); sab.lazyInitialization(true).run(args); } }
Simple example
Bean
package com.example.lazy; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @Component @Lazy public class LazyTest { public LazyTest() { System.out.println("[LazyTest.LazyTest]: 10"); } public void print() { System.out.println("This is LazyTest print"); } }
ApplicationContext tool class
package com.example.lazy; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class SpringApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext context; public void setApplicationContext(ApplicationContext context) throws BeansException { SpringApplicationContextHolder.context = context; } public static ApplicationContext getContext() { return context; } }
Test class
package com.example.controller; import com.example.lazy.LazyTest; import com.example.lazy.SpringApplicationContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/test1") public String test1() { LazyTest lazyTest = SpringApplicationContextHolder.getContext().getBean(LazyTest.class); System.out.println("About to execute lazy Class method"); lazyTest.print(); return "test1 success"; } }
test
Start the program. Result: no related printing.
visit: localhost:8080/test1
result:
[LazyTest.LazyTest]: 10 About to execute lazy Class method This is LazyTest print
If @ Lazy on the LazyTest class is removed, the contents of the constructor will be printed as soon as it is started:
[LazyTest.LazyTest]: 10
Usage scenario
1. Solve circular dependency
2. Solve the problem of injecting @ Service annotation into @ Configuration
Failure example
Non deferred loaded Controller
@Controller public class TestController implements InitializingBean{ @Autowired private TestService testService; @Override public void afterPropertiesSet() throws Exception { System.out.println("testController Initializing"); } }
Deferred loaded Service
@Lazy @Service public class TestService implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("testService Initializing"); } }
test
Start printing:
testService Initializing testController Initializing
analysis
After starting the Spring program, the string printed in TestService is output. The @ Lazy annotation is clearly used, but it has no effect. This class is still loaded when Spring starts the project. This involves the use of automatic injection annotations such as @ Autowired.
Since the Controller class is not loaded late and uses the @ Autowired auto injection annotation to inject Service, the Controller will be initialized during program initialization. At the same time, when processing the @ Autowired annotated fields, it will call the getBean method to obtain the bean object of the field from the Spring factory. Therefore, it is added to the Service through the @ Autowired route, This leads to the invalidation of @ Lazy annotation. Therefore, although it is not initialized through the refresh method process, it is initialized through the processing class of @ Autowired.
resolvent:
Method 1: change the Controller to @ Lazy, so that it will not be loaded at startup and the call of @ Autowired annotation dependency chain will not be triggered. As follows:
@Lazy @Controller public class TestController implements InitializingBean{ @Autowired private TestService testService; @Override public void afterPropertiesSet() throws Exception { System.out.println("testController Initializing"); } }
Method 2: @ Lazy is added to @ Autowired. As follows:
@Controller public class TestController implements InitializingBean{ @Autowired @Lazy private TestService testService; @Override public void afterPropertiesSet() throws Exception { System.out.println("testController Initializing"); } }
principle
brief introduction
When the container starts Only processing Non lazy init beans, lazy loaded beans do not do any processing at all during the Spring startup phase.
Spring initialization entry refresh
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Instantiate all remaining (non-lazy-init) singletons. // Initialize all non lazy loaded bean s finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); }catch(){} } }
finishBeanFactoryInitialization(beanFactory); It is related to this topic.
finishBeanFactoryInitialization(beanFactory)
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { //Other codes // Code of concern beanFactory.preInstantiateSingletons(); } // Other codes }
There is a function to initialize non lazy init beans in finishBeanFactoryInitialization(beanFactory) preInstantiateSingletons()
preInstantiateSingletons
The specific logic is as follows
1. Traverse the beanNames collection to obtain each BeanDefinition
2. Judge whether it is lazy loading. If not, continue processing (non lazy init beans do not process it)
3. Judge whether it is a factorybean. If not, instantiate and dependency injection
public void preInstantiateSingletons() throws BeansException { // Iterate over a copy to allow for init methods which in turn register new bean definitions. // While this may not be part of the regular factory bootstrap, it does otherwise work fine. // All beanDefinition sets List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); // Trigger initialization of all non-lazy singleton beans... // Trigger the initialization of all non lazy load singleton bean s for (String beanName : beanNames) { // Get bean definition RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // Determine whether it is a lazy loading singleton bean. If it is singleton and not lazy loaded, it is in the Spring container if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // Determine whether it is a FactoryBean if (isFactoryBean(beanName)) { Object bean = getBean(FACTORY_BEAN_PREFIX + beanName); if (bean instanceof FactoryBean) { final FactoryBean<?> factory = (FactoryBean<?>) bean; boolean isEagerInit; if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) { isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit, getAccessControlContext()); } else { isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit()); } if (isEagerInit) { getBean(beanName); } } } else { // If it is an ordinary bean, initialization dependency injection is performed. The logic triggered by this getBean(beanName) is the same as // The logic triggered by context.getBean("beanName") is the same getBean(beanName); } } } }