【Spring】依赖注入&循环依赖

本文探讨了依赖注入的两种方式(构造器和setter)以及Spring自动注入模式,重点剖析了如何处理循环依赖,通过三级缓存机制来确保单例Bean的正确创建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

依赖注入&循环依赖

依赖注入

依赖注入有2种方式:

Constructor-based dependency injection 和 Setter-based dependency injection

构造器注入例子:

SimpleMovieLister依赖了MovieFinder,然后通过构造参数来实现依赖注入

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private final MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

Setter注入的例子:
是在调用无参构造之后调用setter来注入的。

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

自动注入

Spring自动注入的5种模式:

  • 0:no不会自动注入 默认是这种

  • 1:byName 根据bean的名字来装配

  • 2:byType 根据bean的类型来装配

  • 3:constructor 通过构造

  • 4:auto-detect 我测试结果是有setter先用setter,没有就用constructor

Spring默认给Bean设置的自动注入默认为0,不自动注入。我们通过设置beanDefinition的AutoWireMode来改变自动注入的模式(虽然还可以在xml里设置)。

byName和byType都是通过setter来自动注入属性的,注意的是byName的setter的方法名需要为set属性名();而byType是不需要,是根据参数类型来判定的。

construtor是通过参数只有依赖属性的构造方法来注入的。

@Autowired来注入

使用@Autowired其实就是手动注入了,Spring默认为没有自动注入的时候我们是通过这种注解来注入依赖的。其实这种方式既不是通过setter也不是通过constructor,而是Spring会帮你去遍历这个类的所有Filed,把加了@Autowire注解的Filed`创建对应对象,并设置对象来注入的。

循环依赖

⚠️只有单例Bean才可以解决循环依赖!

Spring是如何解决循环依赖的呢?

三级缓存

Spring提供了三级缓存分别是:

  1. singletonObjects:单例池,存放已经创建完成,属性也填充好的对象。
  2. earlySingletonObjects:存放已经创建完成,但是属性没有注入好的对象。
  3. singletonFactories:存放已经创建完成,但是属性还注入好的对象的工厂对象,通过这个工厂可以返回这个对象。

使用三级缓存的原因

使用第三级缓存singletonFactories的原因:因为Spring的AOP是在bean初始化之后的后置处理方法里实现的。如果没有第三级缓存,我们只能在实例化bean的时候就对其AOP代理了,或者就直接实例化,无法注入一个AOP的bean了。

使用这个存放工厂对象的缓存,咱们并没有真的注入一个bean,而是先注入一个半成品bean,在生命周期后面对用后置处理SmartInstantiationAwareBeanPostProcessor方法完成处理AOP,就返回一个Aop处理后的对象,如果该类没有被代理就返回一个传入的bean。

循环依赖的流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lvwF3H30-1625675090390)(/Users/lmafia/Library/Application Support/typora-user-images/image-20210707013639839.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-35sglgNz-1625675186085)(/Users/lmafia/Downloads/spring-Spring循环依赖-1.png)]

源码分析

当需要拿到一个单例bean是做了哪些判断?

DefaultSingletonBeanRegistry.java 里


	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		Object singletonObject = this.singletonObjects.get(beanName);
    //从一级缓存拿不到单例对象,然后这个单例对象正在创建,singletonCurrentlyInCreation是一个集合,记录了正在创建的bean的名字
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      //从二级缓存中取
			singletonObject = this.earlySingletonObjects.get(beanName);
      //如果二级缓存中没有,是否允许早期引用(其实就是说要不要在三级缓存中找)
			if (singletonObject == null && allowEarlyReference) {
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
              //在三级缓存中找
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
                //通过工厂创建一个对象
								singletonObject = singletonFactory.getObject();
                //把对象放到二级缓存中
								this.earlySingletonObjects.put(beanName, singletonObject);
                //从三级缓存中删除
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
为循环依赖做准备

通过lambda表达式创建工厂对象 - “a”,并放到三级缓存singletonFactories中

// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

接下来填充属性populateBean(beanName, mbd, instanceWrapper);

postProcessProperties里有方法metadata.inject(bean, beanName, pvs);会进行属性注入,B也是在这里通过反射fied对象注入的。最后会去到Spring容器中找:beanFactory.getBean();,实际上回去Spring到单例池singletonObjects(一级缓存)找。

然后肯定是找不到B的,又需要去创建bean B,走B的生命周期创建了,也会去为B创建一个工厂对象放到三级缓存中,然后B又需要属性填充了,B又依赖A,所以需要到Spring容器(单例池)中找A,找不到,会通过一级一级找下去,然后在三级缓存中找到。创建A后放到二级缓存中,依赖注入,然后B的生命周期继续走,走完后放到单例池里,从三级缓存的删除掉。

然后回到A的getBean(B),从单例池中获得B,依赖注入,A生命周期继续走,放到单例池中,从二级缓存中删掉。

最后注册到单例池的方法如下:

protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
     //放到单例池中
      this.singletonObjects.put(beanName, singletonObject);
     //从二、三级缓存中删除
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
   }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值