Spring框架启动流程(一)

来源:互联网 发布:mac os 10.12原版镜像 编辑:程序博客网 时间:2024/05/19 17:51

一般来说,Spring框架启动流程大体上分成两个大的步骤:IoC容器初始化和Bean的依赖注入。

IoC容器初始化

Spring IOC容器初始化分三个过程:

  • 第一个过程是Resource定位过程, 这个Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一的接口;

  • 第二个过程是BeanDefinition的载入, 这个载入过程是把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。具体来说,BeanDifinition实际上就是POJO对象在IOC容器中的抽象,通过这个BeanDifinition定义的数据结构,使IOC容器能够方便的对POJO对象也就是Bean进行管理;

  • 第三个过程是向IoC容器注册这些BeanDefinition的过程, 这个过程是通过调用BeanDifinitionRegistry借口来实现的。这个注册过程把载入过程中解析得到的BeanDifinition向Ioc容器进行注册。在阅读源码中可知,在IOC容器内部将BeanDifinition注入到一个HashMap中去,Ioc容器就是通过这个HashMap来持有这些BeanDifinition数据的。

Resource定位

Spring对其内部使用到的资源实现了自己的抽象结构——Resource接口来封装底层资源

public interface InputStreamSource {    InputStream getInputStream() throws IOException;}public interface Resource extends InputStreamSource {    boolean exists();    boolean isReadable();    boolean isOpen();    URL getURL() throws IOException;    URI getURI() throws IOException;    File getFile() throws IOException;    long lastModified() throws IOException;    Resource createRelative(String var1) throws IOException;    String getFilename();    String getDescription();}

下图列出了Resource接口的定义和继承关系:
图1

BeanDefinition载入

完成BeanDefinition定位后,Spring通过返回的Resource对象来进行BeanDefinition的载入了。
BeanDefinition载入最主要的处理逻辑在于如下接口:

public interface BeanDefinitionReader {            ……    int loadBeanDefinitions(Resource var1) throws BeanDefinitionStoreException;            ……}

针对不同类型的Resource,Spring使用不同的BeanDefinitionReader完成BeanDefinition的载入任务。
这里写图片描述

我们以XmlBeanDefinitionReader为例,查看其具体实现逻辑

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {                ……        var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());                ……            return var5;    }
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {        ……            int ex = this.getValidationModeForResource(resource);            Document doc = this.documentLoader.loadDocument(inputSource, this.getEntityResolver(), this.errorHandler, ex, this.isNamespaceAware());            return this.registerBeanDefinitions(doc, resource);    }

其中loadDocument方法验证Resource的模式并将其转换为Document对象,接下来的registerBeanDefinitions才是我们的重头戏。先看一下registerBeanDefinitions的具体实现:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {       ……            BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();            int countBefore = this.getRegistry().getBeanDefinitionCount();            documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));            return this.getRegistry().getBeanDefinitionCount() - countBefore;      ……    }
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {        this.readerContext = readerContext;        this.logger.debug("Loading bean definitions");        Element root = doc.getDocumentElement();        BeanDefinitionParserDelegate delegate = this.createHelper(readerContext, root);        this.preProcessXml(root);        this.parseBeanDefinitions(root, delegate);        this.postProcessXml(root);    }
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {        if(delegate.isDefaultNamespace(root.getNamespaceURI())) {            NodeList nl = root.getChildNodes();            for(int i = 0; i < nl.getLength(); ++i) {                Node node = nl.item(i);                if(node instanceof Element) {                    Element ele = (Element)node;                    String namespaceUri = ele.getNamespaceURI();                    if(delegate.isDefaultNamespace(namespaceUri)) {                        this.parseDefaultElement(ele, delegate);                    } else {                        delegate.parseCustomElement(ele);                    }                }            }        } else {            delegate.parseCustomElement(root);        }    }

对于默认命名空间的标签,Spring调用parseDefaultElement()方法解析,而对于不是默认命名空间的标签,则调用parseCustomElement()方法解析。而判断是否为默认命名空间其实是使用getNamespaceURI()获取命名空间并与Spring中固定的命名空间http://www.Springframework.org/schema/beans 进行比较,一致则任务是默认命名空间,反之则否。

默认标签解析

Spring的默认标签包括import、alias、bean和beans,其中对于bean的解析最为复杂且最为重要。bean标签的具体解析逻辑如下:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {        //首先委托BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法进行元素解析        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);        if(bdHolder != null) {            //如果默认标签下存在自定义属性,还需对自定义标签解析            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);            try {                //委托BeanDefinitionReaderUtils的registerBeanDefinition方法完成注册                        BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());            } catch (BeanDefinitionStoreException var5) {                this.getReaderContext().error("Failed to register bean definition with name \'" + bdHolder.getBeanName() + "\'", ele, var5);            }            //发出响应事件通知相关的监听器            this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));        }    }
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {        return this.parseBeanDefinitionElement(ele, (BeanDefinition)null);    }    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {        String id = ele.getAttribute("id");        String nameAttr = ele.getAttribute("name");        ……        AbstractBeanDefinition beanDefinition = this.parseBeanDefinitionElement(ele, beanName1, containingBean);        ……    }
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {        ……        AbstractBeanDefinition bd = this.createBeanDefinition(className, ex);        //解析bean的各种属性        this.parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, "description"));        //解析元数据        this.parseMetaElements(ele, bd);        //解析lookup-method属性        this.parseLookupOverrideSubElements(ele, bd.getMethodOverrides());        //解析replaced-method属性        this.parseReplacedMethodSubElements(ele, bd.getMethodOverrides());        //解析构造函数参数        this.parseConstructorArgElements(ele, bd);        //解析property子元素        this.parsePropertyElements(ele, bd);        //解析qualifier子元素        this.parseQualifierElements(ele, bd);        bd.setResource(this.readerContext.getResource());        bd.setSource(this.extractSource(ele));        AbstractBeanDefinition var7 = bd;        return var7;        ………    }
//这是AbstractBeanDefinition的数据结构,可以看到很多属性经常在xml配置文件中出现public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable {    ……    private volatile Object beanClass;    private String scope;    private boolean singleton;    private boolean prototype;    private boolean abstractFlag;    private boolean lazyInit;    private int autowireMode;    private int dependencyCheck;    private String[] dependsOn;    private boolean autowireCandidate;    private final Map qualifiers;    private boolean primary;    private ConstructorArgumentValues constructorArgumentValues;    private MutablePropertyValues propertyValues;    private MethodOverrides methodOverrides;    private String factoryBeanName;    private String factoryMethodName;    private String initMethodName;    private String destroyMethodName;    private boolean enforceInitMethod;    private boolean enforceDestroyMethod;    private boolean synthetic;    private int role;    private String description;    private Resource resource;    ……}

对于具体属性的解析过程,读者可根据相关代码深入查看。

自定义标签解析

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {        //通过getNamespaceURI()方法获取标签元素的命名空间;        String namespaceUri = ele.getNamespaceURI();        //根据命名空间提取对应的标签处理器NamespaceHandler;        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);        if(handler == null) {            this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);            return null;        } else {            //解析并注册自定义bean            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));        }    }

BeanDefinition注册

Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 我努力奔跑 奔跑造句 奔跑歌 奔跑人 努力奔跑 男子在雨中奔跑搓澡 奔跑兄弟第五季 奔跑兄弟第二季 奔跑兄弟第六季 随着马儿奔跑律动 奔跑歌曲下载 奔跑歌曲播放 奔跑羽泉歌曲 奔跑的歌词是什么 奔跑羽泉歌词 奔跑歌词 羽泉 奔跑歌曲羽泉 只管向前奔跑 向着阳光奔跑 夕阳下的奔跑 奔跑的独角兽 我一直在奔跑 奔跑歌词完整版 一个人的奔跑 在阳光下奔跑 长大以后我学会奔跑 长大以后我只能奔跑 奔达 奔达400 奔达250 奔达摩托 奔达星罗400 奔达摩托车 奔达阿修罗 奔达400价格 奔达康集团 奔达丝网 思奔达音箱 奔达阿修罗400 奔达摩托是杂牌吗