Spring源码分析之IOC容器的初始化(Resource定位过程)

IOC容器的初始化主要包括三个过程,即

  • Resource定位过程;
  • BeanDefinition(也就是XML配置文件)的载入;
  • 向IOC容器注册BeanDefinition;

Resource定位过程

以FileSystemXmlApplicationContext为例,分析ApplicationContext的实现是怎样完成这个Resource定位过程的。

下面是FileSystemXmlApplicationContext的继承关系图

从类图中,可以发现:

FileSystemXmlApplicationContext继承AbstractApplicationContext具备了ResourceLoader读入以Resource定义的BeanDefinition的能力,因为AbstractApplicationContext的基类是DefaultResourceLoader。

下面看下FileSystemXmlApplicationContext的源码:

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {

        //构造函数
	public FileSystemXmlApplicationContext() {
	}
    
        //根据双亲的IOC容器创建容器
	public FileSystemXmlApplicationContext(ApplicationContext parent) {
		super(parent);
	}

        //configLocation包含的是BeanDefinition所在的路径
	public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}

        //configLocationn允许包含多个BeanDefinition路径
	public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
		this(configLocations, true, null);
	}

        //configLocationn允许包含多个BeanDefinition路径的同时,还允许指定自己的双亲IOC容器
	public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
		this(configLocations, true, parent);
	}

    
	public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
		this(configLocations, refresh, null);
	}
    
        //在对象的初始化过程中,调用refresh函数载入BeanDefinition,这个refresh启动了
        //BeanDefinition的载入过程
	public FileSystemXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}

        //1.应用于文件系统中Resource的实现,通过构造一个FileSystemResource来得到一个在文件系统中定
        //义的BeanDefinition
        //2.这个getResourceByPath是在BeanDefinitionReader的loadBeanDefinition中被调用的
        //3.loadBeanDefinition采用了模板模式,具体的定位实现实际上是由各个子类完成的
	@Override
	protected Resource getResourceByPath(String path) {
		if (path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemResource(path);
	}

}

 

定位过程分析

执行下面这段程序

ApplicationContext applicationContext = new FileSystemXmlApplicationContext("file:///F:/JavaProdject/spring_bean/src/main/resources/bean.xml");

用Debug分析执行过程

  • 首先执行的是AbstractApplicationContext中的静态代码块
  • 开始执行FileSystemXmlApplicationContext的构造函数,最后调用的是下面这个构造函数

也就是对BeanDefinition资源的定位的过程,最初是由refresh()这个方法触发的。

  • refresh()方法是在基类AbstractApplicationContext调用的

进入上面的这个方法

  • obtainFreshBeanFactory方法

  • refreshBeanFactory方法是Resource定位的关键

发现执行的是AbstractRefreshableApplicationContext的refreshBeanFactory()方法,this指的是FileSystemXmlApplicationContext

 protected final void refreshBeanFactory() throws BeansException {
        //判断是否已经建立BeanFactory,如果已经创建则销毁并关闭该BeanFactory
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        //
        try {
            //创建beanFactory
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            //设置序列化id
            beanFactory.setSerializationId(this.getId());
            //自定义beanFactory
            this.customizeBeanFactory(beanFactory);
            //加载BeanDefinition
            this.loadBeanDefinitions(beanFactory);
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }

createBeanFactory方法

      //这里就是创建DefaultListableBeanFactory的地方
    protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(this.getInternalParentBeanFactory());
    }
//根据容器已有的双亲IOC容器的信息来生成DefaultListableBeanFactory的双亲IOC容器   
 protected BeanFactory getInternalParentBeanFactory() {
        return (BeanFactory)(this.getParent() instanceof ConfigurableApplicationContext ? ((ConfigurableApplicationContext)this.getParent()).getBeanFactory() : this.getParent());
    }

loadBeanDefinitions定义在AbstractXmlApplicationContext内

  • loadBeanDefinitions方法
 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        //创建一个XmlBeanDefinitionReader
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        //设置值
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        //在这里设置了属性resourceLoader,this指的是FileSystemXmlApplicationContext
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        //初始化XmlBeanDefinitionReader,在这里设置validationMode和namespaceAware
        this.initBeanDefinitionReader(beanDefinitionReader);
        继续加载BeanDefinitions
        this.loadBeanDefinitions(beanDefinitionReader);
    }
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        //固定返回null
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        //获取BeanDefinition地址,可以是多个
        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            //加载BeanDefinition
            reader.loadBeanDefinitions(configLocations);
        }

    }
    @Nullable
    protected Resource[] getConfigResources() {
        return null;
    }
  • XmlBeanDefinitionReader的loadBeanDefintions调用的是基类AbstractBeanDefinitionReader的方法,这里的this指的是

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        //判断beanDefition的地址是否为空,为空则抛异常
        Assert.notNull(locations, "Location array must not be null");
        //用来记录beanDefinition的数量
        int counter = 0;
        String[] var3 = locations;
        //获取配置文件的个数
        int var4 = locations.length;

        //循环加载beanDefinition
        for(int var5 = 0; var5 < var4; ++var5) {
            String location = var3[var5];
            counter += this.loadBeanDefinitions(location);
        }
        //返回beanDefinition的个数
        return counter;
    }
  • AbstractBeanDefinitionReader的loadBeanDefinition

 public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        //这里取得resourceLoader,在AbstractXmlApplicationContext的loadBeanDefinitions方法中已经设置,
        //取得即是FileSystemXmlApplicationContext
        ResourceLoader resourceLoader = this.getResourceLoader();
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
        } else {
            int loadCount;
            //这里对Resource的路径进行解析,得到需要的Resource集合,这些Resource集合指向的我们
            //已经定义好的BeanDefinition信息,可以是多个;
            //判断是否属于classpath*:的resource加载器
            if (!(resourceLoader instanceof ResourcePatternResolver)) {
                //调用DefaultResourceLoader的getResource完成具体的Resource定位
                Resource resource = resourceLoader.getResource(location);
                loadCount = this.loadBeanDefinitions((Resource)resource);
                if (actualResources != null) {
                    actualResources.add(resource);
                }

                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
                }

                return loadCount;
            } else {
                try {
                    //调用DefaultResourceLoader的getResource完成具体的Resource定位
                    Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                    loadCount = this.loadBeanDefinitions(resources);
                    if (actualResources != null) {
                        Resource[] var6 = resources;
                        int var7 = resources.length;

                        for(int var8 = 0; var8 < var7; ++var8) {
                            Resource resource = var6[var8];
                            actualResources.add(resource);
                        }
                    }

                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                    }

                    return loadCount;
                } catch (IOException var10) {
                    throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
                }
            }
        }
    }
  • 前一种getResource方法,这个方法定义在基类DefaultResourceLoader中

	@Override
	public Resource getResource(String location) {
		Assert.notNull(location, "Location must not be null");
                
                //先使用协议解析器,如果能生成Resource就直接返回
		for (ProtocolResolver protocolResolver : this.protocolResolvers) {
			Resource resource = protocolResolver.resolve(location, this);
			if (resource != null) {
				return resource;
			}
		}
                //如果beanDefinition的地址以"/"开头,就使用前面FileSystemXmlApplicationContext的getResourceByPath生成Resource
		if (location.startsWith("/")) {
			return getResourceByPath(location);
		}
                 //处理以"classpath:"开头的Resource
		else 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 (ResourceUtils.isFileURL(url) ? new FileUrlResource(url) : new UrlResource(url));
			}
			catch (MalformedURLException ex) {
				//如果既不是classpath,也不是URL标识的Resource定位,则调用getResourceByPath
				return getResourceByPath(location);
			}
		}
	}

        //根据beanDefinition地址得到一个ClassPathContextResource
	protected Resource getResourceByPath(String path) {
		return new ClassPathContextResource(path, getClassLoader());
	}
  • 后一种AbstractApplicationContext调用的是PathMatchingResourcePatternResolver的getResoruces方法
//判断locationPattern是否以classpath*开头
//true:以findPathMatchingResources的方法或者findAllClassPathResources方法
//false:以findPathMatchingResources的方法或者DefaultResourceLoader的getResource方法
public Resource[] getResources(String locationPattern) throws IOException {
        Assert.notNull(locationPattern, "Location pattern must not be null");
        if (locationPattern.startsWith("classpath*:")) {
            return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));
        } else {
            int prefixEnd = locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(":") + 1;
            return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};
        }
    }

至此,我们已经明确的找到了Resource定位的方法,即DefaultResourceLoader中的getResource方法,该方法针对对各类的beanDefinition创建Resource;此外,AbstractApplicationContext的refrush方法是beanDefinition资源定位的入口。

 

归纳一下这些方法的调用:

1.FileSystemXmlApplicationContext 构造函数

2. AbstractApplicationContext  refresh()

3. AbstractApplicationContext  obtainFreshBeanFactory()

4. AbstractRefreshableApplicationContext  refreshBeanFactory()

5. AbstractXmlApplicationContext  各种loadBeanDefinitions方法(5个)

6. AbstractBeanDefinitionReader  各种loadBeanDefinitions()(3个)

7. DefaultResourceLoader  getResource(String location)

 

总结

Resource的定位过程包括

  1. AbstractApplicationContext  refresh()方法是入口,是定位的开始方法;
  2. AbstractXmlApplicationContext  loadBeanDefinitions方法中获得beanDefinition的路径;

  3. AbstractBeanDefinitionReader中调用getResource方法获取Resource

 

参考书籍:《SPRING技术内幕:深入解析SPRING架构与设计原理》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值