【Spring】Spring源码(一)循环依赖与bean生命周期

Spring循环依赖



在这里插入图片描述

如图,类之间的相互引用就是循环依赖,spring是允许这样的循环依赖(前提是单例的,非构造方法注入的情况下)

原型模式时每次注入bean(例如B)都会创建一个新的bean实例,创建B的实例又会注入bean(C),再创建C的实例则又要创建A的实例。所以原型模式使用循环依赖会直接抛出BeanCurrentlyInCreationException

Spring解决循环依赖

首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存。

  • singletonObjects 俗称“单例池”“容器”,缓存创建完成单例Bean的地方。
  • earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.
  • singletonFactories 映射创建Bean的原始工厂

在这里插入图片描述

图片来源:https://blog.csdn.net/qq_35190492

所谓的循环依赖其实无非就是属性注入,或者自动注入, 故而搞明白循环依赖就需要去研究spring自动注入的源码;spring的属性注入属于spring bean的生命周期一部分。

理解Spring生命周期:

  1. spring bean——受spring容器管理的对象,可能经过了完整的spring bean生命周期,最终存在spring容器当中;一个bean一定是个对象
  2. 对象——任何符合java语法规则实例化出来的对象,但是一个对象并不一定是spring bean;

Spring生命周期

Class--------》beanDefinition----------》bean 与普通对象不同的是bean对象要经过

首先,Java虚拟机将类的class文件加载到方法区,然后扫描是否有注入bean的操作。 对于要注入的,spring会new一个BeanDefination接口(这个接口包括各种对bean的属性判断方法,例如GenericBeanDefinition)的实现类,然后将实现类对象即bean的各种属性(属性例如:beanClassName=“A”, beanClass=A.class,scope=“singleton”,…)put到BeanDefinitionMap的beanDefinitionMap中去,通过finishiBeanFactoryInitialization(beanFactory)方法获取beanDefinitionMap中的beanName开始验证,验证成功后new一个bean对象放到spring单例池SingletonObjects(map类型)中,【注意:原型对象不是在spring初始化的时候new一个bean,而是在getBean的时候】bean对象到单例池之前还可以调用BeanFactoryPostProcessor接口的实现类进行扩展。

具体步骤如下:

1.扫描:扫描是否有bean注入

例如通过xml方式,JavaConfig方式或注解方式的bean注入。

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
 this();
 register(annotatedClasses);//注册配置类
 refresh();// 真正初始化bean的方法
}
2.解析:解析是不是单例模式,是否懒加载…

在一个for循环中 {

​ new一个BeanDefinition类的子类GenericBeanDefinition对象genericBeanDefinition,这个对象包括setBeanClass,setBeanClassName,setScope…等方法。

​ 定义一个BeanDefinitionMap的beanDefinitionMap,将GenericBeanDefinition对象put到beanDefinitionMap<“xxx”,genericBeanDefinition>中。

​ 此外还会定义一个List的beanNames,beanNames.add(xxx),负责存储beanDefinitionMap中的key,用于遍历GenericBeanDefinition对象。

}

3.调用扩展:查看是否赋予额外的扩展功能

如何扩展:

创建一个类实现BeanFactoryPostProcessor接口,继承postProcessBeanFactory方法,方法会传一个beanFactory(就是Spring工厂,Spring工厂中就包括了上一步定义的beanDefinitionMap),通过beanFactory调用getBeanDefinition获取beanDefinitionMap中的BeanDefinition对象。

// 扩展实现,更改beanClass
@Component	//加上才会扫描到,否则不起作用
public class test implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        GenericBeanDefinition bd = beanFactory.getBeanDefinition("A");
        // 这里写一些扩展功能,比如更改beanName,禁止循环依赖...
        bd.setBeanClass(B.Class);
    }
}
// getBeanDefinition内部方法
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
    // 在这里获取map中的beanDefinition对象,beanName未声明则默认为自动驼峰命名。
    BeanDefinition bd = this.beanDefinitionMap.get(beanName);
    if (bd == null) {
        if (this.logger.isTraceEnabled()) {
            this.logger.trace("No bean named '" + beanName + "' found in " + this);
        }
        throw new NoSuchBeanDefinitionException(beanName);
    }
    return bd;
}
4.验证:根据第二步解析获得的属性判断要不要new一个Bean

具体判断bean是不是原型的;装配模式是byType还是byName;构造方法有没有参数,参数合不合理;

合理就实例化Bean:new一个 Bean对象到Spring的单例池SingletonObjects(Map类型)中。

验证代码

finishBeanFactoryInitialization

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 。。。
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}

preInstantiateSingletons

public void preInstantiateSingletons() throws BeansException {
		//。。。
 	// 取出beanDefinitionMap存放的beanNames
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

		// 遍历并验证beanNames,进行实例化
		for (String beanName : beanNames) {
         // 验证是不是单例、懒加载。。。
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				//。。。
             //实例化方法
              getBean(beanName);
              //。。。
			}
		}
	}

说明

// 实例化所有还剩下的、非懒加载的单例对象
finishiBeanFactoryInitialization(beanFactory);
// 内部继续调用preInstantiateSingletons方法
beanFactort.preInstantiateSingletons();
// 获取第二步存入List的beanNames,进行遍历验证,例如是不是单例,是否懒加载...等,验证之后开始实例化
getBean(beanName);
// 内部调用doGetBean方法
5.实例化:实例化Bean

一级缓存: private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

二级缓存: private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

三级缓存: private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

getBean

public Object getBean(String name) throws BeansException {
	return doGetBean(name, null, null, false);
}

doGetBean

protected <T> T doGetBean(final String name, 。。。) throws BeansException {
	// 验证beanName合法性
final String beanName = transformedBeanName(name);
// 检查单例池中是否已经有了这个bean
Object sharedInstance = getSingleton(beanName);
// 若单例池中没有此bean,
if (sharedInstance != null && args == null) {...}
else {
  // 判断这个类是不是在创建过程中,是则抛出异常
  if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
  }
}

//继续进行一系列验证...

// 如果是单例
if (mbd.isSingleton()) {
  //...
  // bean开始创建
	return createBean(beanName, mbd, args);
}
//...
}

getSingleton //判断单例池中是否已经有了这个bean

/** 单例对象缓存/单例池(beanName->bean实例): bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);
	// 如果单例池中没有这个bean,且当前bean正在创建,则去三级缓存中取bean。
 // 循环依赖中,第一次初始化bean时‘正在创建’条件不成立,当循环到第二次初始化bean时,‘正在创建’条件成立
 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     // 条件一旦成立,就会从三级缓存中取出bean,移到二级缓存中
     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;
}

createBean

protected Object createBean(String beanName, RootBeanDefinition mbd, ...)throws BeanCreationException {
//...
RootBeanDefinition mbdToUse = mbd;
//...
// 第一次调用后置处理器,允许用代理对象替换目标对象
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// ...
// 进入doCreateBean方法创建bean实例
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}

doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {
// 实例化bean
BeanWrapper instanceWrapper = null;
// 单例的,则从bean工厂里移除
if (mbd.isSingleton()) {
  instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
  // 实例化对象,createBeanInstance里第二次调用后置处理器(通过构造方法反射实例化对象)
  instanceWrapper = createBeanInstance(beanName, mbd, args);
  //。。。
  // 第三次后置处理器,修改合并的bean定义
  applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
  //。。。
  // 是否允许循环依赖(单例的&&允许循环依赖&&正在创建)【allowCircularReferences默认为true】
  // 可以通过setAllowCircularReferences(false);设置不支持循环依赖
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
			//。。。
      // 第四次调用后置处理器,判断是否需要AOP。将未完成的bean存入三级缓存。
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
  // 初始化bean实例
  Object exposedObject = bean;
		try {
      // 填充属性,即自动注入。内部完成第五,第六次后置处理器调用
       	//注入属性时就会发现循环依赖的bean没有。
			populateBean(beanName, mbd, instanceWrapper);
      // 初始化Spring。内部完成第七,第八次后置处理器调用
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
}
}

============================


  • Spring是如何默认支持循环依赖的?

    比如说要在A类中注入B,B类中注入A,在Spring生命周期的doGetBean方法会将A放入表示正在创建的集合中。到了doCreateBean方法中,会通过allowCircularReferences属性判断Spring容器是否允许循环依赖,allowCircularReferences属性默认为true,所以Spring容器是默认开启循环依赖的。当判断允许循环依赖时,Spring会将未完成的bean:A存入第三级缓存singletonFactories中。第二次循环创建B注入A时,到doGetBean方法中会调用getSingleton方法判断单例池,也就是第一级缓存singletonObjects中是否有bean:A,如果没有且A正在创建,就会先从二级缓存earlySingletonObjects 中取,二级取不到就从第三级缓存中取出bean移到第二级缓存中,并清除三级缓存中的这个bean。

    • 第一次循环时要注入的bean刚开始创建,到了第二次循环才会判断出这个bean正在创建

    • 可以手动调用初始化spring环境的主要方法refresh(),并在此之前使用setAllowCircularReferences(false)关闭循环依赖。

    • 也可以通过扩展功能,实现BeanFactoryPostProcessor接口,获取BeanDefinition对象,设置属性关闭循环依赖。

    • 二级缓存earlySingletonObjects 的作用:出现循环依赖时,保存暴露的早期对象,防止工厂重复创建对象,提升性能。

      参考:

      https://blog.csdn.net/u012098021/article/details/107352463/

  • @Autowired与@Resource有什么区别?

    @Autowired默认使用byType,匹配不到bean时再使用byName,此时的name是属性的名(xml中的byName是根据set方法名)。可以使用@Qualifier()指定bean的id。默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) 。

    @Resource默认使用byName,当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。可以@Resource(name = / type =)指定name与type。

    @Autowired是由后置处理器AutowiredAnnotationBeanPostProcessor类解析的

    @Resource是由后置处理器CommonAnnotationBeanPostProcessor类解析的

  • Spring原理(IOC与AOP的理解)?

    ​ spring框架最核心的就是ioc和AOP了,其中ioc控制反转是一种设计模式,主要为了降低代码的耦合度,主要使用依赖注入实现。创建对象不再用new,而是通过xml、注解、Javaconfig的方式装配bean到ioc容器中,再通过构造函数方式、setter方法或@Autowired/@Resource注解方式注入到需要依赖的类中,从而实现将类和类之间的依赖关系交给spring容器去管理。

    ​ AOP面向切面编程就是将系统性的服务,比如说日志模块,安全模块,事务管理等通过自定义注解、切面类和反射实现切面化,并用声明的方法将他们应用到他们需要影响的组件中去,从而使业务组件具有更高的内聚性,只需关心自身的业务,而不用为了实现系统性的服务导致代码复杂。

  • Spring Bean的生命周期?

    1. 首先,Java虚拟机将类的class文件加载到方法区,然后扫描是否有注入bean的操作,进行Bean的实例化。对于要注入的,spring会new一个BeanDefination接口(这个接口包括各种对bean的属性判断方法,例如GenericBeanDefinition)的实现类
    2. 实例化后对bean进行属性注入。然后将实现类对象即bean的各种属性(属性例如:beanClassName=“A”, beanClass=A.class,scope=“singleton”,…)put到BeanDefinitionMap的beanDefinitionMap中去,通过finishiBeanFactoryInitialization(beanFactory)方法获取beanDefinitionMap中的beanName开始验证
    3. 初始化之前实现BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口,获取bean的name,类加载器,BeanFactory容器等资源。
    4. 再实现ApplicationContextAware接口,返回当前的ApplicationContext应用上下文对象。
    5. 实现BeanPostProcessor接口,bean初始化之前执行postProcessBeforeInitialization()方法。【bean对象到单例池之前还可以调用BeanFactoryPostProcessor接口的实现类进行扩展。验证成功后new一个bean对象放到spring单例池SingletonObjects(map类型)中】
    6. 实现InitializingBean接口,调用afterPropertiesSet()方法初始化bean。如果bean使用init-method声明了初始化方法,该方法也会被调用
    7. 实现BeanPostProcessor接口,bean初始化之后执行postProcessAfterInitialization()方法。
    8. 此时,Bean已初始化完成,可以被应用程序使用。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
    9. 实现DisposableBean接口,执行destory()方法,销毁bean。如果bean使用了destory-method 声明销毁方法,该方法也会被调用。

    ​ 【注意:原型对象不是在spring初始化的时候new一个bean,而是在getBean的时候】

    ​ 【

    • BeanPostProcessor

    • InstantiationAwareBeanPostProcessor

      Spring扩展中最重要的两个接口!InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。】

      【 Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!】

      参考:

      https://www.jianshu.com/p/1dec08d290c1

      https://www.cnblogs.com/javazhiyin/p/10905294.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值