IOC容器初始化过程
发起是上一节课,的refresh()来启动的。启动会进行,BeanDefinition的Resouce定位、载入和注册三个基本过程。(spring把三个过程分开了,在不同的模块完成,然后组合起来,这样就可以更加灵活的剪裁和扩展)。
1.Resource定位,它由ResourceLoader通过统一的Resource接口完成。
具体实现在上一节写过代码。获得配置文件的resource,通过reader将resource和factory结合。这样提现出applicationContext的好处了。有具体的FileSystemXmlApplicationContext这就是读取系统文件等。
先看一下该类继承那些类。
由于已经继承了AbstractXmlApplicationContext类(最上层是Loader),所以具备了resourceLoader读入BeanDefinition的能力。
FileSystemXmlApplicationContext的构造器允许传入string (Resource的文件路径)或数组(多个),已经其他IOC。refresh参数,是指定是否刷新将beanDefinition载入。
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
这个方法是在 BeanDefinitionReader类的loadBeanDefinition方法(使用的是模板模式,具体实现是由子类来完成的,也就是这里的getResourceByPath方法实现的)中调用的。
那么FileSystemXmlApplicationContext类是在父类AbstractRefreshableAppicationContext类中,定义BeanDefinitionReader从而完成bean读入。
AbstractRefreshableAppicationContext的 refreshBeanFactory方法。它被FileSystemXmlApplicationContext构造方法中refresh()调用。
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) { //如果已经建立了BeanFactory,则销毁并关闭beanFactory
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory(); //通过此方法构建了一个ioc容器来供applicationContext使用
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory); //载入BeanDefinition 启动
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
createBeanFactory,getInternalParentBeanFactory是在AbstractAppicationContext实现获取双亲的IOC容器。
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
BeanDefinitionReader是顶层父类,重载了很多loadBeanDefinitions方法。实现是在AbstractBeanDefinitionReader类。但是最终调用的还是一个方法。
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader(); //使用的是DefaultResourceLoader
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
//这里对Resource的路径解析。比如设定的各种ant格式的路径定义,得到需要的resource集合
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// 同样是完成resource定位
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
getResource方法 看DefaultResourceLoader是如何实现的。
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); if (location.startsWith("/")) { return getResourceByPath(location); } //如果路径带有classpath,进行下面的处理 else if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // Try to parse the location as a URL... 处理URL定位 URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { // No URL -> resolve as resource path. 如果都不是就交给子类实现 getResourceByPath方法 我们这里就是FileSystemXXX.实现
}}}return getResourceByPath(location);
2.BeanDefinition的载入,将用户定义好的bean表示成IOC的数据结构(BeanDefinition)。
通过HashMap<BeanDefinition>来维护的。
以DefaultListableBeanFactory为例,看IOC如何载入。
IOC初始化是调用refresh(),该方法是在父类,AbstractAppicationContext类中实现的。其中包含:BeanFactory更新、MessageSource和PostProcessor的注册。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // 子类中启动refreshBeanFactory()的地方 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // 设置BeanFactory的后置处理 postProcessBeanFactory(beanFactory); // 调用BeanFactory的后处理器,这些后处理器是在Bean定义中向容器注册 invokeBeanFactoryPostProcessors(beanFactory); // 注册Bean的后处理器,在Bean创建过程中调用 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster(机制) for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.初始化其他的特殊bean onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. 初始化所有的单件 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. 发布容器事件,结束refresh过程 finishRefresh(); } catch (BeansException ex) { logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex); // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
之后看,AbstractRefreshableApplicationContext的refreshBeanFactory()方法(该方法在前面提到过)
上面讲的loadBeanDefinitions方法,是在AbstractXmlApplicationContext中实现,(存在2个方法,重载,另一个),初始化了读取器XmlBeanDefinitionReader。protected final void refreshBeanFactory() throws BeansException { //是否已经存在IOC容器,如果存在销毁关闭。保证refresh之后是新的,更新bean if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建IOC容器 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //启动对BeanDefinition的载入 loadBeanDefinitions(beanFactory); //看参数,下面介绍存在一个重载方法,才是真正使用的loadBeanDefinition synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
该方法就是实现了reader(beanfactory),然后将beanDefinition设置到Factory
最终还是reader获取了resource然后进行一个配置的。所以最后的载入还是看Reader
AbstractBeanDefinitionReader ,已经为BeanDefinition做好了准备。我们调用的就是这个类的loadBeanDefinitions方法。
具体的读入方式,看子类。比如XmlBeanDefinitionReader。就继承了上面的Reader。并重写了方法。public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { //这里是resource...,是多个文件。 Assert.notNull(resources, "Resource array must not be null"); int counter = 0; for (Resource resource : resources) { //这里的load方法,就是后面我们在子类重写的方法 counter += loadBeanDefinitions(resource); } return counter; }
继续调用的就是读入xml的流然后进行处理了。之后是由BeanDefinitionParserDelegate完成。public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }
之后就是如何读取xml然后进行处理的流程。如何按照spring的Bean语义要求进行解析转化成内部数据结构,代码较多。这我放在下一节中
3.向IOC容器注册BeanDefinition。调用BeanDefinitionRegistry接口来实现的。注入到一个Map中,进行管理。
不包含bean依赖注入的实现。它与载入是两个独立的过程。
依赖注入一般发生在第一次通过getBean获取Bean,或者设置lazyinit属性。(是否想起了hibernate的懒加载?)
此时BeanDefinition信息以及在容器简历自己的数据结构,和表示。但还不能在容器直接使用,需要注册。
在DefaultListableBeanFactory中,定义了MAP
/** Map of bean definition objects, keyed by bean name */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
注册的步骤:
1.在DefaultListableBeanFactory实现了 BeanDefinitionRegistry的接口。这个接口就是来注册的。其实很简单,就是将BeanDefinition设置到map中,如果遇到同名的BeanDefinition,根据allowBeanDefinitionOver配置来完成的。
XmlBeanDefinitionReader,通过registerBeanDefinitions(Document doc, Resource resource)方法调用。BeanDefinitionDocumentReader的registerBeanDefinitions方法