博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring容器启动流程(上)
阅读量:4223 次
发布时间:2019-05-26

本文共 8715 字,大约阅读时间需要 29 分钟。

一般来说,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中固定的命名空间 进行比较,一致则认为是默认命名空间,反之则否。

默认标签解析

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中读取配置信息。

你可能感兴趣的文章
Host 'XXX' is not allowed to connect to this MySQL server解决方案
查看>>
corosync pacemaker 配置高可用集群(一)
查看>>
5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO
查看>>
nginx(一) nginx详解
查看>>
nginx(二) nginx编译安装 及 配置WEB服务
查看>>
nginx(三) nginx配置:反向代理 负载均衡 后端健康检查 缓存
查看>>
nginx(四) nginx+keepalived 实现主备+双主热备模型的高可用负载均衡代理服务
查看>>
jQuery核心--多库共存
查看>>
4 51 单片机最小系统
查看>>
6 51点亮第一个LED
查看>>
8 51 LED流水灯
查看>>
Multisim 14.0 搭建并仿真51单片机最小系统
查看>>
51 中断系统 外部中断0 外部中断1
查看>>
51 单片机 时间/计数器中断
查看>>
腾讯云本地还原mysql物理冷备
查看>>
算法图解 第3章 递归
查看>>
Java反转整数
查看>>
jdbc中Datetime与java.util.Date的相互转换
查看>>
hibernate中取得connection的方法
查看>>
如何使用log4j输出单个级别的log到指定文件
查看>>