本文共 8715 字,大约阅读时间需要 29 分钟。
一般来说,Spring框架启动流程大体上分成两个大的步骤:IoC容器初始化和Bean的依赖注入。
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数据的。
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接口的定义和继承关系:
完成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)); } }
Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。