Spring容器启动流程(上)

一般来说,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中读取配置信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值