javascript
Spring IOC源代码具体解释之容器初始化
Spring IOC源代碼具體解釋之容器初始化
上篇介紹了Spring IOC的大致體系類圖,先來看一段簡短的代碼,使用IOC比較典型的代碼
ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(res);
上面使用創建IOC經歷了四個過程
- 創建IOC配置文件的抽象資源
- 創建一個BeanFactory
- 把讀取配置信息的BeanDefinitionReader,這里是XmlBeanDefinitionReader配置給BeanFactory
- 從定義好的資源位置讀入配置信息,詳細的解析過程由XmlBeanDefinitionReader來完畢,這樣完畢整個加載bean定義的過程。
從上面能夠看出,整個IOC初始化,大致分為資源定位、資源裝載、資源解析、Bean生成,Bean注冊這幾個過程
首先看資源的定位,一個常見的ApplicationContext
ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);追蹤FileSystemXmlApplicationContext。里面有一些構造函數,都指向以下這個
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)throws BeansException {super(parent); //有前面類圖 可 以知道,終于 父類 為AbstractApplicationContextsetConfigLocations(configLocations);if (refresh) {refresh();}} //追蹤super,終于定位到 AbstractApplicationContext中public AbstractApplicationContext() {this.resourcePatternResolver = getResourcePatternResolver();}protected ResourcePatternResolver getResourcePatternResolver() {return new PathMatchingResourcePatternResolver(this);}繼續往下,追蹤setConfigLocations方法
public void setConfigLocations(String... locations) {if (locations != null) {Assert.noNullElements(locations, "Config locations must not be null");this.configLocations = new String[locations.length];// 該方法調用 SystemPropertyUtils.resolvePlaceholders(path) ;對 path 中的占位 符進行// 替換, eg : path 路徑中含有 ${user.dir} ,則將替換為: System.getProperty(user.dir);for (int i = 0; i < locations.length; i++) {this.configLocations[i] = resolvePath(locations[i]).trim();}}else {this.configLocations = null;}} 代碼中 configLocations 為 AbstractRefreshableConfigApplicationContext 類中 string[] 類型的字段。至此應用程序傳入路徑保存在 AbstractRefreshableConfigApplicationContext 中 。
回到FileSystemXmlApplicationContext中繼續往下。即最關鍵的Refash方法,refresh()-方法定義在類: AbstractApplicationContext ,由類圖可知: FileSystemXmlApplicationContext 間接繼承至: AbstractApplicationContext refresh() 方法例如以下:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //調用容器準備刷新的方法,獲取 容器的當時時間,同一時候給容器設置同步標識 prepareRefresh(); //告訴子類啟動refreshBeanFactory()方法。Bean定義資源文件的載入從 //子類的refreshBeanFactory()方法啟動 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //為BeanFactory配置容器特性,比如類載入器、事件處理器等 prepareBeanFactory(beanFactory); try { //為容器的某些子類指定特殊的BeanPost事件處理器 postProcessBeanFactory(beanFactory); //調用全部注冊的BeanFactoryPostProcessor的Bean invokeBeanFactoryPostProcessors(beanFactory); //為BeanFactory注冊BeanPost事件處理器. //BeanPostProcessor是Bean后置處理器。用于監聽容器觸發的事件 registerBeanPostProcessors(beanFactory); //初始化信息源,和國際化相關. initMessageSource(); //初始化容器事件傳播器. initApplicationEventMulticaster(); //調用子類的某些特殊Bean初始化方法 onRefresh(); //為事件傳播器注冊事件監聽器. registerListeners(); //初始化全部剩余的單態Bean. finishBeanFactoryInitialization(beanFactory); //初始化容器的生命周期事件處理器,并公布容器的生命周期事件 finishRefresh(); } catch (BeansException ex) { //銷毀以創建的單態Bean destroyBeans(); //取消refresh操作。重置容器的同步標識. cancelRefresh(ex); throw ex; } } }
-
prepareRefresh(): 為刷新準備上下文環境
- obtainFreshBeanFactory() :讓子類刷新內部 bean 工廠。
進入obtainFreshBeanFatory()。關閉前面全部 bean 工廠,為新的上下文環境初始化一個新的 bean 工廠。這里須要子類來 協助完畢資源位置定義 ,bean 加載和向 IOC 容器注冊的過程
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {refreshBeanFactory();ConfigurableListableBeanFactory beanFactory = getBeanFactory();if (logger.isDebugEnabled()) {logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);}return beanFactory;}
上面代碼能夠看到refreshBeanFactory,AbstractApplicationContext子類中的方法 AbstractApplicationContext類中僅僅抽象定義了refreshBeanFactory()方法。容器真正調用的是其子類AbstractRefreshableApplicationContext實現的 refreshBeanFactory()方法,方法的源代碼例如以下
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) {//假設已經有容器。銷 毀 容器中的bean,關閉容器 destroyBeans(); closeBeanFactory(); } try { //創建IoC容器 DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); //對IoC容器進行定制化,如設置啟動參數,開啟注解的自己主動裝配等 customizeBeanFactory(beanFactory); //調用加載Bean定義的方法,主要這里使用了一個委派模式。在當前類中僅僅定義了抽象的loadBeanDefinitions方法,詳細的實現調用子類容器 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } 在這種方法中,先推斷BeanFactory是否存在,假設存在則先銷毀beans并關閉beanFactory,接著創建DefaultListableBeanFactory,并調用loadBeanDefinitions(beanFactory)裝載bean
在這種方法中,先推斷BeanFactory是否存在,假設存在則先銷毀beans并關閉beanFactory,接著創建DefaultListableBeanFactory,并調用loadBeanDefinitions(beanFactory)裝載bean定義??瓷厦娴膌oadBeanDefinitions。在AbstractRefreshableApplicationContext子類。
相同。AbstractRefreshableApplicationContext中僅僅定義了抽象的loadBeanDefinitions方法,容器真正調用的是其子類AbstractXmlApplicationContext對該方法的實現,AbstractXmlApplicationContext的主要源代碼例如以下: protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {// 這里使用XMLBeanDefinitionReader來加載bean定義信息的XML文件XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);//這里配置reader的環境,當中ResourceLoader是我們用來定位bean定義信息資源位置的///由于上下文本身實現了ResourceLoader接口,所以能夠直接把上下文作為ResourceLoader傳遞給XmlBeanDefinitionReaderbeanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));initBeanDefinitionReader(beanDefinitionReader);//這里轉到定義好的XmlBeanDefinitionReader中對加載bean信息進行處理loadBeanDefinitions(beanDefinitionReader);}
繼續,看到了最以下的loadBeanDefinitions(beanDefinitionReader);轉到XmlBeanDefinitionReader中的loadBeanDefinitions。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = getConfigResources();if (configResources != null) {//調用XmlBeanDefinitionReader來加載bean定義信息。 reader.loadBeanDefinitions(configResources);}String[] configLocations = getConfigLocations();if (configLocations != null) {reader.loadBeanDefinitions(configLocations);}} Xml Bean讀取器(XmlBeanDefinitionReader)調用其父類AbstractBeanDefinitionReader的 reader.loadBeanDefinitions方法讀取Bean定義資源。
因為我們使用FileSystemXmlApplicationContext作為樣例分析,因此getConfigResources的返回值為null,因此程序運行reader.loadBeanDefinitions(configLocations)分支。
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { //這里得到當前定義的ResourceLoader,默認的 我 們 使用DefaultResourceLoaderResourceLoader resourceLoader = getResourceLoader();.........//假設沒有找到我們須要的ResourceLoader,直接拋出異常if (resourceLoader instanceof ResourcePatternResolver) {// 這里處理我們在定義位置時使用的各種pattern,須要ResourcePatternResolver來完畢try {Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);int loadCount = loadBeanDefinitions(resources);return loadCount;}........}else {// 這里通過ResourceLoader來完畢位置定位Resource resource = resourceLoader.getResource(location);// 這里已經把一個位置定義轉化為Resource接口,能夠供XmlBeanDefinitionReader來使用了int loadCount = loadBeanDefinitions(resource);return loadCount;}} 重載方法public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {Assert.notNull(locations, "Location array must not be null");int counter = 0;for (String location : locations) {counter += loadBeanDefinitions(location);}return counter;}
loadBeanDefinitions(Resource...resources)方法和上面分析的3個方法類似,相同也是調用XmlBeanDefinitionReader的loadBeanDefinitions方法。
從對AbstractBeanDefinitionReader的loadBeanDefinitions方法源代碼分析能夠看出該方法做了下面兩件事:
-
首先,調用資源載入器的獲取資源方法resourceLoader.getResource(location)。獲取到要載入的資源。
-
其次,真正運行載入功能是其子類XmlBeanDefinitionReader的loadBeanDefinitions方法。
由上類圖能夠知道此時調用的是DefaultResourceLoader中的getSource()方法定位Resource。由于FileSystemXmlApplicationContext本身就是DefaultResourceLoader的實現類。
2、資源載入
XmlBeanDefinitionReader通過調用其父類DefaultResourceLoader的getResource方法獲取要載入的資源,這樣,就能夠從文件系統路徑上對IOC配置文件進行載入 - 當然能夠依照這個邏輯從不論什么地方載入,在Spring中看到它提供的各種資源抽象。比方ClassPathResource, URLResource,FileSystemResource等來供我們使用。
//獲取Resource的具 體實現方法 public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); //假設是類路徑的方式。那須要使用ClassPathResource 來得到bean 文件的資源對象 if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } try { // 假設是URL 方式,使用UrlResource 作為bean 文件的資源對象 URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { } //假設既不是classpath標識。又不是URL標識的Resource定位,則調用 //容器本身的getResourceByPath方法獲取Resource return getResourceByPath(location); }
FileSystemXmlApplicationContext容器提供了getResourceByPath方法的實現。就是為了處理既不是classpath標識,又不是URL標識的Resource定位這樣的情況
@Overrideprotected Resource getResourceByPath(String path) {if (path != null && path.startsWith("/")) {path = path.substring(1);}return new FileSystemResource(path);}
所以此時又回到了FileSystemXmlApplicationContext中來,提供了FileSystemResource來完畢從文件系統得到配置文件的資源定義。
Bean資源載入
繼續XmlBeanDefinitionReader的loadBeanDefinitions(Resource …)
//XmlBeanDefinitionReader加載資源 的入口方法 public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { //將讀入的XML資源進行特殊編碼處理 return loadBeanDefinitions(new EncodedResource(resource)); } //這里是加載XML形式Bean定義資源文件方法public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { ....... try { //將資源文件轉為InputStream的IO流 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //從InputStream中得到XML的解析源 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //這里是具體的讀取過程 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { //關閉從Resource中得到的IO流 inputStream.close(); } } ......... 26} //從特定XML文件里實際加載Bean定義資源的方法 protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { int validationMode = getValidationModeForResource(resource); //將XML文件轉換為DOM對象,解析過程由documentLoader實現 Document doc = this.documentLoader.loadDocument( inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware); //這里是啟動對Bean定義解析的具體過程,該解析過程會用到Spring的Bean配置規則return registerBeanDefinitions(doc, resource); } ....... }
源代碼的最后。將XML文件轉為DOM對象,即將Bean對象轉成DOm資源。這是怎樣完畢的呢?
3、資源解析
2中Bean定義的Resource得到了。最后還講到ocumentLoader將Bean定義資源轉換為Document對象:DocumentLoader將Bean定義資源轉換成Document對象的源代碼例如以下:
以下這句是XmlBeanDefinitionReader中關于DocumentLoader。用了DefaultDocumentLoaderprivate DocumentLoader documentLoader = new DefaultDocumentLoader();
上面的DefaultDocumentLoader肯定不陌生吧。在Spring AOP中多次出現了。找到DefaultDocumentLoader中的loadDocument()方法
//使用標準的JAXP 將 加載的B ean 定義 資源 轉換成document對象 public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { //創建文件解析器工廠 DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } //創建文檔解析器 DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); //解析Spring的Bean定義資源 return builder.parse(inputSource); }
繼續找到createDocumentBuilder
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)throws ParserConfigurationException {DocumentBuilder docBuilder = factory.newDocumentBuilder();if (entityResolver != null) {docBuilder.setEntityResolver(entityResolver);}if (errorHandler != null) {docBuilder.setErrorHandler(errorHandler);}return docBuilder;}
上面調用了Javaee的JAXP標準。依據定位的Bean定義資源文件,載入讀入并轉換成為Document對象過程完畢
4、bean生成(依據DOM資源解析成bean)
回想3: XmlBeanDefinitionReader類中的doLoadBeanDefinitions方法是從特定XML文件里實際加載Bean定義資源的方法,該方法在加載Bean定義資源之后將其轉換為Document對象。繼續看doLoadBeanDefinitions,進入registerBeanDefinitions
Document doc = this.documentLoader.loadDocument()后面。有一句return registerBeanDefinitions(doc, resource); //依照Spring的Bean語義要求 將Bean 定 義 資源解析并轉換為容器內部數據結構 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //得到BeanDefinitionDocumentReader來對xml格式的BeanDefinition解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //獲得容器中注冊的Bean數量 int countBefore = getRegistry().getBeanDefinitionCount(); //解析過程入口。這里使用了委派模式,BeanDefinitionDocumentReader僅僅是個接口,//詳細的解析實現過程有實現類DefaultBeanDefinitionDocumentReader完畢 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //統計解析的Bean數量 return getRegistry().getBeanDefinitionCount() - countBefore; } //創建BeanDefinitionDocumentReader對象。 解 析 D ocument對象 protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass)); }
Bean定義資源的加載解析分為下面兩個過程:
-
首先。通過調用XML解析器將Bean定義資源文件轉換得到Document對象,可是這些Document對象并沒有依照Spring的Bean規則進行解析。這一步是加載的過程
-
其次,在完畢通用的XML解析之后,依照Spring的Bean規則對Document對象進行解析。
繼續
documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 這里的documentReader為BeanDefinitionDocumentReader對象,BeanDefinitionDocumentReader接口通過registerBeanDefinitions方法調用事實上現類DefaultBeanDefinitionDocumentReader對Document對象進行解析 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;//獲得XML 描 述 符 logger.debug("Loading bean definitions");Element root = doc.getDocumentElement();//獲得Document的根元素 doRegisterBeanDefinitions(root);} protected void doRegisterBeanDefinitions(Element root) {//BeanDefinitionParserDelegate中定義了Spring Bean 定 義 XML 文件的各種元素BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);//以下有解析if (this.delegate.isDefaultNamespace(root)) { //這里應該是推斷是否使用默認命名空間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;}}}//在解析Bean定義之前,進行自己定義的解析,增強解析過程的可擴展性 preProcessXml(root);//從Document的根元素開始進行Bean定義的Document對象,見以下解析parseBeanDefinitions(root, this.delegate);//在解析Bean定義之后,進行自己定義的解析,添加解析過程的可擴展性 postProcessXml(root);this.delegate = parent;} //上面的createDelegate()方法, 創建BeanDefinitionParserDelegate,用于完畢真正的解析過程 protected BeanDefinitionParserDelegate createDelegate(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);//BeanDefinitionParserDelegate初始化Document根元素 delegate.initDefaults(root, parentDelegate);return delegate;} //上面的parseBeanDefinitions// 使 用Spring的Bean規則從Document的根元素開始進行Bean定義的Document對象 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {//Bean定義的Document對象使用了Spring默認的XML命名空間 if (delegate.isDefaultNamespace(root)) {//獲取Bean定義的Document對象根元素的全部子節點 NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {//獲得Document節點是XML元素節點 Node node = nl.item(i); if (node instanceof Element) {Element ele = (Element) node;if (delegate.isDefaultNamespace(ele)) {//假設使用的是默認命名控件,使用Spring的Bean規則解析元素節點 parseDefaultElement(ele, delegate);}else {//沒有使用Spring默認的XML命名空間。則使用用戶自己定義的解//析規則解析元素節點 delegate.parseCustomElement(ele);}}}}else {//使用默認解析規則解析Document根節點 delegate.parseCustomElement(root);}} //使用默 認 規 則 是個什么樣子呢private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {//元素節點是IMPORT導入if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}//元素節點是Alias相關else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}//元素節點是Bean元素else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {processBeanDefinition(ele, delegate);}else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}}上面代碼的三中Import、Alials、Bean我們主要來看Bean的解析,追蹤processBeanDefinition(ele, delegate
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {//托付給BeanDefinitionParserDelegate來完畢對bean元素的 處 理 。這個類包括了詳細的bean解析的過程。 // 把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要載體。也是IOC容器的管理對象。
//BeanDefinitionHolder是對BeanDefinition的封裝。即Bean定義的封裝類 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) {//假設存在bean定義的話 //對Document對象中<Bean>元素的解析由BeanDefinitionParserDelegate實現 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { //向Spring IoC容器注冊解析得到的Bean定義。這是Bean定義向IoC容器注冊的入口 。實際上是放到一個Map里面 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } //在完畢向Spring IoC容器注冊解析得到的Bean定義之后。發送注冊事件 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
這里事實上還能夠深入了解,bean的解析。追蹤上面的這條
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
能夠進入parseBeanDefinitionElement方法中,里面具體講了Bean元素是怎樣解析的。
//解析<Bean>元素的入口 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); } //解析Bean定義資源文件里的<Bean>元素。這個 方 法中主要處理<Bean>元素的id,name //和別名屬性 public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { //獲取<Bean>元素中的id屬性值 String id = ele.getAttribute(ID_ATTRIBUTE); //獲取<Bean>元素中的name屬性值 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); 獲取<Bean>元素中的alias屬性值 List<String> aliases = new ArrayList<String>(); //將<Bean>元素中的全部name屬性值存放到別名中 if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; //假設<Bean>元素中沒有配置id屬性時,將別名中的第一個值賦值給beanName if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isDebugEnabled()) { logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } //檢查<Bean>元素所配置的id或者name的唯一性,containingBean標識<Bean> //元素中是否包括子<Bean>元素 if (containingBean == null) { //檢查<Bean>元素所配置的id、name或者別名是否反復 checkNameUniqueness(beanName, aliases, ele); } //具體對<Bean>元素中配置的Bean定義進行解析的地方 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { //假設<Bean>元素中沒有配置id、別名或者name。且沒有包括子//<Bean>元素,為解析的Bean生成一個唯一beanName并注冊 beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { //假設<Bean>元素中沒有配置id、別名或者name,且包括了子//<Bean>元素,為解析的Bean使用別名向IoC容器注冊 beanName = this.readerContext.generateBeanName(beanDefinition); //為解析的Bean使用別名注冊時,為了向后兼容 //Spring1.2/2.0。給別名加入類名后綴 String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } //當解析出錯時,返回null return null; } //具體對<Bean>元素中配置的Bean定義其它屬性進行解析,因為上面的方法中已經對//Bean的id、name和別名等屬性進行了處理,該方法中主要處理除這三個以外的其它屬性數據 public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { //記錄解析的<Bean> this.parseState.push(new BeanEntry(beanName)); //這里僅僅讀取<Bean>元素中配置的class名字,然后加載到BeanDefinition中去 //僅僅是記錄配置的class名字,不做實例化,對象的實例化在依賴注入時完畢 String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; //假設<Bean>元素中配置了parent屬性。則獲取parent屬性的值 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } //依據<Bean>元素配置的class名稱和parent屬性值創建BeanDefinition //為加載Bean定義信息做準備 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //對當前的<Bean>元素中配置的一些屬性進行解析和設置。如配置的單態(singleton)屬性等 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //為<Bean>元素解析的Bean設置description信息 bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //對<Bean>元素的meta(元信息)屬性解析 parseMetaElements(ele, bd); //對<Bean>元素的lookup-method屬性解析 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //對<Bean>元素的replaced-method屬性解析 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //解析<Bean>元素的構造方法設置 parseConstructorArgElements(ele, bd); //解析<Bean>元素的<property>設置 parsePropertyElements(ele, bd); //解析<Bean>元素的qualifier屬性 parseQualifierElements(ele, bd); //為當前解析的Bean設置所需的資源和依賴對象 bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } catch (ClassNotFoundException ex) { error("Bean class [" + className + "] not found", ele, ex); } catch (NoClassDefFoundError err) { error("Class that bean class [" + className + "] depends on not found", ele, err); } catch (Throwable ex) { error("Unexpected failure during bean definition parsing", ele, ex); } finally { this.parseState.pop(); } //解析<Bean>元素出錯時。返回null return null; }
上面方法中一些對一些配置如元信息(meta)、qualifier等的解析。我們在Spring中配置時使用的也不多。我們在使用Spring的元素時,配置最多的是property屬性
//解析<property>元素中ref,value或者集合等子元素 public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) { //假設<property>沒有使用Spring默認的命名空間,則使用用戶自己定義的規則解析//內嵌元素 if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } //假設子元素是bean,則使用解析<Bean>元素的方法解析 else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null) { nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } return nestedBd; } //假設子元素是ref。ref中僅僅能有下面3個屬性:bean、local、parent else if (nodeNameEquals(ele, REF_ELEMENT)) { //獲取<property>元素中的bean屬性值,引用其它解析的Bean的名稱 //能夠不再同一個Spring配置文件里。詳細請參考Spring對ref的配置規則 String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (!StringUtils.hasLength(refName)) { //獲取<property>元素中的local屬性值。引用同一個Xml文件里配置 //的Bean的id,local和ref不同,local僅僅能引用同一個配置文件里的Bean refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE); if (!StringUtils.hasLength(refName)) { //獲取<property>元素中parent屬性值,引用父級容器中的Bean refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; if (!StringUtils.hasLength(refName)) { error("'bean', 'local' or 'parent' is required for <ref> element", ele); return null; } } } //沒有配置ref的目標屬性值 if (!StringUtils.hasText(refName)) { error("<ref> element contains empty target attribute", ele); return null; } //創建ref類型數據。指向被引用的對象 RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); //設置引用類型值是被當前子元素所引用 ref.setSource(extractSource(ele)); return ref; } //假設子元素是<idref>。使用解析ref元素的方法解析 else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } //假設子元素是<value>,使用解析value元素的方法解析 else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } //假設子元素是null。為<property>設置一個封裝null值的字符串數據 else if (nodeNameEquals(ele, NULL_ELEMENT)) { TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } //假設子元素是<array>,使用解析array集合子元素的方法解析 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } //假設子元素是<list>,使用解析list集合子元素的方法解析 else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } //假設子元素是<set>,使用解析set集合子元素的方法解析 else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } //假設子元素是<map>,使用解析map集合子元素的方法解析 else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } //假設子元素是<props>,使用解析props集合子元素的方法解析 else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } //既不是ref,又不是value。也不是集合。則子元素配置錯誤,返回null else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } }
上面代碼中對property元素中配置的Array、List、Set、Map、Prop等各種集合子元素的都通過上述方法解析。生成相應的數據對象,比方ManagedList、ManagedArray、ManagedSet等,這些Managed類是Spring對象BeanDefiniton的數據封裝。對集合數據類型的詳細解析有各自的解析方法實現。假設想了解當中的一種集合是怎樣解析的 ,進入相應的方法。
5、Bean是怎樣在IOC容器中注冊的
以下看解析完的bean是如何在IOC容器中注冊的:接著4中的代碼,進入
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {//得到須要 注冊 的 bean名字String beanName = definitionHolder.getBeanName();//開始注冊registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// 別名也是能夠 通過IOC容器和bean聯系起來的進行注冊 String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {registry.registerAlias(beanName, alias);}}} 上面的registry對象是BeanDefinitionRegistry,上一步是BeanDefinitionReaderUtils.registerBeanDefinition(),即當調用BeanDefinitionReaderUtils向IoC容器注冊解析的BeanDefinition時。真正完畢注冊功能的是DefaultListableBeanFactory。
那么DefaultListableBeanFactory怎么會和BeanDefinitionReaderUtils扯到一起呢?
事實上上面的類圖還忽略了一點,就是
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactoryimplements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {}DefaultListableBeanFactory 實現了BeanDefinitionRegistry接口。這下就豁然開朗,那么DefaultListableBeanFactory是怎樣注冊的呢DefaultListableBeanFactory中使用一個HashMap的集合對象存放IoC容器中注冊解析的BeanDefinition。
@Override//---------------------------------------------------------------------// 這里是IOC容器對BeanDefinitionRegistry接口的實現//---------------------------------------------------------------------public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {.....//這里省略了對BeanDefinition的驗證過程//先看看在容器里是不是已經有了同名的bean,假設有拋出異常。Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);if (oldBeanDefinition != null) {if (!this.allowBeanDefinitionOverriding) {...........}else {//把bean的名字加到IOC容器中去this.beanDefinitionNames.add(beanName);}//這里把bean的名字和Bean定義聯系起來放到一個HashMap中去,IOC容器通過這個Map來維護容器里的Bean定義信息。this.beanDefinitionMap.put(beanName, beanDefinition);removeSingleton(beanName);}
這樣就完畢了Bean定義在IOC容器中的注冊。就可被IOC容器進行管理和使用了
總結IOC初始化過程
-
1、setConfigLocations方法
-
2、初始化的入口在容器實現中的 refresh()調用來完畢
-
3、AbstractRefreshableApplicationContext實現的 refreshBeanFactory()方法
-
4、創建DefaultListableBeanFactory,并調用loadBeanDefinitions(beanFactory)裝載bean定義
-
5、轉到XmlBeanDefinitionReader中的loadBeanDefinitions。
-
6、XmlBeanDefinitionReader通過調用其父類DefaultResourceLoader的getResource方法獲取要載入的資源
-
7 、DocumentLoader將Bean定義資源轉換成Document對象
-
8、doLoadBeanDefinitions中進入registerBeanDefinitions 解 析 D ocument對象
-
9、DefaultListableBeanFactory中使用一個HashMap的集合對象存放IoC容器中注冊解析的BeanDefinition,
總結
以上是生活随笔為你收集整理的Spring IOC源代码具体解释之容器初始化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python基础一 -------如何在
- 下一篇: AngularJS+RequireJS集