Spring源码分析(九)lazy-init 在Spring中是怎么控制加载的

简介: ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化(也就是依赖注入)。提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的singleton bean。通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。

作者石臻臻, CSDN博客之星Top5Kafka Contributornacos Contributor华为云 MVP ,腾讯云TVP, 滴滴Kafka技术专家KnowStreaming, 《Kafka运维与实战宝典》电子书作者。 领取《Kafka运维与实战宝典》PDF请联系石臻臻


KnowStreaming  是滴滴开源的Kafka运维管控平台, 有兴趣一起参与参与开发的同学,但是怕自己能力不够的同学,可以联系我,当你导师带你参与开源!

1、lazy-init说明:

ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化(也就是依赖注入)。提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的singleton bean。通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。

<bean id="testBean" class="com.fhx.TestBean">

该bean默认的设置为:

<bean id="testBean" class="com.fhx.TestBean" lazy-init="false">
lazy-init="false" 立退加载, 表示spring启动时,立刻进行实例化。

(lazy-init 设置只对scop属性为singleton的bean起作用)

有时候这种默认处理可能并不是你想要的。如果你不想让一个singleton bean在ApplicationContext实现在初始化时被提前实例化,那么可以将bean设置为延迟实例化。

,   lazy-init="true"> 延迟加载 ,设置为lazy的bean将不会在ApplicationContext启动时提前被实例化,而是在第一次向容器通过getBean索取bean时实例化的。

如果一个设置了立即加载的bean1,引用了一个延迟加载的bean2,那么bean1在容器启动时被实例化,而bean2由于被bean1引用,所以也被实例化,这种情况也符合延迟加载的bean在第一次调用时才被实例化的规则。 在容器层次中通过在元素上使用'default-lazy-init'属性来控制延迟初始化也是可能的。如下面的配置:

<beans default-lazy-init="true"><!-- no beans will be eagerly pre-instantiated... --></beans>

一般beans  和 bean 层次配置的默认值都是false;并且bean的优先级>beans的优先级 如果一个bean的scope属性为scope=“pototype“时,即使设置了lazy-init="false",容器启动时不实例化bean,而是调用getBean方法是实例化的; 现在我们通过源码来分析一下; ##二、lazy-init 属性被设置的地方,并且优先级 bean>beans; 如果想看所有属性被设置的地方请看博文Spring是如何解析xml中的属性到BeanDefinition中的



//解析bean的属性值
 public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) {
 // 省略
 //如果当前元素没有设置 lazyInit 懒加载;则去 this.defaults.getLazyInit();这个defaults是上一篇分析过的;整个xml文件全局的默认值;
        String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
        if (DEFAULT_VALUE.equals(lazyInit)) {
            lazyInit = this.defaults.getLazyInit();
        }
//省略....
}

2、lazy-init发挥作用的地方


@Override
 public void refresh() throws BeansException, IllegalStateException {
 // 忽略..
 // 实例化所有剩余非 lazy-init 为true的单例对象
  finishBeanFactoryInitialization(beanFactory);
 // 忽略..  
 }

最终执行了 beanFactory.preInstantiateSingletons();

@Override
 public void preInstantiateSingletons() throws BeansException {
  if (this.logger.isDebugEnabled()) {
   this.logger.debug("Pre-instantiating singletons in " + this);
  }
  // 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.
  List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
  // 遍历所有bean
  for (String beanName : beanNames) {
   RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
   //1.bd的abstract属性是false;<bean abstract="false"> 不能被实例化,它主要作用是被用作被子bean继承属性用的;
   //2.单例对象并且 lazy-init为false
   //满足上面条件才行
   if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
   /**
   *如果实现了FactoryBean接口
   *1.先将FactoryBean的实现类实例化;
   *2.判断是否将FactoryBean实现类的getObject方法返回的实例对象也实例化;判断依据
   *  2.1如果当前bean实现了SmartFactoryBean接口,并且isEagerInit()返回true;才会调用工厂类的方法
   */
    if (isFactoryBean(beanName)) {
     final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
     boolean isEagerInit;
     if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
      isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
       @Override
       public Boolean run() {
        return ((SmartFactoryBean<?>) factory).isEagerInit();
       }
      }, getAccessControlContext());
     }
     else {
      isEagerInit = (factory instanceof SmartFactoryBean &&
        ((SmartFactoryBean<?>) factory).isEagerInit());
     }
     if (isEagerInit) {
      getBean(beanName);
     }
    }
    else {//如果不是FactoryBean接口之间实例化
     getBean(beanName);
    }
   }
  }
  // 调用所有SmartInitializingSingleton类型的实现类的afterSingletonsInstantiated方法;通过名字可以知道它表示 单例对象实例化后需要做的操作
  for (String beanName : beanNames) {
   Object singletonInstance = getSingleton(beanName);
   if (singletonInstance instanceof SmartInitializingSingleton) {
    final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
    if (System.getSecurityManager() != null) {
     AccessController.doPrivileged(new PrivilegedAction<Object>() {
      @Override
      public Object run() {
       smartSingleton.afterSingletonsInstantiated();
       return null;
      }
     }, getAccessControlContext());
    }
    else {
     smartSingleton.afterSingletonsInstantiated();
    }
   }
  }
 }

3、问答


1.Ioc容器在实例化bean的时候,Ioc会主动调用FactoryBean类型的的getObject方法来为我们生成对象吗?答: 一般情况下是不会的,一般情况碰到FactoryBean类型的是调用 getBean(&beanName),但是有一种情况例外,如果这个FactoryBean还实现了SmartInitializingSingleton接口的话,IOC就会帮我们主动调用getBean(beanName)来实例化;

相关文章
|
3月前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
85 1
|
22天前
|
监控 Java 应用服务中间件
SpringBoot是如何简化Spring开发的,以及SpringBoot的特性以及源码分析
Spring Boot 通过简化配置、自动配置和嵌入式服务器等特性,大大简化了 Spring 应用的开发过程。它通过提供一系列 `starter` 依赖和开箱即用的默认配置,使开发者能够更专注于业务逻辑而非繁琐的配置。Spring Boot 的自动配置机制和强大的 Actuator 功能进一步提升了开发效率和应用的可维护性。通过对其源码的分析,可以更深入地理解其内部工作机制,从而更好地利用其特性进行开发。
42 6
|
2月前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
3月前
|
监控 IDE Java
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
195 8
|
5月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
432 24
|
4月前
|
缓存 JavaScript Java
Spring之FactoryBean的处理底层源码分析
本文介绍了Spring框架中FactoryBean的重要作用及其使用方法。通过一个简单的示例展示了如何通过FactoryBean返回一个User对象,并解释了在调用`getBean()`方法时,传入名称前添加`&`符号会改变返回对象类型的原因。进一步深入源码分析,详细说明了`getBean()`方法内部对FactoryBean的处理逻辑,解释了为何添加`&`符号会导致不同的行为。最后,通过具体代码片段展示了这一过程的关键步骤。
Spring之FactoryBean的处理底层源码分析
|
3月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
55 1
|
3月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
53 1
|
3月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
48 0
|
5月前
|
XML 存储 Java
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)