一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務器之家 - 編程語言 - JAVA教程 - 解析Java的Spring框架的基本結構

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

2020-04-11 14:29liweisnake JAVA教程

這篇文章主要介紹了Java的Spring框架的基本結構,作者從Spring的設計角度觸發解析其基礎的架構內容,需要的朋友可以參考下

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

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

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

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

   在混沌狀態最先要創造的是一切對象的母親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類中實現。

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

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

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

?
1
2
3
4
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的)
解析Java的Spring框架的基本結構

    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載入這一宏偉的目標而努力。

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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. 刷新的生命周期

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

    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

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

   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完成了另一個擴展用法,實在是高超。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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完成事件發布。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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數量:

?
1
2
3
4
5
6
7
8
9
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中

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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并控制其生產的對象。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 三星w699| 日本在线你懂的 | 国产亚洲精品第一综合另类 | h黑寡妇一级毛片 | 国产色网址| 射逼视频| 国产精品夜色视频一级区 | 午夜久久免影院欧洲 | 久久久久999 | 国产精品每日在线观看男人的天堂 | 欧美美女一级片 | 国内揄拍国内精品久久 | mmkk在线看片 | 男生同性视频twink在线 | 免费高清特黄a 大片 | 国产一成人精品福利网站 | 99热在线免费观看 | 九九精品视频在线播放 | 91庥豆果冻天美精东蜜桃传媒 | 国产精品亚洲va在线观看 | 美女吃jj | 男同志与动人物zozotv | 热99re久久精品精品免费 | 国产成人精品本亚洲 | 青草午夜精品视频在线观看 | 久久99re热在线播放7 | 日日干天天爽 | 免费十几分视频 | 午夜AV亚洲一码二中文字幕青青 | 关晓彤被调教出奶水的视频 | 美女把腿开让我 | 亚洲一区 在线播放 | 成人性用品| 五月天婷婷亚洲 | ts人妖系列在线专区 | 亚洲欧美一级夜夜爽w | 日本高清有码视频 | 99精彩视频在线观看 | 午夜一个人在线观看完整版 | 久久影院中文字幕 | 国产高清在线看 |