SpringIOC容器启动后开启了BeanDefinition的Resource定位,载入和注册的三个过程,loadBeanDefinitions方法中提供了Resource的定位的三种方式,classpath,url,getResourceByPath,其次就是载入的过程,主要采用原始的IO的InputSource,但是在解析xml之前做了一些安全性的操作,这种安全性的操作可以用在自己的编码中。这个过程以XmlBeaDefinitionReader为例,方法loadBeanDefinitions中
/**
* 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());
}
//通过上文中
//private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
// new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently //being loaded");
//可以知道resourcesCurrentlyBeingLoaded是一个threadlocal本地变量。
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
//如果从threadlocal中获取的currentResource为空则初始化一个容量为4的hashSet,并将 //currentResource放入threadlocal中。
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
//如果已经存在则抛出异常,提示发现有循环载入encodeResource,而终止载入。可以看出这里的、、//threadlocal只是一个防止循环第三个变量
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 {
//处理结束后将encodedResouce删除
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
//从threadlocal中移除hashSet
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
ThreadLocal是如何保证线上安全的?ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。这里的不安全的变量即为encodedResource。threadlocal这种通过“以空间换时间”的方式比使用synchronized这种“以时间换空间”的同步机制更为简洁易懂。