服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - SpringBean依赖和三级缓存的案例讲解

SpringBean依赖和三级缓存的案例讲解

2021-08-16 11:46酒剑随马@ Java教程

这篇文章主要介绍了SpringBean依赖和三级缓存的案例讲解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

spring中的bean依赖有大体上可以分为两类,共3中形式,下面简单介绍一下。

第一类是构造方法中的循环依赖,这种会报错

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Service
public class ServiceA {
  private ServiceB serviceB;
  public ServiceA(ServiceB serviceB) {
    this.serviceB = serviceB;
  }
  public void methodA(){
    System.out.println("a");
  }
}
 
@Service
public class ServiceB {
  private ServiceA serviceA;
  public ServiceB(ServiceA serviceA) {
    this.serviceA = serviceA;
  }
  public void methodB(){
    System.out.println("b");
  }
}
 
//错误提示
Description:
The dependencies of some of the beans in the application context form a cycle:
 
┌─────┐
| serviceA defined in file [C:\demo\target\classes\com\example\demo\ServiceA.class]
↑   ↓
| serviceB defined in file [C:\demo\target\classes\com\example\demo\ServiceB.class]
└─────┘

第二类是field循环依赖,它分为两种,第一类循环依赖的作用域scope默认是singleton,启动不会报错

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class ServiceA {
  @Autowired
  private ServiceB serviceB;
 
  public void methodA(){
    System.out.println("a");
  }
}
 
@Service
public class ServiceB {
  @Autowired
  private ServiceA serviceA;
 
  public void methodB(){
    System.out.println("b");
  }
}

第二种作用域scope为prototype,在这种情况下bean是多例的,按理说这种启动也会报错,但是它成功了。。我也不知道为啥

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
@Scope("prototype")
public class ServiceA {
  @Autowired
  private ServiceB serviceB;
 
  public void methodA(){
    System.out.println("a");
  }
}
 
@Service
@Scope("prototype")
public class ServiceB {
  @Autowired
  private ServiceA serviceA;
 
  public void methodB(){
    System.out.println("b");
  }
}

据我在网上查找的资料,spring可以帮我们处理bean的scope为singleton的field循环依赖,个人感觉应该也是这样,下面说一下它的处理过程。

简单说一下bean的加载过程,当spring启动的时候,首先加载进beanFactory的是beanDefinition,之后会根据beanDefinition判断其是否为sington并且为非抽象类非懒加载,那么之后会去创建bean,

bean的创建分为三步:

1.调用构造方法创建对象实例(这一步完成之后其它对象实例就可以引用它)

2.填充实例内部属性(会依次从三级缓存中获取依赖的bean,如果没有找到,则会先去创建依赖的bean,之后再返回继续填充属性)

3.执行initializeBean方法,进行初始化

当bean进行创建时,会先调用getbean方法->执行doGetBean方法,在doGetBean方法中会调用getSingleton方法,这一步就是从三级缓存中获取对象缓存,因为是刚开始创建bean所以缓存中肯定没有,之后会调用createBean方法,在createBean方法中会调用doCreateBean执行bean的创建过程就是上面的那三步,当bean创建成功之后会将其放入一级缓存之中,此时会将它从三级和二级缓存中删除。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
  }
 
  doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
 
    //从缓存中获取实例
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
      //省略代码
    } else {
      //省略代码
      createBean(beanName, mbd, args);
    }
    //将创建完成的bean放入一级缓存
    addSingleton(beanname,object)
  }
 
  protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
    //省略代码
    doCreateBean()
  }
 
  protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
    //根据构造方法创建对象实例
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    final Object bean = instanceWrapper.getWrappedInstance();
 
    //将对象实例放入第三级缓存中
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    //实例内部属性填充
    populateBean(beanName, mbd, instanceWrapper);
 
    //初始化bean
    exposedObject = initializeBean(beanName, exposedObject, mbd);
 
    return exposedObject;
  }
 
  protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
          SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
          exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
        }
      }
    }
    return exposedObject;
  }

其中我们可以看到当第一步执行完毕后会将刚刚创建的实例放入singletonFactories(第三级缓存)中,那么我们下面了解下到底什么是spring的三级缓存。处于最上层的缓存是singletonObjects,它其中存储的对象是完成创建好,可以正常使用的bean,二级缓存叫做earlySingletonObjects,它其中存储的bean是仅执行了第一步通过构造方法实例化,并没有填充属性和初始化,第三级缓存singletonFactories是一个工场。

?
1
2
3
4
5
6
7
8
/** Cache of singleton objects: bean name to bean instance. */
 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
 
 /** Cache of singleton factories: bean name to ObjectFactory. */
 private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
 
 /** Cache of early singleton objects: bean name to bean instance. */
 private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

其实在getSingleton方法中会首先从一级缓存中获取bean,一级缓存中没有再从二级缓存中获取,二级也没有就会从三级中获取factory当factory不为null时,则会调用getObject方法获取bean,并将bean放入二级缓存,之后再从三级缓存中删除该key-value对,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
 Object singletonObject = this.singletonObjects.get(beanName);
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  synchronized (this.singletonObjects) {
  singletonObject = this.earlySingletonObjects.get(beanName);
  if (singletonObject == null && allowEarlyReference) {
   ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
   if (singletonFactory != null) {
   singletonObject = singletonFactory.getObject();
   this.earlySingletonObjects.put(beanName, singletonObject);
   this.singletonFactories.remove(beanName);
   }
  }
  }
 }
 return singletonObject;
 }

那么到此处我们就可以看出为什么不能在构造方法中存在循环依赖了,假如现在有a、b两个service,它们两个互相在构造方法中循环依赖,当项目启动,创建a的bean时执行第一步通过构造方法创建实例,但是发现依赖b的bean,所以就从三级缓存中获取,但是没发现,那么就先挂起a的创建过程,先去创建b,在b创建过程中,又依赖于a,但是三级缓存中也没有a的bean,这样就进入了一个循环创建的过程,自然是不可取的。

而内部field scope为prototype为何也会报错呢,当scope为prototype每次引用它时都会创建一个新的对象,所以也会存在循环创建的过程。

而默认情况下bean的scope为singleton,整个容器中仅有整个service的一个bean,还是假如a、b两service存在field循环依赖,当创建a的bean时,执行完构造方法后,a的实例已生成,将其factory对象存入第三级缓存singletonFactories中,在填充属性时,发现依赖b的bean,但是在缓存中没有b的bean;因此转而去创建b,在此过程中执行完b的构造方法后将其factory也放入三级缓存,此时执行b的属性填充,发现依赖a,从三级缓存中获取a的对象,并将a放入二级缓存中,之后执行intialize初始化,最后将b的bean转入一级缓存;再继续回来创建a,这个时候发现在一级缓存中已经有了b,那么属性填充成功,进行初始化,最后a也放入一级缓存,至此执行完毕。

那么大家可能会感到疑惑,为什么要使用三级缓存呢,感觉没有singletonFactories使用二级缓存也可以呀?

从前面的代码里可以看到向第三级缓存中放置的是一个objectFactory的匿名实现类,addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)),当我们从singletonFactories中获取objectFctory然后调用getObject方法获取bean的时候,实际就是通过getEarlyBeanReference获取的object,那么进入这个方法看一下。

它在这里会获取所有的beanPostProcessor实现类,然后从中找出实现了SmartInstantiationAwareBeanPostProcessor的beanPostProcessor,然后调用它的getEarlyBeanReference(obgect,beanName)方法,对bean进行处理,然后进行返回,这些实现类中就有aop的核心AbstractAutoProxyCreator,从这里我们就可以看出来,从第三级缓存objectFactory中获取的obejct是经过了处理的一个代理对象,个人理解三级缓存就是为了获取在创建对象的过程中提前对其进行一些扩展操作。

三级缓存实现bean的扩展,将代理对象放入二级缓存中,供其他依赖该bean的对象的使用,如果没有了三级缓存,将bean扩展放在二级缓存中实现,那么如果有bean a被其他多个bean依赖,那么在其他bean填充属性的过程中会多次获取bean a,这个过程中就会多次执行获取bean a代理,就有些多余,而三级缓存结构就是在第三级缓存完成bean的扩展,生成代理对象,放入二级缓存之中,供其他bean获取。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。如有错误或未考虑完全的地方,望不吝赐教。

原文链接:https://blog.csdn.net/swl1993831/article/details/106678932

延伸 · 阅读

精彩推荐