乾貨分享之Spring框架原始碼解析01-(xml配置解析)

01發表於2021-10-14

記錄並分享一下本人學習spring原始碼的過程,有什麼問題或者補充會持續更新。歡迎大家指正!

環境: spring5.X + idea

Spring 是一個工廠,是一個負責物件的建立和維護的工廠。它給我提供了一個功能齊全而且方便我們使用的ApplicationContext子介面,它最底層的介面是BeanFactory。在這個BeanFactory下面衍生了各種功能的子介面。

  1. 容器管理HierarchicalBeanFatory
  2. 自動注入AutowireCapableBeanFactory
  3. 讀取配置資訊ListableBeanFactory

可以自行找一下BeanFactory類關係圖,它有一個子實現類XmlBeanFactory,先說一下XML配置檔案的讀取

ListableBeanFactory 是讀取配置資訊的,它的子實現類XmlBeanFactory就是讀取xml檔案的具體實現。
而ApplicationContext繼承了ListableBeanFactory並對xml解析做了進一步的封裝所以再我們使用ApplicationContext
時直接給他一個對應位置的資原始檔名它就會幫我們讀取到配置資訊。

ApplicationContext ctx = new ClassPathXmlApplicatiionContext("applicationContext.xml");
User user = ctx.getBean("user"); 

//我們直接用最底層的介面 BeanFactory 獲取xml資訊

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
User user = bf.getBean("user");

說明:

  1. beanFactory 底層獲取xml檔案資訊的實現類 XmlBeanFactory 需要傳入一個 ClassPathResource 物件。這個物件的父介面就是InputStreamSource 他就是提供了一個獲取輸入流的方法
    InputStream getInputStream() throws IOException;
    
    而ClassPathResource 對這個方法的實現就是通過類或者類載入器實現的
    if (this.clazz != null) {
    	is = this.clazz.getResourceAsStream(this.path);
    }
    else if (this.classLoader != null) {
    	is = this.classLoader.getResourceAsStream(this.path);
    }
    
    獲取了輸入流那就自然可以獲取檔案的內容了。
  2. Spring獲取xml內容後通過XmlBeanDefinitionReader解析配置檔案的內容封裝成BeanDefinition方便後續使用。
    //前邊說了是XmlBeanFactory具體實現獲取xml資訊的功能
    1. public class XmlBeanFactory extends DefaultListableBeanFactory {
        //xmlBeanFactory 中直接例項化 xmlBeanDefinitionReader
    	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    .....
    	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    		super(parentBeanFactory);
    		this.reader.loadBeanDefinitions(resource);
    	}
      }
    2. public class XmlBeandefinitionReader extends AbstractBeanDefinitionReader{
    	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    		......
    		try (
    			//獲取輸入流
    			InputStream inputStream = encodedResource.getResource().getInputStream()) {
    			//xml 解析工具類  
    			InputSource inputSource = new InputSource(inputStream);
    			if (encodedResource.getEncoding() != null) {
    				inputSource.setEncoding(encodedResource.getEncoding());
    			}
    			//開始具體解析
    			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    		}
    		......
    	};
    		
    	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    		throws BeanDefinitionStoreException {
    		try {
    			// xml 解析為 document 
    			Document doc = doLoadDocument(inputSource, resource);
    			// document 轉 beanDefinition
    			int count = registerBeanDefinitions(doc, resource);
    			if (logger.isDebugEnabled()) {
    				logger.debug("Loaded " + count + " bean definitions from " + resource);
    			}
    			return count;
    		}
    	}
       }
    3. public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
       	protected void doRegisterBeanDefinitions(Element root){
       		...
       		preProcessXml(root);
       		parseBeanDefinitions(root,this,dalegate);
       		postProcessXml(root);
       	};
    
    	protected void parseBeanDefinitions(Element root,BeanDefinitionParseDelegate 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);
    					}
    				}
    			}
    		}
    	}
    	
       }
    
    解釋說明:
    1. 通過 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法得到輸入流和xml解析工具類在 doLoadBeanDefinitions方法中把輸入流也就是獲得的xml檔案資訊轉化為 Document 再通過 registerBeanDefinitions 方法封裝成 beanDefinition
    2. 封裝beanDefinition是在 DefaultBeanDefinitionDocumentReader 類中的doRegisterBeanDefinitions . parseBeanDefinitions方法做了具體功能的實現,也就是解析檔案中的標籤並和beanDefinition的屬性做對映。例如: 根標籤(beans、prefile等) 子標籤 (基本標籤 bean、import、alias等,自定義標籤 aop、mvc:annotation-driven、tx:annotation-driven、context:等)
    3. 用 BeanDefinitionParserDelegate 把解析標籤得到的值對映成beanDefinition方便後續使用。

最後

感謝您的閱讀,有什麼意見和問題歡迎評論區留言!書寫不易!
覺得文章對你有幫助記得給我點個贊,歡迎大家關注和轉發文章!

相關文章