Spring Bean的生命周期
Bean实例的属性填充
注入单向对象引用属性时
情况一,被注入的引用对象先于创建
情况二,被注入的引用对象后于创建
可以看到,情况二的时候,加载xml文件将<bean>包装进beanDefinitionMap后,是按顺序取出bean信息进行创建的,如果创建时需要的引用依赖未被加载进容器,则会暂停当前bean的生命周期,先去创建需要的那个引用依赖对应的bean。
注入双向对象引用属性时
多个实体之间相互依赖并形成闭环的情况就叫做“循环依赖”,也叫“循环引用”。
如下图,getBean("userService")后,先实例化Service,开辟内存给一个地址用于存储半成品的Service,然后继续执行bean的生命周期,对Service初始化,填充引用对象userDao,但是没有userDao,就先暂停userService的生命周期,改去执行userDao的生命周期,但是userDao初始化也要填充引用对象userService,这样看起来就变成了一个死循环的问题。
但其实, 其中一方在填充对方的bean时,可以引用对方bean的内存地址,然后对方生命周期执行完了之后,因为地址在咱这,对方依然是咱的人,这和等对方初始化完了之后再给咱是一样的,最终都是那同一个在实例化之初就开辟好的地址。这样就结束了所谓的死循环,即将半成品的未初始化的bean先存在某处(反正不是单例池)。
真正的解决方法!!!
三级缓存,将完整Bean实例和半成品Bean实例分开存储,当Bean初始化要注入引用类型时,先去完整Bean实例的地方寻找,没有的话再去半成品Bean实例寻找,这样就可以解决循环引用问题。
在DefaultSingletonBeanRegistry的源码中,有:
singletonObjects(我们属性的最终存储单例Bean成品的单例池,在这里被称为一级缓存),
sigletonFactories(早期单例池,缓存已被引用的半成品对象,被称为二级缓存),
earlySingletonObjects(也是早期单例池,缓存创建完毕但初始化未完成且未被引用的半成品对象,被称为三级缓存)
循环依赖解决方案的描述:
Aware接口
要导包
IoC容器实例化Bean整体流程分析
当ApplicationContext容器初始化时,BeanDefinitionReader读取器读取xml文件中的<bean>标签,并将每个<bean>标签的信息抽取封装到BeanDefinition对象中,BeanDefinition对象会被存储到beanDefinitionMap中,最终Spring容器遍历beanDefinitionMap,通过反射创建每一个bean实例对象,把对象存储在singletonObjects单例池中,当执行getBean时,就回去单例池中寻找单例对象。
Bean工厂后处理器
Bean后处理器
Spring整合第三方框架
Spring整合Mybatis
一般使用Mybatis:
准备测试用的数据表
编写实体类对象
编写Mapper接口
编写Mapper对应xml文件
配置mybatis-config配置文件
编写启动类
Spring整合Mybatis:
导入Maven坐标,前两个分别为mysql连接jar和mybatis坐标,后两个为Spring为Mybatis整合进Spring提供的坐标。
目的是将mybatis-config这个Mybatis配置类中配置源以及扫描mapper包配置到xml文件中:
配置数据源:
配置扫描Mapper包:
使用时,在需要mapper接口的地方直接注入即可: