专栏名称: ImportNew
伯乐在线旗下账号,专注Java技术分享,包括Java基础技术、进阶技能、架构设计和Java技术领域动态等。
目录
相关文章推荐
51好读  ›  专栏  ›  ImportNew

Spring 源码分析:Bean 加载流程概览及配置文件读取

ImportNew  · 公众号  · Java  · 2017-04-21 11:59

正文

请到「今天看啥」查看全文


registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.

finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.

finishRefresh();

}

catch (BeansException ex) {

// Destroy already created singletons to avoid dangling resources.

destroyBeans();

// Reset 'active' flag.

cancelRefresh(ex);

// Propagate exception to caller.

throw ex;

}

}

}


每个子方法的功能之后一点一点再分析,首先refresh()方法有几点是值得我们学习的:


1、方法是加锁的,这么做的原因是避免多线程同时刷新Spring上下文


2、尽管加锁可以看到是针对整个方法体的,但是没有在方法前加synchronized关键字,而使用了对象锁startUpShutdownMonitor,这样做有两个好处:


(1)refresh()方法和close()方法都使用了startUpShutdownMonitor对象锁加锁,这就保证了在调用refresh()方法的时候无法调用close()方法,反之亦然,避免了冲突


(2)另外一个好处不在这个方法中体现,但是提一下,使用对象锁可以减小了同步的范围,只对不能并发的代码块进行加锁,提高了整体代码运行的效率


3、方法里面使用了每个子方法定义了整个refresh()方法的流程,使得整个方法流程清晰易懂。这点是非常值得学习的,一个方法里面几十行甚至上百行代码写在一起,在我看来会有三个显著的问题:


(1)扩展性降低。反过来讲,假使把流程定义为方法,子类可以继承父类,可以根据需要重写方法


(2)代码可读性差。很简单的道理,看代码的人是愿意看一段500行的代码,还是愿意看10段50行的代码?


(3)代码可维护性差。这点和上面的类似但又有不同,可维护性差的意思是,一段几百行的代码,功能点不明确,不易后人修改,可能会导致“牵一发而动全身”


prepareRefresh方法


下面挨个看refresh方法中的子方法,首先是prepareRefresh方法,看一下源码:


/**

* Prepare this context for refreshing, setting its startup date and

* active flag.

*/

protected void prepareRefresh() {

this.startupDate = System.currentTimeMillis();

synchronized (this.activeMonitor) {

this.active = true;

}

if (logger.isInfoEnabled()) {

logger.info("Refreshing " + this);

}

}


这个方法功能比较简单,顾名思义,准备刷新Spring上下文,其功能注释上写了:


1、设置一下刷新Spring上下文的开始时间


2、将active标识位设置为true


另外可以注意一下12行这句日志,这句日志打印了真正加载Spring上下文的Java类。


obtainFreshBeanFactory方法


obtainFreshBeanFactory方法的作用是获取刷新Spring上下文的Bean工厂,其代码实现为:


protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {

refreshBeanFactory();

ConfigurableListableBeanFactory beanFactory = getBeanFactory();

if (logger.isDebugEnabled()) {

logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);

}

return beanFactory;

}


其核心是第二行的refreshBeanFactory方法,这是一个抽象方法,有AbstractRefreshableApplicationContext和GenericApplicationContext这两个子类实现了这个方法,看一下上面ClassPathXmlApplicationContext的继承关系图即知,调用的应当是AbstractRefreshableApplicationContext中实现的refreshBeanFactory,其源码为:


protected final void refreshBeanFactory() throws BeansException {

if (hasBeanFactory()) {

destroyBeans();

closeBeanFactory();

}

try {

DefaultListableBeanFactory beanFactory = createBeanFactory();

beanFactory.setSerializationId(getId());

customizeBeanFactory(beanFactory);

loadBeanDefinitions(beanFactory);

synchronized (this.beanFactoryMonitor) {

this.beanFactory = beanFactory;

}

}

catch (IOException ex) {

throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);

}

}


这段代码的核心是第7行,这行点出了DefaultListableBeanFactory这个类,这个类是构造Bean的核心类,这个类的功能会在下一篇文章中详细解读,首先给出DefaultListableBeanFactory的继承关系图:



AbstractAutowireCapableBeanFactory这个类的继承层次比较深,版面有限,就没有继续画下去了,本图基本上清楚地展示了DefaultListableBeanFactory的层次结构。


为了更清晰地说明DefaultListableBeanFactory的作用,列举一下DefaultListableBeanFactory中存储的一些重要对象及对象中的内容,DefaultListableBeanFactory基本就是操作这些对象,以表格形式说明:



XML文件解析


另外一个核心是第10行,loadBeanDefinitions(beanFactory)方法,为什么我们配置的XML文件最后能转成Java Bean,首先就是由这个方法处理的。该方法最终的目的是将XML文件进行解析,以Key-Value的形式,Key表示BeanName,Value为BeanDefinition,最终存入DefaultListableBeanFactory中:


/** Map of bean definition objects, keyed by bean name */

private final Map beanDefinitionMap = new ConcurrentHashMap ();


/** List of bean definition names, in registration order */

private final List beanDefinitionNames = new ArrayList ();







请到「今天看啥」查看全文