Spring-IOC源码解读2.1-BeanDefinition的Resource定位
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring-IOC源码解读2.1-BeanDefinition的Resource定位相关的知识,希望对你有一定的参考价值。
Spring通过ResourceLoader来处理得到的Resource。我们先看下前面提到的ClassPathXmlApplicationContext 类定义:
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext { private Resource[] configResources; public ClassPathXmlApplicationContext(String[] paths, Class clazz, ApplicationContext parent) throws BeansException { super(parent); Assert.notNull(paths, "Path array must not be null"); Assert.notNull(clazz, "Class argument must not be null"); this.configResources = new Resource[paths.length]; for (int i = 0; i < paths.length; i++) { this.configResources[i] = new ClassPathResource(paths[i], clazz); } refresh(); } //这是应用于类路径上的Resource实现,这个getResource是在BeanDefinitionReader类(AbstractXmlApplicationContext)的loadBeanDefinition方法中调用的,
//loadBeanDefinition采用了模板方法设计模式,具体的实现实际是由各个子类来实现的。 @Override protected Resource[] getConfigResources() { return this.configResources; } }
refresh()方法会开始初始化容器,在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); } }
那么ClassPathXmlApplicationContext在哪里如何调用BeanDefinitionLoader来完成对BeanDefinition的读入的呢? Spring设计的时候解耦的非常好,实际上BeanDefinition的定位,读入,注册过程是分开进行的。通过eclipse工具观察调用栈可以看到在ClassPathXmlApplicationContext的父类AbstractXmlApplicationContext的loadBeanDefinitions(XmlBeanDefinitionReader reader)方法中完成了资源文件的获取与读入。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
BeanDefinition的具体载入过程委托给了AbstractRefreshableApplicationContext的子类完成,这里的实现就是在子类AbstartactXmlApplication类中的loadBeanDefinition()方法中完成:
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); }
具体的实现在AbstartctBeanDefinitionReader中:
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException { //这里的ResourceLoader使用的DefaultResourceLoader ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available"); } //这里对ResourceLoader的路径模式进行解析,比如我们设定的各种Ant格式的路径定义,得到需要的resource集合,即我们已经定义好的BeanDefinition信息,可以是多个文件 if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //调用DefaultResourceLoader的getResource完成具体的Resource定位 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; } }
我们看下DefaultResourceLoader类中getReource()方法:
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); //处理带有classpath标识的resource if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // 处理带有url标识的resource URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { //如果既不是classpath也不是url则交给getResourceByPath()方法完成资源加载,这个一般由子类实现 return getResourceByPath(location); } } }
前面我们看到getResourceByPath()会被子类ClassPathXMLApplicationContext实现,这个方法返回的是一个FileSystemResource对象,通过这个对象Spring就可以进行相关的IO操作,完成BeanDefinition的定位。分析到这里我们已经可以得出它实现的就是一个对path进行解析然后生成个FileSystemResource对象返回的过程。到这里就已经完成了Resource的定位过程,在BeanDefinition定位完成的基础上我们就可以通过返回的Resource对象进行BeanDefinition的载入了。
以上是关于Spring-IOC源码解读2.1-BeanDefinition的Resource定位的主要内容,如果未能解决你的问题,请参考以下文章