public int loadBeanDefinitions(String location, SetgetResourceLoaderactualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); int loadCount = loadBeanDefinitions(resources); if (actualResources != null) { for (Resource resource : resources) { actualResources.add(resource); } } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]"); } return loadCount; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int loadCount = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isDebugEnabled()) { logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]"); } return loadCount; } }
该方法返回的是XmlWebApplicationContext实例
因为XmlWebApplicationContext父类 AbstractApplicationContext继承了DefaultResourceLoader类 而DefaultResourceLoader实现了ResourceLoader接口,又因为ResourcePatternResolver接口继承了ResourceLoader接口 所以XmlWebApplicationContext间接实现了ResourcePatternResolver接口
if (resourceLoader instanceof ResourcePatternResolver) {
在XmlWebApplicationContext父类AbstractApplicationContext初始化时 resourcePatternResolver默认为PathMatchingResourcePatternResolver类型
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
所以这里实际上是 PathMatchingResourcePatternResolver.getResources 参数为web.xml中配置的 classpath:applicationContext.xml
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
// CLASSPATH_ALL_URL_PREFIX = classpath*: 显然不是classpath*开头 所以走else
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// a class path resource (multiple resources for same name possible)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = locationPattern.indexOf(":") + 1;
//public boolean isPattern(String path) {
// return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
//}
// 判断路径中是否包含* 或者 ? 很明显这里没有所以else
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
因为PathMatchingResourcePatternResolver在初始化的时候构造函数默认创建的是DefaultResourceLoader类 所以这里getResourceLoader返回的是DefaultResourceLoader实例 location为classpath:applicationContext.xml
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
// no
if (location.startsWith("/")) {
return getResourceByPath(location);
}
// CLASSPATH_URL_PREFIX = classpath: 成功命中
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
path为applicationContext.xml 至此getResources(location)方法返回了 一个Resource[]数组 此时实际类型ClassPathResource
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());
}
循环resources进行加载bean
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int counter = 0;
for (Resource resource : resources) {
counter += loadBeanDefinitions(resource);
}
return counter;
}
跟踪代码 调转到XmlBeanDefinitionReader的loadBeanDefinitions方法 上篇说过默认的阅读器就是XmlBeanDefinitionReader
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();
// 如果是null 则进行初始化
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 判断encodedResource是否已在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());
}
// 在spring中 所有实际的操作都是do开头的
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();
}
}
}
doLoaddocument不需要过多解读 将xml转换成document结构 确保你的文件中使用的标签在你的文件头都有声明 并且存在对应的依赖包
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
document doc = doLoaddocument(inputSource, resource);
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);
}
}
这里documentReader实际上是实例化 DefaultBeanDefinitiondocumentReader类
getRegistry返回的是DefaultListableBeanFactory 而getBeanDefinitionCount返回的实际是DefaultListableBeanFactory中beanDefinitionMap的size 也就意味着已经加载的beanDefinition都会被放进beanDefinitionMap 类型是Map
public int registerBeanDefinitions(document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitiondocumentReader documentReader = createBeanDefinitiondocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
readerContext由createReaderContext(resource)创建 new了一个XmlReaderContext实例
doc是DeferreddocumentImpl类型
public void registerBeanDefinitions(document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getdocumentElement();
doRegisterBeanDefinitions(root);
}
值得一提的是readerContext中的是从meta-INF/spring.handlers中读取的 这样spring就知道什么时候用什么处理器



