发现这样debug到哪说到哪好像有点回不来了~让我重新理下思路,主要步骤先上图,有什么不同意见欢迎批评教育~

(一)spring IOC的主要步骤都在refresh()这个方法中,我给出了自己的理解注释

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 解析前准备工作,主要为打log,设置active标志
prepareRefresh(); // 对xml文件进行解析工作,并把解析后的BeanDefintiton定义信息保存入容器
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 在上下文中用容器之前的准备工作,主要为对DefaultListableBeanFactory beanFactory的参数设置
            prepareBeanFactory(beanFactory);

            try {
// beanFactory添加后处理器
postProcessBeanFactory(beanFactory); // 调用beanFactoryPostProcessor接口实现类,并放入容器
invokeBeanFactoryPostProcessors(beanFactory); // 实例化实现beanPostProcessor的类,并放入容器
registerBeanPostProcessors(beanFactory); // 实例化MessageSource接口实现类,没有会实现默认实现类,用来实现国际化
initMessageSource(); // 实例化ApplicationEventMulticaster事件广播器,没有会创建默认类,应该是用来给监听的类发送广播吧
initApplicationEventMulticaster(); // 实例化一些特殊的类
onRefresh(); // 检查监听器并注册他们
registerListeners(); // 实例化所有非懒加载的单例对象,处理实现了Aware相关接口的类
finishBeanFactoryInitialization(beanFactory); // 结束容器的更新,初始化lifecycleProcessor接口实现类,广播容器更新完毕事件
finishRefresh();
} }
}

(二)接下来讲解几个我认为比较重要的部分,注意整篇随笔是根据方法的调用顺序一个个讲下去的

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}

1、obtainFreshBeanFactory()方法,注释里有说明他的作用,具体深入下,主要操作在refreshBeanFactory()中,对beanFactory处理完成后getBeanFactory()去获得处理完成的beanFactory返回。

    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);
}
}

2、进入refreshBeanFactory()方法,先是检查是否已经存在容器,第一次进都是没有的,之后进入红色字体方法createBeanFactory(),创建默认的beanFactory容器,设id和定制化操作后开始进入加载bean描述信息的loadBeanDefinitions()方法。

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 为beanFactory创建XmlBeanDefinitionReader即xml阅读器
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 初始化阅读器的一些配置,比如加载环境信息,加载容器本身等等,由下面提供的运行调用截图可以看出,虽然在不同的子类调用方法,其本质都是XmlWebApplicationContext容器在运行
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 允许子类提供定制的阅读器进行初始化,之后处理加载bean描述信息
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);

3、

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}

接上面的方法,loadBeanDefinitions()方法开始加载xml信息了,此方法后也算结束了一阶段xmlWebApplicationContext容器的使用,进入阅读器解析xml阶段

此图可以看出从XmlWebApplicationContext到XmlBeanDefinitionReader的转换。

4、

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//省略。。
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
  //省略。。
}
    @Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
counter += loadBeanDefinitions(resource);
}
return counter;
}
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  //省略
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
    //省略
}

我尽量把它进入的一个个步骤都列出来,以免看客觉得怎么突然到了这个方法,但还是会省略一些细节,比如进入上述最后一个方法后,会对encodedResource进行保存操作等等,这里只列出特别主要的部分。上述操作进行了对资源的编解码,这么多方法的层层进入都还是准备工作,主要工作的方法为doLoadBeanDefinitions().

5、

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
       //加载资源信息成Document对象
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
  //开始
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
     //创建beanDefinition文档阅读器
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(getEnvironment());
     //获取DefalutListableBeanFactory中已经被加载的beanDefinition资源的多少
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//这个方法是在DefalutListablebeanFactory中进行的,可以看出bean描述信息被保存在了这个类中
@Override
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
}
  //这个方法开始对节点解析了,之前创建的文档阅读器开始发挥作用,
  //开始从XmlBeanDefinitionReader类处理转向DefaultBeanDefinitionDocumentReader类处理(BeanDefinitionDocumentReader的实现类)看下图
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}

6、

   protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
     //创建beanDefinition解析代理
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);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
     //根节点前置处理,预留子类实现
preProcessXml(root);
     //使用代理,解析bean定义
parseBeanDefinitions(root, this.delegate);
     //根节点后置处理,预留子类实现
postProcessXml(root); this.delegate = parent;
} protected BeanDefinitionParserDelegate createDelegate(
XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) { BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
     //初始化代理,查询根节点的各项默认属性并赋给代理文档默认定义的类DocumentDefaultsDefinition,这些属性包括lazy-init,autowire,init-method,destory-method等等
        delegate.initDefaults(root, parentDelegate);
return delegate;
}

7、

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
       //这里面开始对root根节点下的元素节点遍历
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
  //这里主要是对context:component-scan,mvc:annotation-driven等特别功能进行解析,同时对注解的类进行相应功能的操作,解析出对应的beanDefinfition
    public BeanDefinition parseCustomElement(Element ele) {
return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
     //取得xml文件命名空间url
String namespaceUri = getNamespaceURI(ele);
     //初始化命名空间处理器
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
     //根据传入元素获得相应的beanDefinition解析器解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

8、对另一个解析方法parseDefaultElement()再进行探讨,它主要用来解析我们自定义的<bean>、<import>、<alias>、<beans>标签

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
     //这块代码很清晰的表示了对各明确元素的处理
     //对improt元素处理
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
     //对alias元素处理
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
     //对bean元素处理
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
     //对beans元素处理
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}

9、就以bean处理为例子吧,对bean的处理也是最复杂的

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
     //解析bean,取得bean信息持有者
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
       //包装bean,额外操作
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 注册被包装好的最终的beanDefintion
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

10、由代理对bean元素解析

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
     //根据英文名也很好理解,获取id和name
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);     //省略 String beanName = id;
//省略
     //对bean元素各方面进行解析,里面有很多细节处理,它的描述信息主要在这里生成
     //在这里我们获得的描述信息有:scope是否单例,isAbstract,lazy-init,autowire,dependCheck,depend-on,autowire-candidate
     //primary,init-method,destory-method,factory-method,factory-bean,className,parent,description,look-up,replace-method,
     //构造方法的name,type,index,属性的name,value等等,还设置extractSource,额外的资源供使用者扩展使用
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
 //省略(若没有beanName则系统生成,有别名获取别名)
       //创建beanDefinitionHolder这个bean信息持有者类,将beanDefinition等信息传进去,并返回
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
} return null;
}

11、正式注册处理完的beanDefinition信息,调用registerBeanDefinition()方法

    public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // 用beanName作为key值,注册beanDefinition信息
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 如果有别名,注册别名和beanName绑定
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
    @Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
     //省略(可靠性判断)
     //这里主要就是把beanDefinition放入Map集合中由容器保存,这里省掉了对是否有旧的同名beanName的判断
this.beanDefinitionMap.put(beanName, beanDefinition);  //省略
}

到此为止就把bean信息全部解析注册完成了,至于最后一步fireComponentRegistered()方法,是对BeanDefinition再做了一层包装,然后通知监听器,spring没有再进行处理,此处是用来给用户扩展用的。

(pass:对bean的解说只是加载了一个bean的全部信息,执行完fireComponentRegistered()方法后,又进入了元素节点的循环解析,直到全部解析完成,XmlWebApplicationContext将保存存储有beanDefiniton信息的DefaultListableBeanFactory beanFactory,至此obtainFreshBeanFactory()方法才算运行完毕)

对spring web启动时IOC源码研究(二)的更多相关文章

  1. 对spring web启动时IOC源码研究

    研究IOC首先创建一个简单的web项目,在web.xml中我们都会加上这么一句 <context-param> <param-name>contextConfigLocatio ...

  2. Chrome自带恐龙小游戏的源码研究(二)

    在上一篇<Chrome自带恐龙小游戏的源码研究(一)>中实现了地面的绘制和运动,这一篇主要研究云朵的绘制. 云朵的绘制通过Cloud构造函数完成.Cloud实现代码如下: Cloud.co ...

  3. spring mvc 启动过程及源码分析

    由于公司开源框架选用的spring+spring mvc + mybatis.使用这些框架,网上都有现成的案例:需要那些配置文件.每种类型的配置文件的节点该如何书写等等.如果只是需要项目能够跑起来,只 ...

  4. Nginx源码研究二:NGINX的事件处理概论

    NGINX作为服务端的应用程序,在客户端发出数据后,服务端在做着这样一些处理,数据先会经过网卡,网卡会和操作系统做交互,经过操作系统的协议栈处理,再和不同的应用程序交互. 在这里面涉及两个概念,一个是 ...

  5. 一文读懂Spring动态配置多数据源---源码详细分析

    Spring动态多数据源源码分析及解读 一.为什么要研究Spring动态多数据源 ​ 期初,最开始的原因是:想将答题服务中发送主观题答题数据给批改中间件这块抽象出来, 但这块主要使用的是mq消息的方式 ...

  6. Chrome自带恐龙小游戏的源码研究(三)

    在上一篇<Chrome自带恐龙小游戏的源码研究(二)>中实现了云朵的绘制和移动,这一篇主要研究如何让游戏实现昼夜交替. 昼夜交替的效果主要是通过样式来完成,但改变样式的时机则由脚本控制. ...

  7. Chrome自带恐龙小游戏的源码研究(一)

    目录 Chrome自带恐龙小游戏的源码研究(一)——绘制地面 Chrome自带恐龙小游戏的源码研究(二)——绘制云朵 Chrome自带恐龙小游戏的源码研究(三)——昼夜交替 Chrome自带恐龙小游戏 ...

  8. Spring IOC 源码分析

    Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文 ...

  9. Spring Ioc源码分析系列--Ioc的基础知识准备

    Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ...

随机推荐

  1. dubbo调用负载均衡

    dubbo负载均衡的地址:http://dubbo.io/books/dubbo-user-book/demos/loadbalance.html 随机策略: public class RandomL ...

  2. BZOJ 2822: [AHOI2012]树屋阶梯 [Catalan数 高精度]

    2822: [AHOI2012]树屋阶梯 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 779  Solved: 453[Submit][Status] ...

  3. 【转】JavaScript的三种消息框alert,confirm,prompt

    1. alert是弹出警告框,在文本里面加入\n就可以换行.2. confirm弹出确认框,会返回布尔值,通过这个值可以判断点击时确认还是取消.true表示点击了确认,false表示点击了取消.3. ...

  4. iOS学习——UIView的研究

    在iOS开发中,我们知道有一个共同的基类——NSObject,但是对于界面视图而言,UIView是非常重要的一个类,UIView是很多视图控件的基类,因此,对于UIView的学习闲的非常有必要.在iO ...

  5. Vue脚手架(vue-cli)安装总结

    单页Web应用(single page web application,SPA),就是只有一张Web页面的应用,是加载单个HTML 页面并在用户与应用程序交互时动态更新该页面的Web应用程序. 提供一 ...

  6. [原]开源的视频转换器,支持gpu,绝对好用ffmpeg的GUI==》dmMediaConverter最新版本2.3

    dmMediaConverter is a crossplatform FFmpeg frontend (GUI) exposing some of its features. It is inten ...

  7. 安装基于 Linux 发行版的重要事项(流程指引)

    安装基于 Linux 发行版的重要事项(Install important issues based on the Linux distribution. (Process guidance)) 1. ...

  8. python如何讲一个文件中的图片分到两个

    最近在做一个图像分类的比赛,作为初次接触深度学习的菜鸟,上手了keras.说实话,除了keras教程,中文博客的技术支持太差了.正在头大的学习中...废话不多说,记录一下学习中的一些小细节.在遇到ge ...

  9. 自动创建字符设备,不需mknod

    自动创建设备文件 1.自动创建设备文件的流程 字符设备驱动模块 -->创建一个设备驱动class--->创建属于class的device--->调用mdev工具(自动完成)--> ...

  10. zabbix客户端一键安装脚本(主动模式监控)

    #!/bin/bash basepath=$(cd `dirname $0`; pwd)SHELL_DIR="${basepath}/shell"PACKAGE_DIR=" ...