从老项目改造梳理SpringIOC

Spring是做java开发一直会用的框架,其中最重要的一个特性就是IOC控制反转。对比使用Spring框架的项目和未使用Spring框架的项目可以明显察觉项目的复杂程度不一样。其中重要的一个原因是Spring将需要开发者自身维护的对象依赖关系交由SpringIOC容器管理,大大简化了项目开发和维护难度。为了更清楚的理解SpringIOC,在查看Spring Bean加载的部分源码和一些资料后,整理了如下的一些流程和问题。

Spring注入Bean的方式主要有2种:一种是通过加载xml配置文件,另一种是通过注解。加载Xml配置文件是通过ClassPathXmlApplicationContext类来实现的,注解注入Bean是通过AnnotationConfigApplicationContext类来实现的。它们的实现都非常复杂,但主要过程一致。
在这里插入图片描述
图1:ApplicationContext继承图

Spring注入Bean的方式主要有2种:一种是通过加载xml配置文件,另一种是通过注解。加载Xml配置文件是通过ClassPathXmlApplicationContext类来实现的,注解注入Bean是通过AnnotationConfigApplicationContext类来实现的。它们的实现都非常复杂,但主要过程一致。
在SpringIOC中我认为最重要有2点:BeanDefinition(存储Bean的信息)、BeanFactory(Bean工厂,生成bean实例)。
简化的过程是服务在启动后,SpringIOC通过解析xml文件,或者扫描各类@Bean注解(@Service@Repository@Controller其实都是各种不同业务的Bean)生成BeanDefinition类,存在beanDefinitionMap中,再通过BeanFactory将BeanDefinition类的bean实例存放到map缓存中,以便整个生命周期中使用。
在Bean实例化过程中会涉及到判断循环依赖,也会涉及到bean作用域(scope),源码中只定义了单例(singleton)和原型(prototype)的实现,关于request、session、global-session作用域都是属于自定义scope。

在这里插入图片描述

图2:Bean加载流程图

从图2中可以整理出如下Bean初始化流程:
Spring通过BeanDefinitionReader将各种定义的Bean信息转为BeanDefinition类;
在BeanDefinition实例化后,BeanFactory生成该Bean之前,可通过BeanFactoryPostProcessor对beanDefinition进行修改扩展;
BeanFactory根据BeanDefinition定义使用反射实例化Bean,并在map中存放实例化对象;

如何使用Bean?
通过注解或者直接通过ApplicationContext根据Type或者Name来获取类的实例(@Autowired默认通过Type注入,@Resource默认通过Name注入)。跟踪源码可发现,获取Bean也是通过BeanFactory来实例化Bean对象,如果是单例已经初始化过的就直接从缓存中获取,否则就按照启动的初始化流程去创建。

这其中又有一些常见问题:
1.普通Bean和FactoryBean有什么区别?
在获取Bean的时候可以发现如果是普通Bean就直接返回,如果是FactoryBean,要返回它创建的实例对象。
普通Bean:在配置文件中定义的bean类型就是返回类型;
FactoryBean:在配置文件中定义的bean类型是继承了FactoryBean的工厂类,与返回类型不同。

2.bean的作用域有哪些?为什么默认使用singleton,有什么好处和坏处?
作用域描述singleton每个bean对象实例都是单例(默认)prototype任意数量的bean对象实例request每个http请求创建一个实例。只在web-aware Spring ApplicationContext中有效session每个http会话创建一个实例。只在web-aware Spring ApplicationContext中有效global-session全局http会话创建一个实例。只在web-aware Spring ApplicationContext中有效
spring bean默认是单例,只在Spring容器中是单例,并不是在整个jvm中是单例。也就是说如果有多个容器,就会有多个bean实例。
单例的好处在于提高性能:
(1)只用初始化时实例化一次,减少实例化的时间消耗;
(2)减少内存开销,减少jvm垃圾回收
(3)快速获取bean,从缓存中获取bean速度快
单例的坏处在于线程安全性上:
在多线程情况下需要开发自己去实现线程安全性,通过volite或者是ThreadLocal。

3.spring如何处理循环依赖的?
循环依赖主要发生在bean对象的实例化和属性填充过程中。正常情况下,a实体中引用了b实体,那么在属性填充中需要对b进行创建,这时候a并没有创建完,b在属性填充中也需要a实体,就导致都创建不了的问题。
spring增加了3级缓存机制来处理这样的问题。
Spring 容器的整个生命周期中,单例Bean对象是唯一的,即可以使用缓存来加速访问。Spring 源码中使用了大量的 Cache 手段,其中在循环依赖问题的解决过程中就使用了“三级缓存”。
三级缓存的意义:
singletonObject:一级缓存,存放完全实例化且属性赋值完成的 Bean ,可以直接使用;
earlySingletonObject:二级缓存,存放早期 Bean 的引用,尚未装配属性的 Bean,即刚实例化,尚未加载属性,前提是执行了构造器,所以完全使用构造器注入的循环依赖无法解决;
singletonFactories:三级缓存,存放实例化完成的 Bean 工厂。
除了三级缓存,还有另外两个缓存。
singletonsCurrentlyInCreation: bean 在创建的过程中都会存储在此,创建完成移出;
alreadyCreated:存放至少被创建一次的 bean,不会重复。即标记 bean 是否创建完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值