signed

QiShunwang

“诚信为本、客户至上”

Spring bean 生命周期循环依赖

2021/6/24 18:12:07   来源:

一、bean生命周期

先来回顾一下spring最基本的应用

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <bean name="source" class="org.example.spring.pojo.Source">
        <property name="drink" value="拿铁"/>
        <property name="sugar" value="加糖"/>
        <property name="size" value="超大杯"/>
    </bean>


   
</beans>
public class Source {

    private String drink;
    private String sugar;
    private String size;

    public String getDrink() {
        return drink;
    }

    public void setDrink(String drink) {
        this.drink = drink;
    }

    public String getSugar() {
        return sugar;
    }

    public void setSugar(String sugar) {
        this.sugar = sugar;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }
}

public class TestSpring {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Source source = (Source) context.getBean("source");
        System.out.println(source.getDrink());
        System.out.println(source.getSize());
        System.out.println(source.getSugar());
    }
}

运行结果
在这里插入图片描述

上面就是spring的一个最基本的应用。我们仅定义了source,就可以直接在测试类TestSpring中获取到source实例及其对应的方法调用。很明显spring帮我们完成了source类的实例化及初始化。
我们知道spring的定位可以作为容器来理解,容器里面的内容就是一个个的bean,每一个bean要想被正常使用,必须完成实例化、初始化步骤。
在这里插入图片描述
spring对bean的实例化内容相对较少(反射),这里主要来看初始化都经历了哪些步骤:
在完成了实例化操作后,此时对象的属性值还都是默认值,
因此初始化的第一个操作就是

给对象的属性赋值:populateBean

在文档开篇的xml文件里面,bean标签里面可以定义init-method,这个就是初始化方法。

<bean name="source" class="org.example.spring.pojo.Source" init-method="method">

在完成属性赋值后就开始执行该标签定义的方法

执行初始化方法:invokeInitMethods

此时容器里面就有了可以完整调用的bean供我们使用了

context.getBean(“source”);

看起来很简单,就是两点内容

  • populateBean
  • invokeInitMethods
    思考一个问题,我们定义的对象除了可以定义类似sugar size这样的基本属性外,如果我们在对象中定义BeanFactory这样的属性
public class Source {

    private String drink;
    private String sugar;
    private String size;
    
    private BeanFactory beanFactory;
}

那beanFactory该如何赋值,我们知道对象属性的赋值可以通过set方法赋值,如果我们给beanFactory添加对应的setBeanFactory方法是否可以完成beanFactory的赋值操作?
aware该出场了,spring通过标记接口完成类似BeanFactory属性的赋值

public interface Aware {

}

看下实现类

子类比较多,找到我们看似熟悉的BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   /**
    * Callback that supplies the owning factory to a bean instance.
    * <p>Invoked after the population of normal bean properties
    * but before an initialization callback such as
    * {@link InitializingBean#afterPropertiesSet()} or a custom init-method.
    * @param beanFactory owning BeanFactory (never {@code null}).
    * The bean can immediately call methods on the factory.
    * @throws BeansException in case of initialization errors
    * @see BeanInitializationException
    */
   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}

没错,该接口就提供了一个set方法,如果要完成BeanFactory属性的赋值,实现此接口即可,类似的接口还有很多,例如
ApplicationContextAwareImportAwareResourceLoaderAwareEnvironmentAware等。
所以在完成populateBean后,spring会继续对容器对象属性赋值

调用aware接口的方法:invokeAwareMethods

至此,属性赋值的操作已全部完成。
接下来,正常情况是直接调用初始化方法,但是spring为我们提供了bean的拓展功能,也可以理解为钩子函数,在初始化方法被调用前后,会有BeanPostProcessor的方法被执行

public interface BeanPostProcessor {

 
   Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;


   Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

before、after中间的内容就是invokeInitMethods

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
         @Override
         public Object run() {
            invokeAwareMethods(beanName, bean);
            return null;
         }
      }, getAccessControlContext());
   }
   else {
      invokeAwareMethods(beanName, bean);
   }

   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }

   try {
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }

   if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}

在真正执行invokeInitMethods时会先去判断bean是否实现了InitializingBean接口,如果是,则会首先调用InitializingBean#afterPropertiesSet。至此,spring的主要初始化工作结束。总结如下:
在这里插入图片描述

二、循环依赖

什么是循环依赖?出现在哪个阶段?

在这里插入图片描述

spring如何解决循环依赖?
通过添加缓存是方式解决循环依赖
具体细节:
未添加缓存的时候,初始化对象的过程是这样的
在这里插入图片描述

添加了缓存之后模拟函数调用栈如下:
在这里插入图片描述