写点什么

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

  • 2022 年 9 月 05 日
    江西
  • 本文字数:3056 字

    阅读完需:约 10 分钟

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


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

一、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();        }//省略....}
复制代码

二、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();    }   }  } }
复制代码

三、问答


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

发布于: 刚刚阅读数: 3
用户头像

关注公众号: 石臻臻的杂货铺 获取最新文章 2019.09.06 加入

进高质量滴滴技术交流群,只交流技术不闲聊 加 szzdzhp001 进群 20w字《Kafka运维与实战宝典》PDF下载请关注公众号:石臻臻的杂货铺

评论

发布
暂无评论
Spring源码分析(九)lazy-init 在Spring中是怎么控制加载的_spring_石臻臻的杂货铺_InfoQ写作社区