九.Spring之依赖注入、循环依赖原理

一.依赖注入

诸如 @Autowired、@Inject、@Resource 是如何将实例注入到属性的。

1.演示代码

BookService,给其中用不同注解,注入不同的dao实例

@Service
public class BookService {
	
	@Autowired(required=true)
	private BookDao bookDao;

	@Inject
	private BookDao2 bookDao2;

	@Resource
	private BookDao3 bookDao3;

	@Qualifier("bookDao4")
	@Autowired
	private BookDao4 bookDao4;
}

随便新建几个dao

@Repository
public class BookDao {
}
@Repository
public class BookDao2 {
}

单元测试 和配置类

@Configuration
@ComponentScan({"com.atguigu.service","com.atguigu.dao",
	"com.atguigu.controller","com.atguigu.bean"})
public class MainConifgOfAutowired {
}

@Test
	public void test01(){
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConifgOfAutowired.class);
		
		BookService bookService = applicationContext.getBean(BookService.class);
		System.out.println(bookService);
}

2.debug创建BookService

创建service这种实例,是在 容器refresh方法的最后一步,finishBeanFactoryInitialization,我直接将断点设置在preInstantiateSingletons方法上,将断点调试为创建 BookService,然后一步步看如何依赖注入。

一直debug到doCreateBean.

AbstractAutowireCapableBeanFactory.doCreateBean

首先会创建bean,然后遍历 MergedBeanDefinitionPostProcessor (一种后置处理器)修改bean定义。

其中有两个 MergedBeanDefinitionPostProcessor 需要讲,AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor。

3.MergedBeanDefinitionPostProcessor

AutowiredAnnotationBeanPostProcessor.findAutowiringMetadata 寻找参数bean有关Autowired注解的元数据,就是bean有哪些属性注解了@Autowired。

buildAutowiringMetadata里面是用反射找,并且放入缓存,后面会用到。

BookService找到了三个有关Autowired的元数据,即三个属性。

对比演示示例,说明@Autowired、@Inject、@Qualifier都是用这个AutowiredAnnotationBeanPostProcessor可以解析。

而CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition完成@Resource元数据的寻找,并放入缓存。

4.populateBean

继续doCreateBean创建BookService实例

  • 判断是否单例、是否允许循环依赖、当前bean是否正在创建然后 addSingletonFactory(这一步会在循环依赖中细讲)
  • populateBean设置属性,向BookService注入示例 就是在这里完成的

进入populateBean,直接看 完成成注入的一步。

遍历InstantiationAwareBeanPostProcessor执行postProcessPropertyValues,其中需要看的是CommonAnnotationBeanPostProcessor 和AutowiredAnnotationBeanPostProcessor。

5.InstantiationAwareBeanPostProcessor

CommonAnnotationBeanPostProcessor.postProcessPropertyValues()完成@Resource 的注入。

  • findResourceMetadata 寻找注解@Resource的属性,之前在postProcessMergedBeanDefinition方法中也调用了此方法,找到并放入了缓存。
  • metadata.inject(bean, beanName, pvs);注入实例

下图看到属性信息

进入metadata.inject,遍历elementsToIterate 就是要注入的属性

用反射注入属性值,因为当前要注入的属性是 BookDao3,这个实例还没有,所以继续debug会发现会用工厂创建BookDao3,就会进入创建bean流程。

再看AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues,找到标注Autowired注解的属性,并完成注入。

6.总结

CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor 分别类上注解解析了@Resource 和@Autowired、@Inject的属性信息,并完成注入,如果容器中没有属性需要的实例,就会创建再注入。

二.@DependsOn

@DependsOn自定义表明当前bean需要依赖些bean,Spring会优先创建依赖bean到容器中。

1.演示

BookService依赖一个名为color2的bean,配置类中注入了这个bean。

@Service
@DependsOn(value = "color2")
public class BookService{
}

@Configuration
@ComponentScan({"com.atguigu.service","com.atguigu.dao",
	"com.atguigu.controller","com.atguigu.bean"})
public class MainConifgOfAutowired {
	

	@Bean
	public Color color2(){
		Color color = new Color();
		return color;
	}
	

}

2.原理 

依然将断点设置到 创建BookService时,AbstractBeanFactory.doGetBean方法。

其中在bean定义中会寻找到依赖的bean,优先创建注册。

三.循环依赖

假设有两个实例bean,A和B,A的属性注入了B实例,B的属性又注入了A,互相注入了对方实例,无线套娃,就叫循环依赖。

首先说明,spring中,只支持在属性注入的循环依赖,如果是通过构造方法注入是不支持循环依赖的

1.演示代码

BookService注入了BookDao

@Service
public class BookService {
	
	@Autowired(required=true)
	private BookDao bookDao;
}

BookDao注入了BookService

@Repository
public class BookDao {

	@Autowired
	private BookService bookService;
}

跟上边一样的配置了和单元测试,运行结果:

2.匿名内部类

看原理前先看一个匿名内部类的应用:

一个工厂接口

public interface ObjectFacoryTest {

    Object getObject();
}

单元测试, 循环在集合中加入 工厂接口的匿名内部类实现,重写方法调用测试类的getnum方法返回数字,最后遍历工厂将数字打印。结果0-9打印

2.spring实例缓存

把断点设置到创建BookService,在 AbstractAutowireCapableBeanFactory的 doCreateBean方法

下图,判断实例单例、允许循环依赖、当前实例正在创建中,则执行 addSingletonFactory 方法,参数是匿名内类,内部类重写方法getObject 其中调用了 AbstractAutowireCapableBeanFactory.getEarlyBeanReference。

注意 getObject 可以得到beanName对应的bean实例,即使是还没初始化的实例

进入addSingletonFactory,判断当前bean是否在 singletonObjects中(bean如果完整创建就存在),这里还不存在,所以将参数工厂放入singletonFactories,以及其他缓存操作。

这里涉及到几个缓存:

  • singletonObjects:<bean名字,bean实例>,已经完全创建好的实例
  • singletonFactories:<bean名字,实例工厂>,bean对应的一个实例工厂,可以得到bean。
  • earlySingletonObjects:<bean名字,早期的bean实例> ,还没完全创建好的bean。
  • registeredSingletons:已经注册过的bean名称

3. 创建被注入的实例

在上边依赖注入中讲过,populateBean会完成注入操作,将BookDao注入BookService,但此时BookDao实例还不在容器中,继续debug会发现流程又走到了 doCreateBean,去创建BookDao。

但BookDao也要注入BookServic实例,刚刚BookService并没有创建成功,但是几个缓存中只有singletonFactories 和registeredSingletons有BookService的信息。

我们来debug 创建BookDao时的populateBean方法,看在BookService没有创建完成的情况下,BookDao是如何创建成功的?

依然是InstantiationAwareBeanPostProcessor遍历执行。因为BookDao中的属性bookService是用@Autowired注入的,所以由 AutowiredAnnotationBeanPostProcessor完成属性注入。

AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues,首先找到了BookDao中由@Autowired注解的属性bookService

断点进入inject方法,由beanFactory.resolveDependency会得到一个实例值。

进入 doResolveDependency方法,里面大概是寻找符合的bean,如果bean数量大于1怎么处理之类的,最后执行流程会进入AbstractBeanFactory.doGetBean,很熟悉,又是从bean工厂获取BookService实例。

但是这次不会再新创建实例,而是在 getSingleton方法直接得到一个bean。

之前将BookService 在创建bean对应的实例工厂缓存起来,这里调用工厂可以得到之前的 正在创建中的BookService bean,

得到后销毁这个BookService实例工厂,并将这个bean放在了 earlySingletonObjects 早期bean缓存中。

getSingleton方法中优先去earlySingletonObjects缓存获取bean,不难想到,如果其他类也循环依赖了BookService 就可以从earlySingletonObjects拿到正在创建的 BookService 实例。

到这步为止,创造BookDao的过程中就将 正在创建的BookService实例拿到了,拿到后继续执行inject方法,就完成了把BookService注入BookDao.

从线程执行流程也可以看到,BookDao的创建流程 是由BookService创建时的populateBean方一步步发起的。

当BookDao创建完成时,继续执行BookService的属性注入

在AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues方法,断点进入inject方法,拿到了BookDao实例,反射将其注入BookService.到此完成了循环依赖

这里的BookService bean和 earlySingletonObjects中的BookService bean是一个bean,可以往前追踪,这里的bean是来自于BeanWrapper,bean的包装。

而earlySingletonObjects的bean来自于singletonFactories,singletonFactories的bean也来自于BeanWrapper。

4.总结

  1. A和B循环依赖,创建A的过程中会将 正在创建的A实例存放在earlySingletonObjects和singletonFactories
  2. 创建A时,发现需要创建B,就去创建B。
  3. 创建B时,通过singletonFactories或者earlySingletonObjects拿到 A实例,完成把A注入B,虽然此时是未完全创建成功的A
  4. 回到创建A的过程,创建好了B,就把B注入A。

猜你喜欢

转载自blog.csdn.net/u014203449/article/details/105956107
今日推荐