spring资源加载过程简单说分三步:
1、定位资源,也就是找到配置文件
2、载入文件,将文件解析为一个个元素和属性
3、将bean对象注册到ioc容器中,如果存在依赖注入,则根据配置文件选择是否在加载容器的时候将依赖注入加载,默认是true。
下面我们一步一步分析DefaultListableBeanFactory以及资源加载过程:
ClassPathResource resource = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
1
、在
DefaultListableBeanFactory
中一个非常重要的常量
beanDefinitionMap
,描述了配置文件中的
bean
相关的对象,都封装成
BeanDefinition
对象,放到
beanDefinitionMap
中,以
bean
的
name
为
key
。参考源码为:
/** Map of bean definition objects, keyed by bean name */
private final Map beanDefinitionMap = new ConcurrentHashMap(64);
2、我们先来看第一步定位资源
ClassPathResourceresource = new ClassPathResource("beans.xml");
我们首先从Resource的根类org.springframework.core.io.Resource源码来分析:
* @author Juergen Hoeller
* @since 28.12.2003
* @see #getInputStream()
* @see #getURL()
* @see #getURI()
* @see #getFile()
* @see WritableResource
* @see ContextResource
* @see FileSystemResource
* @see ClassPathResource
* @see UrlResource
* @see ByteArrayResource
* @see InputStreamResource
* @see PathResource
*/
public interface Resource extends InputStreamSource {}
ClassPathResource是具体的一种resource了。看类的注释:
/**
* {@link Resource} implementation for class path resources.
* 翻译:为 classpath 资源的实现类
* Uses either a given ClassLoader or a given Class for loading resources.
*
* Supports resolution as {@code java.io.File} if the class path
* resource resides in the file system, but not for resources in a JAR.
*翻译:支持资源文件在classpath的系统文件中,且不在jar包中
* Always supports resolution as URL.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 28.12.2003
* @see ClassLoader#getResourceAsStream(String)
* @see Class#getResourceAsStream(String)
*/
public class ClassPathResource extends AbstractFileResolvingResource {}
我们来看classpathresouce的构造方法,可以看到这句代码完成了将参数赋予了成员变量并实例化了类构造器。
/**
* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
* A leading slash will be removed, as the ClassLoader resource access
* methods will not accept it.
* The thread context class loader will be used for
* loading the resource.
* @param path the absolute path within the class path
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
*/
public ClassPathResource(String path) {
this(path, (ClassLoader) null);
}
跟进 this(path, (ClassLoader) null);
/**
* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
* A leading slash will be removed, as the ClassLoader resource access
* methods will not accept it.
* @param path the absolute path within the classpath
* @param classLoader the class loader to load the resource with,
* or {@code null} for the thread context class loader
* @see ClassLoader#getResourceAsStream(String)
*/
public ClassPathResource(String path, ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
3、DefaultListableBeanFactory factory = newDefaultListableBeanFactory();
这行代码实例化了一个工厂,为下面的reader做准备。
4、
XmlBeanDefinitionReader reader = newXmlBeanDefinitionReader(factory);
重点是最后一行代码:
reader.loadBeanDefinitions(resource);
/**
* Load bean definitions from the specified XML file.
*翻译:加载制定xml文件的bean的定义
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
/**
* Load bean definitions from the specified XML file.
* @param encodedResource the resource descriptor for the XML file,
* allowing to specify an encoding to use for parsing the file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
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 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 {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
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();
}
}
}
DefaultBeanDefinitionDocumentReader:
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* Opens a DOM Document; then initializes the default settings
* specified at the {@code } level; then parses the contained bean definitions.
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root); // 核心实现,下篇文章继续讲解
}