Spring-IOC源码解读2.1-BeanDefinition的Resource定位

Spring通过ResourceLoader来处理得到的Resource。在前面我们知道容器初始化是以refresh()方法为入口的,内部的实现首先准备上下文,然后通过obtainFreshBeanFactory()方法获取beanFactory,

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
    }

进入AbstractRefreshableApplicationContext类的refreshBeanFactory()方法:

protected final void refreshBeanFactory() throws BeansException {
        //如果已经存在beanfactory则先销毁之后再重新创建
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //通过createBeanFactory()方法创建一个Ioc容器供容器使用,可以看到这个容器的类型是DefaultListableBeanFactory 
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            // 启动loadBeanDifinitions来载入BeanDifinition,因为有多种载入方式,这里通过一个抽象函数把具体的实现委托给子类来完成
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

BeanDefinition的具体载入过程委托给了AbstractRefreshableApplicationContext的子类完成,我们测试代码中实现该逻辑的子类就是AbstractXMLApplicationContext类,进入loadBeanDefinitions()方法:

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 为指定的BeanFactory创建一个XmlBeanDefinitionReader 
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // 使用此上下文的资源加载环境配置BeanDefinitionReader,由于AbstartactXmlApplication继承了DefaultResourceLoader,所以这里的ResourceLoader传的是this
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader, then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

上面的loadBeanDefinitions方法中设置了ResourceLoader对象用来定位Resource,由于AbstartactXmlApplicationContext继承自DefaultResourceLoader,所以该类本身就是一个加载器。Spring设计的时候解耦的非常好,实际上BeanDefinition的定位,读入,注册过程是分开进行的。我们看下DefaultResourceLoader是如何实现资源定位的,

public Resource getResource(String location) {
		Assert.notNull(location, "Location must not be null");
		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);
			}
		}
	}

上面的getResourceByPath()方法会根据路径加载Resource对象

protected Resource getResourceByPath(String path) {
		return new ClassPathContextResource(path, getClassLoader());
	}

上面方法返回的是一个ClassPathContextResource对象,通过这个对象Spring就可以进行相关的I/O操作了。到这里我们就已经完成BeanDefinition的定位了。在BeanDefinition定位完成的基础上我们就可以通过返回的Resource对象进行BeanDefinition的载入了。

 

posted @ 2017-10-15 16:36  cello_ist  阅读(225)  评论(0编辑  收藏  举报