Spring源码学习(五)——Spring.Beans(二)

        接上一篇,我们通过ClassUtils.forName()方法完成了对依赖注入接口的加载,接着DefaultListableBeanFactory实例化了一个静态map,这个map用于存储bean工厂实例和序列化id的映射关系。这个流程结束后,XmlBeanFactory的类初始化完成,然后就进入它的构造方法了。

    1.再看一下Demo中构造方法的参数,是一个ClassPathResource实例,ClassPathResource通过构造方法构造一个配置文件的实例对象,很明显,进入构造方法之前,需要先进入ClassPathResource的构造方法:

public ClassPathResource(String path) {
		this(path, (ClassLoader) null);
	}

       这个构造方法调用了另一个构造方法,参数是path和类加载器。进去看:

	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());
	}

    首先调用StringUtils.cleanPath方法,进去看:

public static String cleanPath(String path) {
		if (path == null) {
			return null;
		}
		String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);

		// Strip prefix from path to analyze, to not treat it as part of the
		// first path element. This is necessary to correctly parse paths like
		// "file:core/../core/io/Resource.class", where the ".." should just
		// strip the first "core" directory while keeping the "file:" prefix.
		int prefixIndex = pathToUse.indexOf(":");
		String prefix = "";
		if (prefixIndex != -1) {
			prefix = pathToUse.substring(0, prefixIndex + 1);
			if (prefix.contains("/")) {
				prefix = "";
			}
			else {
				pathToUse = pathToUse.substring(prefixIndex + 1);
			}
		}
		if (pathToUse.startsWith(FOLDER_SEPARATOR)) {
			prefix = prefix + FOLDER_SEPARATOR;
			pathToUse = pathToUse.substring(1);
		}

		String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);
		List<String> pathElements = new LinkedList<String>();
		int tops = 0;

		for (int i = pathArray.length - 1; i >= 0; i--) {
			String element = pathArray[i];
			if (CURRENT_PATH.equals(element)) {
				// Points to current directory - drop it.
			}
			else if (TOP_PATH.equals(element)) {
				// Registering top path found.
				tops++;
			}
			else {
				if (tops > 0) {
					// Merging path element with element corresponding to top path.
					tops--;
				}
				else {
					// Normal path element found.
					pathElements.add(0, element);
				}
			}
		}

		// Remaining top paths need to be retained.
		for (int i = 0; i < tops; i++) {
			pathElements.add(0, TOP_PATH);
		}

		return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);
	}

    这方法的作用是将传入路径参数处理成可供IOC容器识别解析的路径。

  1)首先是对Windows的文件分隔符“\”替换成“/”

  2)接着对路径的前缀进行处理,如果路径包含“:”,将前缀取出保存,然后对“:”之后的路径进行解析

  3)根据路径中存在的"/"情况,将整个路径截断成各个层级,并存入一个数组中,数组从前至后分别存放从上到下的目录

  4)接着,沿着目录层级,从下至上的递归遍历:三种情况:a.如果存在标识当前目录的"./",则直接跳过;b.如果存在标识上级目录的"../",则对其进行记录,如果记录数目大于零的情况下,遍历到了包含实际目录信息的元素,则进行一次抵消,如"../asf/dasf/../../dfsad.xml",当遍历到dasf这一层时,有两个"../",这时不会保存dasf,而是对"../"进行一次抵消,因为我们知道"../"标识的就是父目录,dasf是实际的父目录;c.最先保存的信息一定是位于最后一层的配置文件的信息,也就是dfsad.xml

  5)经过上述处理之后,如果还有"../"未被抵消,则将剩下的加入最终路径。将遍历后的路径与之前保存的前缀组合,得出最后路径,引用上例,我们会得到../dfsad.xml

2.对路径处理完了之后,调用ClassUtils.getDefaultClassLoader方法获取默认类加载器。至此ClassResource实例化完成。这里针对Resource进行一下介绍。

 

 Resource接口被Spring用来封装底层资源,上类图:

我们可以看到,Resource的父接口是InputStreamSource,这个接口只定义了一个方法getInputStream()用于返回inputStream对象,这说明一切可以返回inputStream的资源都可以被Resource封装。回到Resource,我们可以看到,Resource为底层资源封装了三个属性:exists(是否存在)、isReadable(是否可通过inputStream读取)、isOpen(该资源是否持有一个打开的InputStream)。除此之外,Resource还提供了获取资源URL、URI、资源信息描述、相关资源等方法。从类图中我们也可以看到,spring针对不同的资源类型,如URL、file、ByteArray等提供了不同的Resource实现。介绍完了,继续走。

3.调用XmlBeanFactory的构造方法XmlBeanFactory(Resource resoutce),该构造方法指向这个方法:

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

一直向上调用各级父类的构造方法,然后就调用loadBeanDefinitions(resource)对资源文件进行解析了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值