欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

解析Java的Spring框架的基本結(jié)構(gòu)

 更新時間:2016年03月21日 08:48:23   作者:liweisnake  
這篇文章主要介紹了Java的Spring框架的基本結(jié)構(gòu),作者從Spring的設(shè)計角度觸發(fā)解析其基礎(chǔ)的架構(gòu)內(nèi)容,需要的朋友可以參考下

   在java屆,有位名叫Rod Johnson的牛人,發(fā)現(xiàn)最初的java企業(yè)級開發(fā)處于混沌狀態(tài)。

   于是,它決心編寫一個能夠解決問題的通用的基礎(chǔ)架構(gòu)。

   因為它深信面向接口編程能夠?qū)⒆兓刂频阶钚。瑫r也利于擴(kuò)展和變化。于是,它編寫了如下的接口。 

201632184450914.png (1045×387)

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

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

   這個BeanFactory最終是要在某個應(yīng)用上使用的,那么,需要給予BeanFactory在一個應(yīng)用中活動的能力。在BeanFactory中,只需要考慮跟bean相關(guān)的行為,比如怎么得到bean,bean的類型等;而如果要賦予其在應(yīng)用中的能力,則就需要考慮更多,比如應(yīng)用的名字,啟動時間,id等等跟應(yīng)用本身相關(guān)的行為和屬性,于是johnson想到了創(chuàng)造ApplicationContext。johnson想,這個ApplicationContext一定要能做許多事,要能夠處理參數(shù)化和國際化的文本信息,于是增加了MessageSource接口;要能發(fā)布事件以便解耦組件,于是有了ApplicationEventPublisher接口;要能得到資源文件,于是有了ResourcePatternResolver接口;要能有在不同環(huán)境有不同處理對象的能力,于是有了EnvironmentCapable接口。

   ApplicationContext繼承了所有這些接口。

   但是最重要的是,無論是BeanFactory還是ApplicationContext,它們都需要有可配置的能力,于是有了子接口ConfigurableBeanFactory和ConfigurableApplicationContext;另外,web在當(dāng)時是非常重要的趨勢,而且相比其他應(yīng)用有些獨(dú)特,需要得到ServletContext,于是有了WebApplicationContext。

   到目前為止,johnson都是面向接口進(jìn)行行為的抽象思考,并未具體實現(xiàn)他們。

   看著創(chuàng)造出來的BeanFactory和ApplicationContext,johnson意識到這一天的工作遠(yuǎn)遠(yuǎn)沒有結(jié)束,因為并沒有真正解決怎么能夠讓整套體系運(yùn)轉(zhuǎn)起來,于是,johnson開始思索如何實現(xiàn)他們。

   johoson首先想到的還是這個實現(xiàn)應(yīng)該具備什么能力?當(dāng)然要把之前提到的AutowireCapableBeanFactory,ListableBeanFactory和ConfigurableBeanFactory都包括進(jìn)去。因此創(chuàng)造了ConfigurableListableBeanFactory。其次,需要考慮對于bean對象的幾種能力,一是起別名的能力;二是保存單例對象的能力;三是緩存的能力;他們分別在SimpleAliasRegistry類,DefaultSingletonBeanRegistry類和FactoryBeanRegistrySupport類中實現(xiàn)。

201632184523588.png (673×591)

   最終,創(chuàng)造出了DefaultListableBeanFactory,它是spring中一切ioc工廠的原型,是BeanFactory第一個真正的孩子,這個孩子非常重要,已經(jīng)成為獨(dú)立的創(chuàng)建ioc容器的基礎(chǔ),如果有擴(kuò)展和使用,大多是繼承它或者組合使用它。

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

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

  接下來johnson想,BeanFactory有了一個功能比較全面的默認(rèn)實現(xiàn),那么ApplicationContext呢?于是johnson孜孜不倦的創(chuàng)造了3個重要的ApplicationContext實現(xiàn):FileSystemXmlApplicationContext, ClassPathXmlApplicationContext, AnnotationConfigWebApplicationContext(其實還有很多,比如處理portlet的, 處理web的)
201632184550126.png (791×504)

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

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

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

201632184620113.png (148×651)

    這個過程放在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的初始化及準(zhǔn)備;3. 生成并注冊beanDefinition;4. beanFactory后處理器;5.設(shè)置消息,事件及監(jiān)聽器。
  1. 刷新的生命周期

201632184644326.png (473×197)

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

  onRefresh,目的是提供給一些特殊的ApplicationContext,使他們有能夠在刷新過程中能夠擴(kuò)展的能力。目前使用到的大多是為servlet application context設(shè)置theme

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

  cancelRefresh,主要是在異常產(chǎn)生時將當(dāng)前的狀態(tài)改變?yōu)榉莂ctive。

  2. 對beanFactory的初始化及準(zhǔn)備

  johnson想,我們應(yīng)該讓beanFactory在初始化時就把bean透明的加載并注冊好,這樣對外界而言,我的封裝就非常成功,因此這步實際上做了很多事。下圖省略了許多步驟,只列出關(guān)鍵點(diǎn)。

  AbstractApplicationContext會調(diào)用refreshBeanFactory,它首先會檢查并關(guān)閉已有的beanFactory,其次新建一個beanFactory,然后利用該factory裝載所有BeanDefinition

  其中,loadBeanDefinitions會交給子類做不同的實現(xiàn),比如AbstractXmlApplicationContext主要是通過xml讀??;AnnotationConfigWebApplicationContext的實現(xiàn)則會調(diào)用掃描器掃描類中的bean

201632184713998.png (992×248)

   3. 生成并注冊beanDefinition
  當(dāng)解析完xml配置以后,DefaultBeanDefinitionDocumentReader的parseDefaultElement方法會根據(jù)xml中的元素做對應(yīng)的處理。其中,遇到bean元素時會最終調(diào)用BeanDefinitionReaderUtils中的registerBeanDefinition方法,該方法傳入的參數(shù)為BeanDefinitionRegistry,實際上是回調(diào)了DefaultListableBeanFactory的registerBeanDefinition方法來注冊beanDefinition(DefaultListableBeanFactory實現(xiàn)了BeanDefinitionRegistry)。

   4. beanFactory后處理器

   beanFactory后處理器是spring提供出來的讓其子類靈活擴(kuò)展的方式。spring中分為2個步驟:postProcessBeanFactory,invokeBeanFactoryPostProcessors。registerBeanPostProcessors則是實例化并調(diào)用所有的BeanPostProcessor,用來在bean初始化前和初始化后對bean做擴(kuò)展。

   5. 設(shè)置消息,事件及監(jiān)聽器

  設(shè)置默認(rèn)消息源為DelegatingMessageSource,如工廠里已經(jīng)有messageSource則使用該messageSource,事件多播器為SimpleApplicationEventMulticaster,如工廠里已經(jīng)有applicationEventMulticaster,則使用該applicationEventMulticaster,并注冊所有的應(yīng)用程序Listener以便能夠接收事件

  消息源:MessageSource是國際化資源文件的重要方法,spring在applicationContext就支持消息源。

  spring中提供了MessageSource的默認(rèn)實現(xiàn),使用java.util.ResourceBundle來提取消息。spring通過配置一個特殊id為messageSource的bean并制定i18n的文件名,就能夠從ApplicationContext.getMessage()直接訪問message。如果在jsp中,還能通過spring:message這個tag訪問到message。

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

  spring中將ApplicationContext當(dāng)做事件產(chǎn)生方,使用applicationListeners作為監(jiān)聽者集合,applicationEventMulticaster用來做事件發(fā)布。

  事件發(fā)布的幾個步驟:

  訂閱:最初addApplicationListener將applicationListener加入監(jiān)聽者集合。

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

  事件發(fā)布非常常用,不僅我們自己的應(yīng)用可以使用這個事件發(fā)布,spring框架自身也在使用事件發(fā)布。下面是一些spring中事件的應(yīng)用:

  在finishRefresh中會發(fā)布ContextRefreshedEvent表明refresh結(jié)束借此通知listener。在ApplicationContext中start和stop方法會發(fā)布事件表示context開始或結(jié)束。
  johnson將spring的主框架與運(yùn)行流程創(chuàng)造完畢之后,發(fā)覺spring中提供了許多靈活擴(kuò)展的地方。于是johnson準(zhǔn)備在第三日將這些靈活擴(kuò)展的用法公布出來。

  1. BeanPostProcessor。BeanPostProcessor提供了bean創(chuàng)建完成后的擴(kuò)展接口,當(dāng)你需要在bean創(chuàng)建完后對其做一定處理,則BeanPostProcessor是首選的方式。

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

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

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); 
      } 
    } 
  } 

  關(guān)于Aware的用法則更多了,比如如下代碼能夠感知ApplicationContext,spring在創(chuàng)建完這個bean之后便會注入ApplicationContext,于是我們就可以使用該context完成事件發(fā)布。

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創(chuàng)建后的擴(kuò)展接口。一個例子如下,當(dāng)注入這個bean以后,它便會在BeanFactory創(chuàng)建完畢自動打印注入的bean數(shù)量:

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,所以可以這樣定義,F(xiàn)actoryBean本身是一種bean,這種bean又有能夠提供bean的能力。下面從FactoryBean的調(diào)用開始,講到spring是如何使用這個bean的。
   要想?yún)^(qū)分普通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,最終調(diào)用會調(diào)用factory.getObject從而返回具體對象。如果將整個spring看做一個抽象工廠,生產(chǎn)抽象的bean時,則FactoryBean就是具體工廠,生產(chǎn)你需要的對象。
  spring中FactoryBean用法很多,舉個比較常見的例子,集成hibernate的sessionFactory時一般會注入LocalSessionFactoryBean,但是這個sessionFactory實際上不是普通的bean,可以簡單在配置文件中注入就能生產(chǎn),它有很多定制的部分,于是spring讓這個bean成為一個FactoryBean并控制其生產(chǎn)的對象。


 

相關(guān)文章

  • Java8新特性Stream流詳解

    Java8新特性Stream流詳解

    Java8 Stream使用的是函數(shù)式編程模式,如同它的名字一樣,它可以被用來對集合進(jìn)行鏈狀流式的操作,本文就將帶著你如何使用 Java 8 不同類型的 Stream 操作,同時還將了解流的處理順序,以及不同順序的流操作是如何影響運(yùn)行時性能的
    2023-07-07
  • 關(guān)于maven:pom文件的使用解析

    關(guān)于maven:pom文件的使用解析

    這篇文章主要介紹了關(guān)于maven:pom文件的使用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • spring監(jiān)視器actuator配置應(yīng)用

    spring監(jiān)視器actuator配置應(yīng)用

    這篇文章主要介紹了spring監(jiān)視器actuator配置應(yīng)用,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • SWT(JFace)Group(分組顯示)

    SWT(JFace)Group(分組顯示)

    SWT(JFace)體驗之Group(分組顯示)
    2009-06-06
  • spring boot發(fā)簡單文本郵件案例

    spring boot發(fā)簡單文本郵件案例

    這篇文章主要介紹了spring boot發(fā)簡單文本郵件案例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • Spring AOP注解案例及基本原理詳解

    Spring AOP注解案例及基本原理詳解

    這篇文章主要介紹了Spring AOP注解案例及基本原理詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • MyBatis動態(tài)SQL表達(dá)式詳解

    MyBatis動態(tài)SQL表達(dá)式詳解

    動態(tài)SQL可以省略很多拼接SQL的步驟,使用類似于JSTL方式,下面這篇文章主要給大家介紹了關(guān)于Mybatis動態(tài)SQL特性的相關(guān)資料,文字通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • 基于RestTemplate的使用方法(詳解)

    基于RestTemplate的使用方法(詳解)

    下面小編就為大家?guī)硪黄赗estTemplate的使用方法(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • Maven優(yōu)雅的添加第三方Jar包的方法

    Maven優(yōu)雅的添加第三方Jar包的方法

    下面小編就為大家?guī)硪黄狹aven優(yōu)雅的添加第三方Jar包的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Java基礎(chǔ)Map集合詳析

    Java基礎(chǔ)Map集合詳析

    這篇文章主要介紹了Java基礎(chǔ)Map集合詳析,主要通過介紹Map集合的常用方法、Map的獲取方法的一些相關(guān)資料展開內(nèi)容,需要的小伙伴可以參考一下
    2022-04-04

最新評論