spring容器浅谈——如何读取配置文件

public void testSimpleLoad(){
		BeanFactory bFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
		MyTestBean bean = (MyTestBean) bFactory.getBean("myTestBean");
		System.out.println(bean);
}

 

对于这个简单的代码完成的功能无非是以下几点:

  1. 读取配置文件 applicationContext.xml
  2. 根据applicationContext.xml中的配置找到对应类的配置,并实例化
  3. 调用实例化后的实例

 

读取配置文件主要做了以下几件事:

  1. 配置文件封装:new ClassPathResource("applicationContext.xml")
  2. 加载bean:new XmlBeanFactory(resource)

             1) 通过DocumentLoader对Resource文件转换为Document文件

             2) 通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader类对Document进行解析

             3) 使用BeanDefinitionParserDelegate对标签进行解析——见下一篇文章

一、封装配置文件

spring的配置文件读取是通过ClassPathResource进行封装的。ClassPathResource类是简介实现了接口Resource,而Resource的作用是为了封装所有spring内部使用到的底层资源:File,URL,ClassPath等。Resource接口的定义如下:

spring的配置文件读取是通过ClassPathResource进行封装的。ClassPathResource类是简介实现了接口Resource,而Resource的作用是为了封装所有spring内部使用到的底层资源:File,URL,ClassPath等。Resource接口的定义如下:

public interface InputStreamSource {
	InputStream getInputStream() throws IOException;
}

public interface Resource extends InputStreamSource {
	boolean exists();
	boolean isReadable();
	boolean isOpen();
	URL getURL() throws IOException;
	URI getURI() throws IOException;
	File getFile() throws IOException;
	long contentLength() throws IOException;
	long lastModified() throws IOException;
	Resource createRelative(String relativePath) throws IOException;
	String getFilename();
	String getDescription();
}

不同来源的资源文件都有相对应的Resource实现:文件(FileSystemResource)、ClassPath资源(ClassPathResource)等,通过getInputStream()获得inputStream之后就可以正常开发了。

二、加载bean

  1. XmlBeanFactory类:该类对DefaultListableBeanFactory类进行了扩展,主要用于XML文档中读取BeanDefinition,现在已过期,一般使用ClassPathXmlBeanFactory。
    public XmlBeanFactory (Resource resource) throws BeansException {
    		this(resource, null);
    }
    
    public XmlBeanFactory (Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    		super(parentBeanFactory);
    		this.reader.loadBeanDefinitions(resource);
    }
    

    加载bean的核心方法就是:this.reader.loadBeanDefinitions(resource);

  2. XmlBeanDefinitionReader类:对xm配置文件进行读取、解析及注册

         首先将resource用EncodedResource封装,然后调用下面的代码

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		//…一些初始化操作
		try {
			//1.获取输入流
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
	//2.继续调用函数   doLoadBeanDefinitions
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (……) {
			……
		}
		finally {
			……
		}
	}

   具体操作由doLoadBeanDefinitions(InputSource, Resource)执行

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)  throws BeanDefinitionStoreException {
		try {
			//1.获取对XML文件的验证模式——DTD or XSD
			//2.加载XML文件并得到对应的Document
			Document doc = doLoadDocument(inputSource, resource);
			//3.根据返回的Document注册Bean信息
			return registerBeanDefinitions(doc, resource);
		}
		catch (……){
			……
		}
	}

使用哪种验证模式通过文件头部信息即可做出判断,而Document文件的创建再spring处也并无特别,我们更关心bean是如何解 析和创建的。

3. DefaultBeanDefinitionDocumentReader类:XmlBeanDefinitionDocumentReader的默认实现类

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//1.实例化documentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//2.记录统计前BeanDefinition的加载个数
		int countBefore = getRegistry().getBeanDefinitionCount();
		//3.加载及注册bean
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//4.返回本次加载的BeanDefinition个数
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

 

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		//1.获得document中的根元素
		Element root = doc.getDocumentElement();
		//2.根据root解析beanDefinition
		doRegisterBeanDefinitions(root);
	}

 

protected void doRegisterBeanDefinitions(Element root) {
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
			//0.处理profile属性
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					return;
				}
			}
		}
		//1.解析前处理,留给子类实现
		preProcessXml(root);
		//2.解析
		parseBeanDefinitions(root, this.delegate);
		//3.解析后处理,留给子类实现
		postProcessXml(root);

		this.delegate = parent;
	}

Profile属性用来设置环境,有了这个属性我们就可以同时在配置文件中部署两套配置来适用于生产环境和开发环境,最常用的就是更换不同的数据库。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						//对默认标签的处理
						parseDefaultElement(ele, delegate);
					}
					else {
						//对自定义标签的处理
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

 

猜你喜欢

转载自blog.csdn.net/CCCCC_SSSSS/article/details/81483889
今日推荐