show: stepversion: 1.0
enable_checker: true

浅谈SpringFramework懒加载机制原理

前言介绍

在前面的学习中,我们对Bean的创建有了一个粗略的了解,接着本文挑一个比较重要的知识点Bean的懒加载进行学习

什么是懒加载?

懒加载(Lazy-initialized beans):懒加载模式是bean在第一次调用时候被实例,而不是spring容器启动时候,默认是不开启的,( A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.),通过改配置lazy-init=”true”

实验环境准备

实验环境:

  • SpringFramework版本
  • Springframework5.0.x
  • 开发环境
  • JAR管理:gradle 4.9/ Maven3.+
  • 开发IDE:IntelliJ IDEA 2018.2.5
  • JDK:jdk1.8.0_31
  • Git Server:Git fro window 2.8.3
  • Git Client:SmartGit18.1.5(可选)

lazy-init使用

  1. import org.springframework.beans.factory.InitializingBean;
  2. /**
  3. *
  4. <pre>
  5. * SpringBean
  6. * </pre>
  7. *
  8. *
  9. <pre>
  10. * @author mazq
  11. * 修改记录
  12. * 修改后版本: 修改人: 修改日期: 2020/11/05 10:50 修改内容:
  13. * </pre>
  14. */
  15. public class SpringBean implements InitializingBean {
  16. public SpringBean(){
  17. System.out.println("SpringBean构造函数");
  18. }
  19. @Override
  20. public void afterPropertiesSet() throws Exception {
  21. System.out.println("SpringBean afterPropertiesSet");
  22. }
  23. }

xml配置方式,在applicationContext.xml加载配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <bean id="springBean" lazy-init="true" class="com.example.bean.SpringBean" ></bean>
  6. </beans>

注解方式,使用@Lazy即可

  1. import com.example.bean.SpringBean;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import com.example.bean.A;
  5. import org.springframework.context.annotation.Lazy;
  6. /**
  7. *
  8. <pre>
  9. * AppConfiguration
  10. * </pre>
  11. *
  12. *
  13. <pre>
  14. * @author mazq
  15. * 修改记录
  16. * 修改后版本: 修改人: 修改日期: 2020/11/05 10:26 修改内容:
  17. * </pre>
  18. */
  19. @Configuration
  20. public class AppConfiguration {
  21. @Bean
  22. @Lazy // 开启懒加载
  23. // @Lazy(value = false) 默认
  24. public SpringBean springBean() {
  25. return new SpringBean();
  26. }
  27. }
  1. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
  2. // lazy-init开启的bean,context.getBean调用时候才会被实例
  3. SpringBean springBean = context.getBean(SpringBean.class);
  4. System.out.println(springBean);

lazy加载机制原理

为什么设置为lazy-init之后,Spring IoC容器启动时候bean不会被实例?可以基于上一章内容找到答案
{@link org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization}

  1. /**
  2. * Finish the initialization of this context's bean factory,
  3. * initializing all remaining singleton beans.
  4. */
  5. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  6. // Initialize conversion service for this context.
  7. // 初始化ConversionService,这个bean用于将前端传过来的参数和后端的 controller 方法上的参数进行绑定
  8. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
  9. beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
  10. beanFactory.setConversionService(
  11. beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
  12. }
  13. // Register a default embedded value resolver if no bean post-processor
  14. // (such as a PropertyPlaceholderConfigurer bean) registered any before:
  15. // at this point, primarily for resolution in annotation attribute values.
  16. if (!beanFactory.hasEmbeddedValueResolver()) {
  17. beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
  18. }
  19. // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
  20. // 先初始化LoadTimeWeaverAware 类型的Bean
  21. // AspectJ 的内容,IoC的源码学习,先跳过
  22. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
  23. for (String weaverAwareName : weaverAwareNames) {
  24. getBean(weaverAwareName);
  25. }
  26. // Stop using the temporary ClassLoader for type matching.
  27. beanFactory.setTempClassLoader(null);
  28. // Allow for caching all bean definition metadata, not expecting further changes.
  29. // 冻结配置,不让bean 定义解析、加载、注册
  30. beanFactory.freezeConfiguration();
  31. // Instantiate all remaining (non-lazy-init) singletons.
  32. // 实例所有非懒加载的单例Bean
  33. beanFactory.preInstantiateSingletons();
  34. }

找到关键代码beanFactory.preInstantiateSingletons();

  1. @Override
  2. public void preInstantiateSingletons() throws BeansException {
  3. if (logger.isDebugEnabled()) {
  4. logger.debug("Pre-instantiating singletons in " + this);
  5. }
  6. // Iterate over a copy to allow for init methods which in turn register new bean definitions.
  7. // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
  8. // 获取beanName列表,this.beanDefinitionNames 保存了所有的 beanNames
  9. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
  10. // Trigger initialization of all non-lazy singleton beans...
  11. // 触发所有非懒加载的单例bean初始化操作(lazy-init=false)
  12. for (String beanName : beanNames) {
  13. // 合并rootBean中的配置, <bean id="a" class="a" parent="p" />
  14. RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
  15. // 非抽象(abstract = false)、非懒加载(lazy-init=false)的单例Bean(scope=singleton)
  16. if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
  17. // 处理FactoryBean,注意对比BeanFactory和FactoryBean
  18. if (isFactoryBean(beanName)) {
  19. // factoryBean调用在beanName加载前缀符号‘&’
  20. // 为什么要加‘&’,应该是做下标记,不过在bean创建过程要进行转换,详情请看下文
  21. Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
  22. if (bean instanceof FactoryBean) {
  23. FactoryBean<?> factory = (FactoryBean<?>) bean;
  24. boolean isEagerInit;
  25. // FactoryBean是SmartFactoryBean 的基类
  26. if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
  27. isEagerInit = AccessController.doPrivileged(
  28. (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
  29. getAccessControlContext());
  30. }
  31. else {
  32. isEagerInit = (factory instanceof SmartFactoryBean &&
  33. ((SmartFactoryBean<?>) factory).isEagerInit());
  34. }
  35. if (isEagerInit) {
  36. getBean(beanName);
  37. }
  38. }
  39. }
  40. else {
  41. // 普通的Bean,调这个方法进行实例,往下跟
  42. getBean(beanName);
  43. }
  44. }
  45. }
  46. // Trigger post-initialization callback for all applicable beans...
  47. // SmartInitializingSingleton 的基类在这里回调
  48. for (String beanName : beanNames) {
  49. Object singletonInstance = getSingleton(beanName);
  50. if (singletonInstance instanceof SmartInitializingSingleton) {
  51. SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
  52. if (System.getSecurityManager() != null) {
  53. AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
  54. smartSingleton.afterSingletonsInstantiated();
  55. return null;
  56. }, getAccessControlContext());
  57. }
  58. else {
  59. smartSingleton.afterSingletonsInstantiated();
  60. }
  61. }
  62. }
  63. }

代码里有isLazyInit()的校验,所以设置lazy-init=true的bean都不会随着ioc容器启动而被实例加载

学习归纳

综上所述:

  • 对于被修饰为lazy-init=false(默认情况)的bean Spring 容器初始化阶段会被依赖注入,并且实例到单例池里
  • 对于懒加载的bean,context.getBean的时候会从缓存里获取,因为容器初始化阶段 Bean 已经初始化完成并缓存了起来,但是还没被实例,第一次调用时候就会被实例