一般来说,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接口的定义和继承关系:
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中读取配置信息。