Spring Bean

Ioc

ioc遵循了依赖导致原则,是一种思想,他将对象的创建由原本的开发者,通过依赖注入的方式,转移给了ioc容器.

组成

BeanFactory

基础的bean创建工厂,主要是提供了getbean(string name),通过名字获取对象.getType(string name)通过名字获取类型,isSingleton(string name)是否是单例等方法.

ApplicationContext

继承了BeanFactory,并提供了一些获取应用名的方法.属于高阶的IOC容器.

BeanDefinition

javabean对象在容器中的表现形式,包含了类的属性,行为,类型,依赖等功能.

实现过程

1.创建beanFactory,扫描resource包下面的xml文件或者@bean等spring容器注解标记的对象,生产一个beanName为元素的arrayList集合.2.实例化bean遍历beanName集合,查找到bean的依赖关系,以递归的方式,一层一层的去使用beanUtil.instance方法初始化bean.3.根据bean的依赖关系,用递归的方式一步步完成bean的依赖注入.

Bean的生命周期

核心步骤图解

1.AbstractBeanFactory.doGetBean方法

2.getSingleton(beanName)

3.getSingleton(beanName, ObjectFactory<?>)

4.addSingleton()

5.createBean(beanName, mbd, args)

6.doCreateBean() 方法

7.addSingletonFactory()

8.populateBean()

总结

  1. 实例化,为Bean分配内存空间。

  2. 属性赋值,填充属性和依赖注入。

  3. 初始化:

  • 针对实现了Aware接口的bean,执行各种通知。

  • 执行前置方法。

  • 执行初始化方法。(@PostConstruct)

  • 执行后置方法。

  • 使用Bean,在程序中使用Bean。

  • 销毁Bean,执行Bean的销毁方法。(@PreDestroy)

Bean的循环依赖

出现场景

1.构造方法里面.2.成员变量里面.

出现类型

1.单例2.原型

解决思路

spring对单例的成员变量的循环依赖进行了解决.主要思想是使用三级缓存,提前获取到实例对象的引用,后期赋值.

三级缓存结构

一个含有循环依赖的bean加载主要流程

1.A.getSingleton(beanName)从三级缓存中获取,获取不到。

2.递归引入A的依赖。此时A还没有成员属性。

3.A.getSingleton(name,factory)去创建bean:crateBean().

4.createBean的时候,先实例化bean,然后把获取bean的beanFactory放进三级缓存。

5.填充A的属性,此时会B.getBean()。

6.B执行1-4步骤。

7.填充B的成员变量A。

8.这个时候,A.getSingleton(beanName)从三级缓存中获取,可以获取到A的提前引用。通过从三级缓存中获取的objectFactory,创建A的二级缓存对象,此时A只进行了实例化,没有初始化。

9.B完成属性赋值和初始化。

10.B将初始化好的bean放入一级缓存。

11.A将初始化好的B进行属性赋值,然后初始化。

12.A的bean放入一级缓存。

13.完成。

Bean的线程安全

spring里面的bean是线程安全的吗,比如@contrller,@service,@bean等生成的对象.

答:Spring里面的bean是线程不安全的.

说明:spring默认的注入方式是单例,即所有的对象都会生成一个.必然存在着资源竞争.但是spring又是怎么保证不会出现线程问题呢?

spring里面的bean都是无状态的,既然每个对象都没有状态,就不会存在线程问题了.

每个bean只存在着方法的调用,这部分是在虚拟机栈里面完成,jvm的虚拟机栈是线程安全的.因此不会存在线程问题.

需要注意的是,我们在每个bean里面也要遵循这种原则,bean里面不要写入变量,防止出现线程问题.

注意事项:

  1. 不要在@Controller/@Service等容器中变量,默认的单例是线程不安全的.

  2. 不要在@Controller/@Service等容器中定义静态变量,不论是单例(singleton)还是多实例(prototype)他都是线程不安全的.

  3. 如果要使用共享变量的话,使用threadLocal.

spring 只使用二级缓存可以解决循环依赖吗?

如果只使用二级缓存也是可以解决的,使用三级缓存的目的1是为了延迟实例化,2是为了更好的符合aop的设计原则:实例化,属性赋值,初始话,生成aop对象。如果只使用二级缓存,则会破坏这个原则。

Last updated