程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 解析Java的Spring框架的根本構造

解析Java的Spring框架的根本構造

編輯:關於JAVA

解析Java的Spring框架的根本構造。本站提示廣大學習愛好者:(解析Java的Spring框架的根本構造)文章只能為提供參考,不一定能成為您想要的結果。以下是解析Java的Spring框架的根本構造正文


   在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並掌握其臨盆的對象。


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved