解析Java的Spring架構的基本結構_java

來源:互聯網
上載者:User

   在java屆,有位名叫Rod Johnson的牛人,發現最初的java企業級開發處於混沌狀態。

   於是,它決心編寫一個能夠解決問題的通用的基礎架構。

   因為它深信面向介面編程能夠將變化控制到最小,同時也利於擴充和變化。於是,它編寫了如下的介面。 

   在混沌狀態最先要創造的是一切對象的母親BeanFactory,有了它,就能夠得到一切它孕育的對象和屬性,也就是說首先要造蓋亞--大地之母。

   有了最初的母親BeanFactory,johnson想,如果我要得到一組Bean對象而不單單是某個或某幾個呢?另外,如果母親的孩子也要孕育對象呢?於是,johnson創造了ListableBeanFactory以操作一組bean對象,比如getBeansOfType就能夠根據得到同類型的一組Bean;創造了HierarchicalBeanFactory來解決多個BeanFactory的層次問題,比如getParentBeanFactory就能夠得到BeanFactory的父Factory。

   這個BeanFactory最終是要在某個應用上使用的,那麼,需要給予BeanFactory在一個應用中活動的能力。在BeanFactory中,只需要考慮跟bean相關的行為,比如怎麼得到bean,bean的類型等;而如果要賦予其在應用中的能力,則就需要考慮更多,比如應用的名字,啟動時間,id等等跟應用本身相關的行為和屬性,於是johnson想到了創造ApplicationContext。johnson想,這個ApplicationContext一定要能做許多事,要能夠處理參數化和國際化的文本資訊,於是增加了MessageSource介面;要能發布事件以便解耦組件,於是有了ApplicationEventPublisher介面;要能得到資源檔,於是有了ResourcePatternResolver介面;要能有在不同環境有不同處理對象的能力,於是有了EnvironmentCapable介面。

   ApplicationContext繼承了所有這些介面。

   但是最重要的是,無論是BeanFactory還是ApplicationContext,它們都需要有可配置的能力,於是有了子介面ConfigurableBeanFactory和ConfigurableApplicationContext;另外,web在當時是非常重要的趨勢,而且相比其他應用有些獨特,需要得到ServletContext,於是有了WebApplicationContext。

   到目前為止,johnson都是面向介面進行行為的抽象思考,並未具體實現他們。

   看著創造出來的BeanFactory和ApplicationContext,johnson意識到這一天的工作遠遠沒有結束,因為並沒有真正解決怎麼能夠讓整套體系運轉起來,於是,johnson開始思索如何?他們。

   johoson首先想到的還是這個實現應該具備什麼能力?當然要把之前提到的AutowireCapableBeanFactory,ListableBeanFactory和ConfigurableBeanFactory都包括進去。因此創造了ConfigurableListableBeanFactory。其次,需要考慮對於bean對象的幾種能力,一是起別名的能力;二是儲存單例對象的能力;三是緩衝的能力;他們分別在SimpleAliasRegistry類,DefaultSingletonBeanRegistry類和FactoryBeanRegistrySupport類中實現。

   最終,創造出了DefaultListableBeanFactory,它是spring中一切ioc工廠的原型,是BeanFactory第一個真正的孩子,這個孩子非常重要,已經成為獨立的建立ioc容器的基礎,如果有擴充和使用,大多是繼承它或者組合使用它。

  如果要初始化一個DefaultListableBeanFactory,可以用如下代碼

ClassPathResource res = new ClassPathResource("applicationContext.xml");     DefaultListableBeanFactory f = new DefaultListableBeanFactory();     XmlBeanDefinitionReader r = new XmlBeanDefinitionReader(f);     r.loadBeanDefinitions(res); 

  接下來johnson想,BeanFactory有了一個功能比較全面的預設實現,那麼ApplicationContext呢?於是johnson孜孜不倦的創造了3個重要的ApplicationContext實現:FileSystemXmlApplicationContext, ClassPathXmlApplicationContext, AnnotationConfigWebApplicationContext(其實還有很多,比如處理portlet的, 處理web的)

    johnson最先考慮的是如何去做spring的啟動流程,它應該放到一個比較抽象的層次以便下層的所有類能夠複用。於是他用一個AbstractApplicationContext實現了ConfigurableApplicationContext。還有一個很重要的功能,即將一個檔案以資源的形式載入進來,這需要將資源抽象為Resource類,將定位資源的具體實現抽象到ResourceLoader,AbstractApplicationContext同樣需要繼承DefaultResourceLoader以提供這個功能。AbstractApplicationContext完成了整個啟動流程(上帝將它安排在第二天完成),唯獨沒有做對BeanFactory的管理。於是,它的子類AbstractRefreshableApplicationContext專門做了這件事,實現了refreshBeanFactory, closeBeanFactory, getBeanFactory專門對BeanFactory的生命週期做了一些管理,但是AbstractRefreshableApplicationContext仍然沒有載入所有配置好的Bean。到哪裡載入配置好的資源,實際上到了下層的子類去做,比如FileSystemXmlApplicationContext,就是到檔案系統去讀一個xml形式的applicationContext;ClassPathXmlApplicationContext則是到類載入路徑下去讀這個applicationContext。而AnnotationConfigWebApplicationContext則從類檔案的annotation中載入bean,spring的掃描也從此開始。

  看著主要的架構已經建立起來,johnson滿意的笑著睡著了。
 
   頭一日,johnson完成了一個spring的整體架構。

  第二日,johnson準備實際去處理前面遺留的問題。比如spring的容器初始化過程。如圖,johnson將這個過程分為很多子過程,這些子過程都在圍繞著如何將bean載入這一宏偉的目標而努力。

    這個過程放在AbstractApplicationContext中的refresh方法中。代碼如下,johnson將refresh的過程分為很多子過程,並且這些子過程在同一個抽象層級上,這種寫法是為了給後人一個榜樣。

public void refresh() throws BeansException, IllegalStateException {   synchronized (this.startupShutdownMonitor) {     // Prepare this context for refreshing.     prepareRefresh();      // Tell the subclass to refresh the internal bean factory.     ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();      // Prepare the bean factory for use in this context.     prepareBeanFactory(beanFactory);      try {       // Allows post-processing of the bean factory in context subclasses.       postProcessBeanFactory(beanFactory);        // Invoke factory processors registered as beans in the context.       invokeBeanFactoryPostProcessors(beanFactory);        // Register bean processors that intercept bean creation.       registerBeanPostProcessors(beanFactory);        // Initialize message source for this context.       initMessageSource();        // Initialize event multicaster for this context.       initApplicationEventMulticaster();        // Initialize other special beans in specific context subclasses.       onRefresh();        // Check for listener beans and register them.       registerListeners();        // Instantiate all remaining (non-lazy-init) singletons.       finishBeanFactoryInitialization(beanFactory);        // Last step: publish corresponding event.       finishRefresh();     }      catch (BeansException ex) {       // Destroy already created singletons to avoid dangling resources.       destroyBeans();        // Reset 'active' flag.       cancelRefresh(ex);        // Propagate exception to caller.       throw ex;     }   } } 

  如果更高層次一些看,實際上這些過程圍繞著幾個方面來做:1. 重新整理的生命週期;2. 對beanFactory的初始化及準備;3. 產生並註冊beanDefinition;4. beanFactory後處理器;5.設定訊息,事件及監聽器。
  1. 重新整理的生命週期

    prepareRefresh,這個過程主要記錄日誌表示spring啟動了,初始化property資源(比如serlvet中一些資源初始化),以及property資源的驗證(比如唯寫了key沒有value)。

  onRefresh,目的是提供給一些特殊的ApplicationContext,使他們有能夠在重新整理過程中能夠擴充的能力。目前使用到的大多是為servlet application context設定theme

  finishRefresh,做一些收尾的工作,如初始化LifecycleProcessor,發布refresh結束的事件等。

  cancelRefresh,主要是在異常產生時將當前的狀態改變為非active。

  2. 對beanFactory的初始化及準備

  johnson想,我們應該讓beanFactory在初始化時就把bean透明的載入並註冊好,這樣對外界而言,我的封裝就非常成功,因此這步實際上做了很多事。下圖省略了許多步驟,只列出關鍵點。

  AbstractApplicationContext會調用refreshBeanFactory,它首先會檢查並關閉已有的beanFactory,其次建立一個beanFactory,然後利用該factory裝載所有BeanDefinition

  其中,loadBeanDefinitions會交給子類做不同的實現,比如AbstractXmlApplicationContext主要是通過xml讀取;AnnotationConfigWebApplicationContext的實現則會調用掃描器掃描類中的bean

   3. 產生並註冊beanDefinition
  當解析完xml配置以後,DefaultBeanDefinitionDocumentReader的parseDefaultElement方法會根據xml中的元素做對應的處理。其中,遇到bean元素時會最終調用BeanDefinitionReaderUtils中的registerBeanDefinition方法,該方法傳入的參數為BeanDefinitionRegistry,實際上是回調了DefaultListableBeanFactory的registerBeanDefinition方法來註冊beanDefinition(DefaultListableBeanFactory實現了BeanDefinitionRegistry)。

   4. beanFactory後處理器

   beanFactory後處理器是spring提供出來的讓其子類靈活擴充的方式。spring中分為2個步驟:postProcessBeanFactory,invokeBeanFactoryPostProcessors。registerBeanPostProcessors則是執行個體化並調用所有的BeanPostProcessor,用來在bean初始化前和初始化後對bean做擴充。

   5. 設定訊息,事件及監聽器

  設定預設訊息源為DelegatingMessageSource,如工廠裡已經有messageSource則使用該messageSource,事件多播器為SimpleApplicationEventMulticaster,如工廠裡已經有applicationEventMulticaster,則使用該applicationEventMulticaster,並註冊所有的應用程式Listener以便能夠接收事件

  訊息源:MessageSource是國際化資源檔的重要方法,spring在applicationContext就支援訊息源。

  spring中提供了MessageSource的預設實現,使用java.util.ResourceBundle來提取訊息。spring通過配置一個特殊id為messageSource的bean並制定i18n的檔案名稱,就能夠從ApplicationContext.getMessage()直接存取message。如果在jsp中,還能通過spring:message這個tag訪問到message。

  事件:事件是比較重要的解耦機制,spring在核心ApplicationContext就引入了它,其原理比較簡單,一方面是事件產生方,能夠發送事件;一方面似乎事件監聽方,能夠響應事件。而具體實現基本上都是在產生方hold住一個事件監聽者集合,並將所有監聽方“註冊”(即加入)到這個事件監聽者集合。

  spring中將ApplicationContext當做事件產生方,使用applicationListeners作為監聽者集合,applicationEventMulticaster用來做事件發布。

  事件發布的幾個步驟:

  訂閱:最初addApplicationListener將applicationListener加入監聽者集合。

  發布:ApplicationContext繼承了ApplicationEventPublisher,因而實現了publishEvent,該方法首先會遍曆本applicationContext的applicationListeners集合,對每個listener調用onApplicationEvent,因此每個listener都會被通知到;這步完成後會對applicationContext的parent的所有applicationListeners做同樣的事件發布。

  事件發布非常常用,不僅我們自己的應用可以使用這個事件發布,spring架構自身也在使用事件發布。下面是一些spring中事件的應用:

  在finishRefresh中會發布ContextRefreshedEvent表明refresh結束藉此通知listener。在ApplicationContext中start和stop方法會發布事件表示context開始或結束。
  johnson將spring的主架構與運行流程創造完畢之後,發覺spring中提供了許多靈活擴充的地方。於是johnson準備在第三日將這些靈活擴充的用法公布出來。

  1. BeanPostProcessor。BeanPostProcessor提供了bean建立完成後的擴充介面,當你需要在bean建立完後對其做一定處理,則BeanPostProcessor是首選的方式。

  2. Aware。注入的bean需要瞭解其容器的某些部分,spring通過Aware完成回調,如BeanNameAware,可以讓bean得知自己的名字, BeanFactoryAware可以讓bean瞭解到BeanFactory, ApplicationContextAware,可以讓bean操作ApplicationContext。通過這種方式,注入spring的bean能夠做更加廣泛的事情。

  對於BeanPostProcessor的擴充,spring自身有一個例子,即如何識別Aware bean的例子。Aware bean是比較特殊的bean,需要spring對其額外注入一些屬性,那麼注入的過程spring會怎麼做呢?實際上spring並沒有將他寫在核心的處理過程裡面,而是放到了ApplicationContextAwareProcessor這個BeanPostProcessor,通過BeanPostProcessor的postProcessBeforeInitialization最終invokeAwareInterfaces以判斷該bean的類型並注入相應的屬性。這種做法利用了BeanPostProcessor完成了另一個擴充用法,實在是高超。

private void invokeAwareInterfaces(Object bean) {     if (bean instanceof Aware) {       if (bean instanceof EnvironmentAware) {         ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());       }       if (bean instanceof EmbeddedValueResolverAware) {         ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(             new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));       }       if (bean instanceof ResourceLoaderAware) {         ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);       }       if (bean instanceof ApplicationEventPublisherAware) {         ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);       }       if (bean instanceof MessageSourceAware) {         ((MessageSourceAware) bean).setMessageSource(this.applicationContext);       }       if (bean instanceof ApplicationContextAware) {         ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);       }     }   } 

  關於Aware的用法則更多了,比如如下代碼能夠感知ApplicationContext,spring在建立完這個bean之後便會注入ApplicationContext,於是我們就可以使用該context完成事件發布。

public class HelloBean implements ApplicationContextAware {      private ApplicationContext applicationContext;    private String helloWord = "Hello!World!";      public void setApplicationContext(ApplicationContext context) {      this.applicationContext = context;    }      public void setHelloWord(String helloWord) {      this.helloWord = helloWord;    }      public String getHelloWord() {      applicationContext.publishEvent(          new PropertyGettedEvent("[" + helloWord + "] is getted"));      return helloWord;    }  }  

  3. BeanFactoryPostProcessor,這個PostProcessor通常是用來處理在BeanFactory建立後的擴充介面。一個例子如下,當注入這個bean以後,它便會在BeanFactory建立完畢自動列印注入的bean數量:

public class BeanCounter implements BeanFactoryPostProcessor{    @Override   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)       throws BeansException {     System.out.println(beanFactory.getBeanDefinitionCount());   }    } 

   4. FactoryBean。FactoryBean是一種特殊的bean,這種bean允許注入到spring容器並用其產生真正的bean,所以可以這樣定義,FactoryBean本身是一種bean,這種bean又有能夠提供bean的能力。下面從FactoryBean的調用開始,講到spring是如何使用這個bean的。
   要想區分普通bean和FactoryBean,spring也必須有判斷他們並特殊處理的過程,這個過程就在AbstractBeanFactory的getObjectForBeanInstance中

protected Object getObjectForBeanInstance(       Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {      // Don't let calling code try to dereference the factory if the bean isn't a factory.     if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {       throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());     }      // Now we have the bean instance, which may be a normal bean or a FactoryBean.     // If it's a FactoryBean, we use it to create a bean instance, unless the     // caller actually wants a reference to the factory.     if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {       return beanInstance;     }      Object object = null;     if (mbd == null) {       object = getCachedObjectForFactoryBean(beanName);     }     if (object == null) {       // Return bean instance from factory.       FactoryBean<?> factory = (FactoryBean<?>) beanInstance;       // Caches object obtained from FactoryBean if it is a singleton.       if (mbd == null && containsBeanDefinition(beanName)) {         mbd = getMergedLocalBeanDefinition(beanName);       }       boolean synthetic = (mbd != null && mbd.isSynthetic());       object = getObjectFromFactoryBean(factory, beanName, !synthetic);     }     return object;   } 

  可以看出來,如果是普通bean,就直接返回了,而如果是FactoryBean,最終調用會調用factory.getObject從而返回具體對象。如果將整個spring看做一個抽象工廠,生產抽象的bean時,則FactoryBean就是具體工廠,生產你需要的對象。
  spring中FactoryBean用法很多,舉個比較常見的例子,整合hibernate的sessionFactory時一般會注入LocalSessionFactoryBean,但是這個sessionFactory實際上不是普通的bean,可以簡單在設定檔中注入就能生產,它有很多定製的部分,於是spring讓這個bean成為一個FactoryBean並控制其生產的對象。


 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.