Spring源码学习笔记(1)——容器的基本实现
一. 实例代码
Resource resource = new ClassPathResource("applicationContext.xml");
XmlBeanFactory beanFactory = new XmlBeanFactory(resource);
Object bean = beanFactory.getBean("beanName");
以上是很简单的一段代码,只完成了3个Spring的基本操作:
- 读取Spring配置文件
- 根据配置文件创建BeanFactory,加载Bean
- 从BeanFactory中获取Bean的实例
上述代码完成了Spring容器的基本功能,看似简单,但是背后却封装了复杂的逻辑。下面我们结合源码看下Spring到底做了哪些处理。
一. 资源的封装——Resource接口
-
Resource接口抽象了所有Spring内部使用的底层资源,如File、Classpath下的资源和ByteArray等。Resource接口继承了InputStreamSource接口,InputStreamSource接口只有一个方法:
public interface InputStreamSource { //返回一个新的InputStream对象 InputStream getInputStream() throws IOException; }Resource接口提供了一些资源基础属性的相关方法。
public interface Resource extends InputStreamSource { boolean exists(); default boolean isReadable() { return true; } default boolean isOpen() { return false; } default boolean isFile() { return false; } URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); } long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; @Nullable String getFilename(); String getDescription(); } -
对于不同来源的资源,Resource有不同类型的实现类,如FileSystemResource、ClassPathResource、UrlResource等。
-
通常,Spring会将配置文件封装成一个Resource对象,后续属性的解析和Bean的加载就交给BeanDefinitionReader去处理。
二. 资源的解析——BeanDefinitionReader
-
当Spring将配置文件封装成了Resource之后,就交给BeanDefinitionReader去对其进行加载和解析。
BeanDefinitionReader定义了解析配置文件的相关方法。
public interface BeanDefinitionReader { BeanDefinitionRegistry getRegistry(); @Nullable ResourceLoader getResourceLoader(); @Nullable ClassLoader getBeanClassLoader(); BeanNameGenerator getBeanNameGenerator(); int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException; int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException; int loadBeanDefinitions(String location) throws BeanDefinitionStoreException; int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException; } -
Spring配置文件可以有不同的描述方式,如xml、properties等,根据不同的描述类型,有不同的实现类,如XmlBeanDefinitionReader、PropertiesBeanDefinitionReader等。
三. Bean的加载
Spring的Bean是何时被加载的呢?看示例代码:
new XmlBeanFactory(resource);
创建了XmlBeanFactory实例,并传入Resource对象,Resource就在这里被加载解析:
public class XmlBeanFactory extends DefaultListableBeanFactory {
//XmlBeanFactory内部维护了一个XmlBeanDefinitionReader对象,用来解析以Xml方式描述的配置文件
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
//构造XmlBeanFactory后,即调用XmlBeanDefinitionReader的方法解析配置文件
this.reader.loadBeanDefinitions(resource);
}
}
XmlBeanFactory调用了XmlBeanDefinitionReader的loadBeanDefinitions()方法解析配置文件,加载Bean定义。具体如下:
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException{
//对Resource进行编码包装
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//从Resource中获取InputStream并构造InputSource
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//调用doLoadBeanDefinitions()进行BeanDefinition的解析
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
可以看到,loadBeanDefinitions()方法只是做了一些准备工作,核心的加载逻辑是在doLoadBeanDefinitions()方法中的:
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//将封装了Spring配置文件的Resource对象解析成Document文档对象
Document doc = doLoadDocument(inputSource, resource);
//向Spring容器中注册BeanDefinition
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
不考虑异常处理,doLoadBeanDefinitions()方法其实只完成了两件事:
- 将资源解析成Document对象;
- 根据解析好的Document对象,向容器中注册BeanDefinition
Document是w3c规范定义的文档对象,doLoadDocument(inputSource, resource)方法的作用就是将Spring的配置文件Resource对象解析成Document对象,具体的逻辑涉及到dom解析相关处理,这里就不再展开了。
下面重点关注registerBeanDefinitions方法:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建DefaultBeanDefinitionDocumentReader实例,用来解析Document文件
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//本次注册BeanDefinition前,容器中BeanDefinition的数量
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回本次成功注册的BeanDefinition的数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
注册BeanDefinition的核心逻辑在DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions()方法中:
protected void doRegisterBeanDefinitions(Element root) {
//创建BeanDefinitionParserDelegate代理对象专门处理解析
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//处理profile属性,多环境支持
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
//解析预处理,模板方法
preProcessXml(root);
//实际处理解析
parseBeanDefinitions(root, this.delegate);
//解析后处理,模板方法
postProcessXml(root);
this.delegate = parent;
}
最后,解析和注册BeanDefinition的处理在parseBeanDefinitions()方法中
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//解析<beans>标签
if (delegate.isDefaultNamespace(root)) {
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;
if (delegate.isDefaultNamespace(ele)) {
//解析<bean>标签
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
//处理自定义标签
else {
delegate.parseCustomElement(root);
}
}