阅读Spring IoC部分源码有一段时间了,经过不断的单步调试和参阅资料,对Spring容器中bean管理有了一定的了解。这里从bean的几个状态的角度出发,研究下IoC容器。
一、原材料
Xml中的bean定义配置(或者注解)、及Java代码
<bean id="book" name="book" class="com.sky.vo.Book" scope="singleton" init-method="productBook" destroy-method="destroyBook"> <property name="title" value="小王子"/> </bean>
public class Book implements InitializingBean, DisposableBean, BeanFactoryAware, ApplicationContextAware{ private String title; public Book(){ System.out.println("调用了Book默认构造函数"); } public void productBook(){ System.out.println("书本初始化init-method"); } ///....... }
二、半成品
BeanDefinition是Spring中很重要的一个接口,作用是对bean的一种抽象(简单的理解是他的属性对应了<bean>中定义的属性和子节点等信息)。他是半成品,所以我们几乎不会直接使用,而是在IoC容器内部流通!
阅读源码发现是用SAX解析xml,然后保存到BeanDefinition对象。以下是关键的伪代码
/* **BeanDefinitionParserDelegate.java */ public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) { AbstractBeanDefinition bd = createBeanDefinition(className, parent){ GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setBeanClassName(className); return bd; } parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); parseConstructorArgElements(ele, bd); parsePropertyElements(ele, bd); //省略了解析其他子节点的代码 }
核心的解析过程比较简单,创建了GenericBeanDefinition,然后节点属性和<property>、<construct-arg>等设置为他的属性。
那么,还有一个比较关键的步奏,就是转化后的BeanDefinition存放的时机和位置,是在Spring IoC容器启动时存放的,具体代码:
/* **DefaultListableBeanFactory.java */ public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){ synchronized (this.beanDefinitionMap) { Object oldBeanDefinition = this.beanDefinitionMap.get(beanName); if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { throw new BeanDefinitionStoreException("Cannot register bean definition for bean , There is already bound."); } else { this.logger.info("Overriding bean definition for bean, replacing oldBeanDefinition"); } } else { this.beanDefinitionNames.add(beanName); } this.beanDefinitionMap.put(beanName, beanDefinition); } }
可以看到最终存储到了beanDefinitionMap这个Map中,来看看他的定义(DefaultListableBeanFactory.java中)
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
这样,Spring容器就持有了BeanDefinition。
三、成品
获取成品就是获取具体的bean对象,其实就是Object bean = BeanFactroy.getBean(beanName).这里根据scope的不同,会有一些不同,简而言之:
A、singleton在IoC容器初始化时实例化,并缓存到Map中;getBean(singletonBeanName)时从这个Map中取即可;
如果singleton同时lazy-init="true",第一次getBean时缓存到Map,以后从Map中取;
B、getBean(prototypeBeanName)时每次都实例化新的bean对象;
1、singleton的bean
直接看伪代码(截取了部分关键代码)
//从getBean入手 BeanFactroy.getBean("beanName"){ AbstractBeanFactory.createBean{ Object beanInstance = doCreateBean(beanName, mbd, args){ // Eagerly check singleton cache for manually registered singletons. //这是返回了缓存的singleton的bean:com.sky.vo.Book@1a1ecd Object sharedInstance = getSingleton(beanName){ return Object singletonObject = this.singletonObjects.get(beanName); } return (T)bean; } } }
从源码我们可以看到,对于singleton的bean,我们直接从singletonObjects这个Map中取!
public class DefaultSingletonBeanRegistry{ /**缓存了singleton的bean:beanName--->bean实例*/ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64); }
那么bean实例是什么时候存入到这个map的呢?看源码知道是在IoC容器启动时。
通过多次分析源码,我们知道AbstractApplicationContext.refresh()完成了容器的初始化过程,singleton的bean的实例化也是在这完成的。
public void AbstractApplicationContext.refresh(){ //创建Spring容器 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Instantiate all remaining (non-lazy-init) singletons. AbstractApplicationContext.finishBeanFactoryInitialization(beanFactory){ DefaultListableBeanFactory.preInstantiateSingletons(){ RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { //然后,执行了getBean的操作!!!这里执行的代码就是BeanFactory.getBean(beanName)部分 AbstractBeanFactory.getBean(beanName){ AbstractBeanFactory.doGetBean(beanName){ //缓存为空 Object sharedInstance = getSingleton(beanName); //如果是singleton if (mbd.isSingleton()) { DefaultSingletonBeanRegistry.getSingleton(){ synchronized (this.singletonObjects) { //这里缓存也是空的 Object singletonObject = this.singletonObjects.get(beanName); //实例化bean singletonObject = singletonFactory.getObject(){ Object beanInstance = doCreateBean(beanName, mbd, args); } //这里非常重要,把实例化的bean放到map中 addSingleton(beanName, singletonObject){ this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT)); } } } } } } } } } }
截取了部分关键的代码片段,可以看到Spring容器初始化时会把实例化的singleton的bean放到singletonObject