关于IOC容器的初始化,结合以前SpringMVC的demo,对其过程进行一个相对详细的梳理,主要分为几个部分:web
答:直接本身从源码看第一遍,会有一个初步的认识;可是看完以后,会由于没有实际走一遍执行而没法验证本身认知的过程;另外就是:由于基于接口自己会有多个实现,所以在不少状况下,经过Ctrl+B直接会进入到接口中,可是方法的实现体为空,经过Ctrl+Alt+B查看具体实现,会出现多个类都实现了这个方法,具体是哪个,没法准确肯定,容易致使理解失误spring
从SpringMVC的demo中的web.xml配置项能够看到:express
<listener>
<listener-class>app
org.springframework.web.context.ContextLoaderListeneride
</listener-class>
</listener>post
从ContextLoaderListener进入,这是入口,以后会通过refresh()过程,refresh()过程当中会将IOC容器初始化完成,其中重点有:load阶段、process阶段、register阶段;接下来会从入口以及后续的三个阶段进行介绍。ui
进入到ContextLoaderListener类,ContextLoaderListener实现了ServletContextListener接口,该接口中有两个default方法,分别是:this
//接收Web应用程序初始化过程正在启动的通知spa
default public void contextInitialized(ServletContextEvent sce) {}debug
//接收ServletContext即将关闭的通知
default public void contextDestroyed(ServletContextEvent sce) {}
ContextLoaderListener类中未保持原有的default,从新作了实现:
/** * Initialize the root web application context. */ @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); }
而后进入到父类ContextLoader类的initWebApplicationContext方法中,该方法中会继续经过:configureAndRefreshWebApplicationContext
最终调用到:AbstractApplicationContext中的refresh(),至此进入到IOC的load阶段(下方为abstractApplicationContext的类图)
进入到重要逻辑分析 ——》ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
一步步往里走,到AbstractRefreshableApplicationContext中,具体以下:
protected final void refreshBeanFactory() throws BeansException { //判断是否已经有beanFactory了,有则销毁 if (hasBeanFactory()) { //销毁Beans destroyBeans(); //将BeanFactory的全部相关内容所有置为null,其中包含: //serializedID为null,而且对象引用自己置为null //即等GC了 closeBeanFactory(); } try { //建立BeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //加载BeanDefinitions loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
从这里的createBeanFactory()跟进去能够进入看到是new了一个DefaultListableBeanFactory
DefaultListableBeanFactory的类图以下:
以后下一步loadBeanDefinitions(beanFactory)——》XmlWebApplicationContext类的loadBeanDefinitions
中(这里就是会有多个实现的状况出现,直接经过demo单步调试跟进肯定程序走向),以后会new一个XmlBeanDefinitionReader实例,是之前面new出来的beanFactory为参数,这个xmlReaderDefinitionReader会做为参数传入到loadBeanDefinitions(XmlBeanDefinitionReader reader)方法中,以后一级一级进行转换,最终进入到:AbstractBeanDefinitionReader类中的LoadDefinitions,这个时候参数通过了前面的各类过程的转换,最终进入到了doLoadBeanDefinitions(inputSource, encodedResource.getResource())
这里作了层层封装,每一次从外层进入到里层的具体实现和调用上,主要变化的是:参数形式,能够看到:从getConfigLocations()获得配置文件的路径,默认路径是:
/** Default config location for the root context. */ public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
再到基于获得的配置路径,对其进行循环loaderDefinitions,在每一层循环中,是针对单一的一个String configLocation,以后将String类型的location转换成Resource,再转换成InputStream,以后转成InputSource,最终在doLoadBeanDefinitions中将直接用InputSource的实例做为参数,层层递进层层转换:
//真正从特定XML文件中加载bean definitions的方法(这里将exception的内容没有所有放进来) protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //加载————》获得一个doc对象。其实最后一个是DOMParser类的对象 //至此,整个过程当中不断去将File先解析以后再转换成输入流,再最终获得Document doc,加载完成 Document doc = doLoadDocument(inputSource, resource); //注册:具体作的事项是: //一、解析xml //二、将解析获得的BeanName和beanDefinition都存储到beanDefinitionMap中进行注册 int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; }
其中,会先doLoadDocument方法调用获得一个DomParser的对象;至此加载完成;而后进入到registerBeanDefinitions的过程,其中:会有process过程 + register过程
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //new一个BeanDefinitionDocumentReader对象 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //注册BeanDefinitions //使用documentReader实例调用registerBeanDefinitions方法,具体操做内容包含: //(1)BeanDefinitionDocumentReader处理Document元素时,将Document元素的解析工做委托给 // BeanDefinitionParserDelegate处理,其中会将beanName作一个惟一值的转换,为后续的register到map中提供前提 //(2)判断BeanDefinitionMap中是否有某个k,v已经存在,有的话更新Definition的v值,无则直接put k,v //DefaultBeanDefinitionDocumentReader 实现了 BeanDefinitionDocumentReader接口 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
其中process过程在registetBeanDefinitions跟进到下一步中,即:DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法,看到:doRegisterBeanDefinitions方法,看到这里就能够兴奋一下啦,真正作事情的来了:
其中关键步骤就两部分:第一部分是createDelegate并赋值给delegate字段;以后进行processxml三步曲 preprocess、parse、postprocess
protected void doRegisterBeanDefinitions(Element root) { //先赋值给parent,以后建立createDelegate从新赋值给delegate BeanDefinitionParserDelegate parent = this.delegate; //在DefaultBeanDefinitionDocumentReader处理Document元素时, // 将Document文档内元素具体解析工做委托给BeanDefinitionParserDelegate类来处理 this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //预处理Xml preProcessXml(root); //解析BeanDefinitions parseBeanDefinitions(root, this.delegate); //后置处理Xml postProcessXml(root); this.delegate = parent; }
进入到parseBeanDefinitions(root, this.delegate)中,跟到具体实现中:
//处理给定的bean节点,解析bean definitions,而且经过registry进行注册 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //这个BeanDefinitionHolder从命名上来看: //分析含义是:用来包裹存储一个BeanDefinition的容器,由于叫作Holder //实际是:确实是用来存储这个BeanDefinition的包裹,其中对beanName作了一个特定处理,将之转换成了惟一值 //而且进行详细的解析,包含:对于set、list、map等 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. //获得了一个自认为完备的beanDefinition了,而后进行注册 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
从上面代码中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())会进入到类BeanDefinitionUtils对static方法registerBeanDefinition的调用:
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); //根据name进行注册,由于name是惟一性的了 //具体里面就是直接调用了map.put,将其放入到beanDefinitionMap中 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
以后跟进到registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()) 最终会进入到DefaultListableBeanFactory类中:
摘取方法中的关键逻辑:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //从map中取出beanName的key对应的Definition的对象 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { } //为map中的beanName的key值从新设置对应的v值 this.beanDefinitionMap.put(beanName, beanDefinition); }
其中beanDefintionMap的定义以下:
/** Map of bean definition objects, keyed by bean name. */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
2、将以上梳理的整个过程经过时序图的方式,更清晰地展示出调用关系:(图片若没法看清的,能够查看大图或者下载)