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

依赖注入&循环依赖

依赖注入

依赖注入有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);
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring循环依赖指的是在Spring中,多个Bean之间存在相互依赖的情况。具体来说,当一个Bean A依赖于另一个Bean B,同时Bean B也依赖于Bean A时,就形成了循环依赖。这种情况下,Spring需要解决Bean的创建和依赖注入的顺序问题。 在Spring中,循环依赖问题是由于Bean的生命周期所引起的。Spring的Bean生命周期包括了Bean的实例化、属性注入、初始化以及销毁等过程。当出现循环依赖时,Spring会通过使用&ldquo;提前暴露&rdquo;的方式来解决这个问题。 具体来说,当Spring创建Bean A时,发现它依赖于Bean B,于是会创建一个A的半成品对象,并将其暂时放入一个缓存中。然后,Spring会继续创建Bean B,并将其注入到A的属性中。接着,Spring会继续完成B的创建,并将其放入缓存中。最后,Spring会将A的半成品对象交给B进行依赖注入,完成A的创建,并将其从缓存中移除。 需要注意的是,Spring循环依赖有一定的限制条件。例如,如果Bean A和Bean B都是单例模式,那么它们之间的循环依赖是无法解决的。因为单例模式下,Bean的创建和依赖注入是同时进行的,无法通过缓存来解决循环依赖。在这种情况下,程序员需要手动调整Bean的依赖关系或使用其他解决方案来避免循环依赖的问题。 综上所述,Spring循环依赖是指在Spring中多个Bean之间存在相互依赖的情况。Spring通过使用缓存和提前暴露的方式来解决循环依赖问题,但在某些情况下有一定的限制条件需要注意。&lt;span class=&quot;em&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;em&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;em&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;em&quot;&gt;4&lt;/span&gt;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值