ClassPathXmlApplicationContext 与 FileSystemXmlApplicationContext

用了这么久的框架,是时候搞一下源码了,一般最初接触spring 从以下步骤开始

  1. 创建一个bean类 并创建 ooxx.xml之类的spring bean描述文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="beanEntity" class="cn.felord.spring.bean.SpringBean">
<property name="beanName" value="springBean"/>
</bean>
</beans>
  1. 加载器进行加载 获取bean
 ApplicationContext context = new FileSystemXmlApplicationContext("F:\\workbase\\spring-framework-source\\src\\main\\resources\\bean.xml");
      //  ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");


        BeanEntity bean = context.getBean(BeanEntity.class);

        System.out.println("beanName >>>>>>>>>>>>>>>>>>>>>>>>>> "+bean.getBeanName());

根据上面发现加载xml有两种方式

  • ClassPathXmlApplicationContext
  • FileSystemXmlApplicationContext

关系如下:

spring源码阅读笔记(一)-LMLPHP

两者具有相同血脉的兄弟关系, 一种通过classpath来进行加载,底层是通过ClassLoader 来进行加载资源的。一种是通过文件IO来加载并读取配置xml的。ClassPathXmlApplicationContext 通过classpath 或者相对路径来加载bean 定义文件 资源路径 使用 configLocations 相对路径使用 (path,Class<?>)

FileSystemXmlApplicationContext改写了 DefaultResourceLoadergetResourceByPath(String path)方法原本默认采用 ClassPathResource 来加载

	/**
	 * Resolve resource paths as file system paths.
	 * <p>Note: Even if a given path starts with a slash, it will get
	 * interpreted as relative to the current VM working directory.
	 * This is consistent with the semantics in a Servlet container.
	 * @param path path to the resource
	 * @return the Resource handle
	 * @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath
	 */
	@Override
	protected Resource getResourceByPath(String path) {
		if (path.startsWith("/")) {
			path = path.substring(1);
		}
		return new FileSystemResource(path);
	}

一个通过classpath 来加载,一个通过获取文件的绝对路径 然后通过文件IO来加载,相较 FileSystemXmlApplicationContext 将配置与IOC容器分离开来。

AbstractXmlApplicationContext

说完儿子 来说下爹这货主要干了什么事呢?他是没有解析ooxx.xml功能的,但是他加载了一个 XmlBeanDefinitionReader,字面意思就是用来读取声明bean的xml文件的,也就是说这货为了读取xml,自己干不了委托别人来搞,具体薪资不详,我们来看一下AbstractXmlApplicationContext的核心代码片段


	/**
	 * Loads the bean definitions via an XmlBeanDefinitionReader.
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	 * @see #initBeanDefinitionReader
	 * @see #loadBeanDefinitions
	 */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		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);
	}

	/**
	 * Initialize the bean definition reader used for loading the bean
	 * definitions of this context. Default implementation is empty.
	 * <p>Can be overridden in subclasses, e.g. for turning off XML validation
	 * or using a different XmlBeanDefinitionParser implementation.
	 * @param reader the bean definition reader used by this context
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
	 */
	protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
		reader.setValidating(this.validating);
	}

可以看出该方法 就是一个初始化 XmlBeanDefinitionReader,然后加载bean定义的过程,但是这里的一顿操作还是很精妙的 initBeanDefinitionReader 被提取为一个protected方法 也就是说 初始化 XmlBeanDefinitionReader的方法也可以让儿子们来干,根据儿子们的不同口味量身定制。这在实际开发中是十分值得借鉴的。

饭也吃了,酒也喝了,要枪给枪要钱给钱,舒服之后最终该干活了, loadBeanDefinitions 命令一出 XmlBeanDefinitionReader 开始干活


	/**
	 * Load the bean definitions with the given XmlBeanDefinitionReader.
	 * <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
	 * method; hence this method is just supposed to load and/or register bean definitions.
	 * @param reader the XmlBeanDefinitionReader to use
	 * @throws BeansException in case of bean registration errors
	 * @throws IOException if the required XML document isn't found
	 * @see #refreshBeanFactory
	 * @see #getConfigLocations
	 * @see #getResources
	 * @see #getResourcePatternResolver
	 */
	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);
		}
	}


此处有必要简单搞一下 这个干活的spring源码阅读笔记(一)-LMLPHP

继承了 两个接口

  • BeanDefinitionReader通过此接口 bean 被定义 保存到注册表中核心有两类重载方法
    • loadBeanDefinitions(String location)通过路径来加载
    • int loadBeanDefinitions(Resource resource) 通过资源来加载
  • EnvironmentCapable用来获取环境参数 详见 Environment 主要是 profile、Property 等一些系统的环境参数
12-16 00:16