posts - 28,  comments - 15,  trackbacks - 0

              我們知道Spring框架提供基于依賴注入的IOC容器,完成對象的構(gòu)造、依賴注入、對象聲明周期維護(hù)等功能,下面將以FileSystemXmlApplicationContext為例來分析Spring IOC容器的實(shí)現(xiàn)。

          1.容器類圖

              從整體上看Spring容器可以分為兩大部分:外部容器和內(nèi)部容器。我們經(jīng)常使用的FileSystemXmlApplicationContext、ClassPathXmlApplicationContext表現(xiàn)為外部容器,他們的父類都繼承了ResourceLoader(資源加載),因此他們主要側(cè)重于對外部資源的注入、解釋方面的處理,而對于對象的構(gòu)造、注入等聲明周期維護(hù)則委托給內(nèi)部對象實(shí)現(xiàn),通過區(qū)分外部容器和內(nèi)部容器,可以有效保護(hù)內(nèi)部容器管理的bean,防止外部錯誤的調(diào)用破壞對象狀態(tài)。
                                                                                  圖-1 外部容器類圖
            
                                                                                     圖2 內(nèi)部容器類圖
              從類圖分析可以看到SpringIOC容器可以分成兩部分:外部容器和內(nèi)部容器。外部容器(FileSystemXmlApplicationContext)主要負(fù)責(zé)與外部的調(diào)用進(jìn)行溝通,包括接收外部的配置信息、為外部提供受容器管理的Bean、外部配置信息的解釋、BeanDefinition的緩存等。內(nèi)部容器(DefaultListableBeanFactory--深綠色)主要負(fù)責(zé)Bean的聲明周期管理,包括Bean的注冊、Bean的緩存、Bean的銷毀等。
              

          1.1 接口和類說明

          類圖中列出了多種類和接口,下面列出重點(diǎn)的進(jìn)行說明。注意官方說明,翻譯自API。

          接口
          ListableBeanFactory

          官方說明

          BeanFactory接口的擴(kuò)展,由那些能夠列舉它們所管理的所有bean實(shí)例的bean工廠所實(shí)現(xiàn)。而不是試圖通過來自于客戶端的by name方式的bean查找。預(yù)載所有bean definitionsBeanFactory實(shí)現(xiàn)可能會實(shí)現(xiàn)該接口。

          個人理解:提供bean factory所管理bean的枚舉的接口

          接口HierarchicalBeanFactory

          官方說明

                 bean factory實(shí)現(xiàn)的子接口,實(shí)現(xiàn)了該接口的bean factory能成為一個層次結(jié)構(gòu)的一部分。

          個人理解:用以組織bean factory層次的一個接口。


          接口ApplicationContext

          官方說明:

          中心接口,以提供應(yīng)用程序的配置。當(dāng)應(yīng)用程序正在運(yùn)行,這是只讀的,但如果實(shí)現(xiàn)支持,它是允許重新加載的。

          一個ApplicationContext提供如下功能:

          用來訪問應(yīng)用程序組件的bean factory方法,其繼承自ListableBeanFactory

          能以通用的方式加載文件資源,其繼承自ResourceLoader

          能夠向注冊的監(jiān)聽器發(fā)布事件,其繼承自ApplicationEventPublisher

          能夠解析消息,支持國際化,繼承自MessageSource

          繼承自父上下文,后代上下文中的Definition將總能獲得優(yōu)先級,這意味著,例如,一個單親上下文能夠被整個應(yīng)用程序使用,而每個servlet有它自己的孩子上下文,它獨(dú)立于其他的servlet

          接口AutowireCapableBeanFactory

          官方說明

              BeanFactory接口的擴(kuò)展,可以被那些有能力自動裝配的bean factory實(shí)現(xiàn),用以達(dá)到為存在的bean暴露此項(xiàng)功能的目的。

              該接口不應(yīng)在常見的應(yīng)用程序中使用,黏合BeanFactory或者ListableBeanFactory是其典型的應(yīng)用案例。

              應(yīng)該注意的是,該接口不應(yīng)被ApplicationContext直接實(shí)現(xiàn),因?yàn)閼?yīng)用程序代碼幾乎不曾被應(yīng)用程序代碼使用。也就是說,它也可以通過訪問ApplicationContextgetAutowireCapableBeanFactory()方法從應(yīng)用程序上下文中獲得,即獲得一個AutowireCapableBeanFactory實(shí)例。
          個人理解

              從接口定義的方法中可以看出,該接口主要用于自動裝配bean,包括創(chuàng)建bean、裝配bean的屬性、裝配bean的依賴、注冊bean等。該接口對于外部的應(yīng)用程序而言,幾乎不需要使用,其只應(yīng)用于Spring容器的內(nèi)部管理,實(shí)際上只有內(nèi)部bean factory實(shí)現(xiàn)此功能。關(guān)于內(nèi)部bean factory后面會有說明。


          接口
          ConfigurableApplicationContext

          官方說明

          SPI(單個程序啟動)接口將會被大多數(shù)而不是全部的應(yīng)用程序上下文實(shí)現(xiàn),除了在ApplicationContext中的應(yīng)用程序上下文客戶端方法,其還提供設(shè)施來配置上下文。

                   配置和生命周期方法被封裝在這里,以避免使他們暴漏給ApplicationContext的客戶端代碼。這些方法應(yīng)該只用于啟動和關(guān)閉代碼中。

          個人理解:


          抽象類
          AbstractApplicationContext

          官方說明:

          ApplicationContext接口的抽象實(shí)現(xiàn)。其不要求配置使用的存儲類型,只是簡單的實(shí)現(xiàn)了常見上下文功能。它采用了模板方法模式,因此要求具體子類實(shí)現(xiàn)抽象方法。

          與普通的BeanFactory相比,ApplicationContext支持檢測定義在其內(nèi)部的bean工廠中的特殊的bean。因此,該類能自動注冊BeanFactoryPostProcessorBeanPostProcessorApplicationListener,它們在上下文中都被定義為beans

          通過擴(kuò)展DefaultResourceLoader實(shí)現(xiàn)資源加載,因此,視非URL資源路徑為類路徑資源(支持完整類路徑資源命名,包括包路徑,例如mypackage/myresource.dat),否則getResourceByPath方法需要在子類中覆寫。

          個人理解:

                 該類提供了BeanFactory的后置processor的注冊、應(yīng)用上下文的雙親維護(hù)、資源加載的功能。


          抽象類
          AbstractRefreshableApplicationContext

          ApplicationContext的基類的實(shí)現(xiàn),其支持多次refreshs,每次創(chuàng)建一個內(nèi)部bean工廠實(shí)例。通常(但不一定),一個上下文將會由一系列的配置定位來驅(qū)動,加載bean definations

          子類唯一需要實(shí)現(xiàn)的方法是loadBeanDefinitions,該方法主要是獲取每次刷新調(diào)用。具體的實(shí)現(xiàn)應(yīng)該是加載bean定義到給定的org.springframework.beans.factory.support.DefaultListableBeanFactory,通常委托給一個或多個特定的bean definition readers


          抽象類
          AbstractRefreshableConfigApplicationContext

          官方說明

          AbstractRefreshableApplicationContext子類,增加了針對 指定的配置位置(configLocations)的常見的處理。可以作為基于XML應(yīng)用程序上下文實(shí)現(xiàn)的基類,例如ClassPathXmlApplicationContexFileSystemXmlApplicationContext也可以是XmlWebApplicationContextXmlPortletApplicationContext
          個人理解:

           主要為配置文件位置的設(shè)置提供入口,即實(shí)現(xiàn)了setConfigLocation方法。這就提供了不依賴于Spring的且更靈活、通用的配置注入方式。


          抽象類
          AbstractXmlApplicationContext說明:

          ApplicationContext便利基類的實(shí)現(xiàn)。用于提取包含bean definition信息的XML文檔,該文檔由XmlBeanDefinitionReader負(fù)責(zé)解釋。

          子類要實(shí)getConfigResources/getConfigLocations,此外,他們可能會覆蓋
          getResourceByPath
          鉤子來解釋相對路徑。

          FileSystemXmlApplicationContext說明

          獨(dú)立的XML應(yīng)用程序上下文,它從文件系統(tǒng)或者URLs獲得上下文定義,解釋普通路徑為相對文件系統(tǒng)位置(如“MYDIR/ myfile.txt”),同時它也是有用的測試工具與獨(dú)立環(huán)境。

          普通路徑總是會被解釋為相對當(dāng)前VM的工作目錄,即使他們以斜線開頭。(這與Servlet容器的語義是一致的。)使用一個明確的“file:”前綴,以執(zhí)行一個絕對文件路徑。

          配置文件位置默認(rèn)值可以通過getConfigLocations覆蓋,配置位置可以表示為/ MYFILES/ context.xml”之類的具體文件,也可以像/ MYFILES/ *- context.xml”似Ant風(fēng)格模式(org.springframework.util.AntPathMatcher)。

          注意:在多個配置位置的情況下,以后的bean定義覆蓋先前加載的文件中定義的。這可以通過一個額外的XML文件,來故意覆蓋某些bean定義。

          這是一個簡單的,一站式的便利的ApplicationContext。考慮結(jié)合使用GenericApplicationContextXmlBeanDefinitionReader提供更靈活的上下文設(shè)置。


          接口
          ResourceLoader

          加載資源(例如,類路徑和文件路徑)的策略接口,ApplicationContext需要提供此功能,加上擴(kuò)展的ResourcePatternResolver支持。DefaultResourceLoader是該接口的獨(dú)立實(shí)現(xiàn),它可用于ApplicationContext以外,也可以被ResourceEditor使用。


          接口
          ResourcePatternResolver

              解釋位置模式的策略接口,其擴(kuò)展自ResourcePatternResolver接口。


          PathMatchingResourcePatternResolver

          ResourcePatternResolver接口的實(shí)現(xiàn),能夠把指定的資源位置路徑轉(zhuǎn)化為一個或者多個匹配資源。源路徑可能是一個簡單的路徑,它一對一映射到目標(biāo)(Resource),或者可能選擇性地包含特定的“classpath*:”前綴和/或者Ant-style正則表達(dá)式(由org.springframework.util.AntPathMatcher匹配)

          在一般的情況,如果指定的位置路徑?jīng)]有以“classpath*:”前綴開始,并且未包含PathMatcher模式,解釋器將通過調(diào)用ResourceLoadergetResource方法返回單一的資源。例子可包括真實(shí)的 URLs,例如file:C:/context.xml;偽URLs,例如classpath:/context.xml;簡單的無前綴的路徑,例如,/WEB-INF/context.xml

          2容器啟動分析

          2.1容器的構(gòu)建
           我們以FileSystemXmlApplicationContext為例來分析容器的啟動過程,首先看看FileSystemXmlApplicationContext的構(gòu)造函數(shù):

          public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
                                            
          throws BeansException {
                  
          super(parent);
                  setConfigLocations(configLocations);
                  
          if (refresh) {
          //refresh方法是容器啟動的核心,包括內(nèi)部容器的維護(hù),資源定位處理,資源的解析、注冊等。
                      refresh();
                  }

          }

          關(guān)于super的調(diào)用,以下代碼是個重點(diǎn):
          以下代碼來自于AbstractApplicationContext,這里要注意的是容器在構(gòu)造時自動構(gòu)造了資源模式解釋器,關(guān)于資源模式解釋器后面會有介紹.
          //資源模式解釋器,用于解釋處理ant-style配置信息,也可以把它理解為批量資源解釋器,并把解釋的一組資源定位解釋成Resource.該實(shí)例在容器創(chuàng)建時會自動創(chuàng)建
          private ResourcePatternResolver resourcePatternResolver;
          public AbstractApplicationContext(ApplicationContext parent) {
                  
          this.parent = parent;
                  
          this.resourcePatternResolver = getResourcePatternResolver();
          }

          protected ResourcePatternResolver getResourcePatternResolver() {
                  
          return new PathMatchingResourcePatternResolver(this);
          }

          一般情況下,我們可以通過編碼的方式來啟動容器,例如,如下:

          ApplicationContext context = new FileSystemXmlApplicationContext (
                  
          new String[] {"classpath:applicationContext*.xml"},true,null);
          BeanFactory factory 
          = (BeanFactory) context;

          2.2容器的初始化
          2.2.1Refresh方法分析
              我們接下來分析fresh方法,該方法由FileSystemXmlApplicationContext的父類AbstractApplicationContext實(shí)現(xiàn),從圖1可以看到AbstractApplicationContext是ApplicationContext接口的最頂層實(shí)現(xiàn)。注意:方法中的子方法調(diào)用將在后面的章節(jié)中分別介紹。

          public void refresh() throws BeansException, IllegalStateException {
              
          //獲得啟動關(guān)閉監(jiān)視器,防止此刻容器被其他線程操作。
          synchronized (this.startupShutdownMonitor) {
                      prepareRefresh();
          //激活容器的活動狀態(tài),采用鎖進(jìn)行同步
          /** *//**這里是容器初始化最為關(guān)鍵的一步,主要完成:
          1.內(nèi)部容器的初始化,包括對已經(jīng)存在的內(nèi)部容器管理的資源的銷毀、內(nèi)部容器的銷毀、創(chuàng)建新的內(nèi)部容器等;
          2.資源定位解釋,即把configLocations解釋成一個或者多個Resource;
          3.BeanDefinition加載與注冊,把Resource加載解釋成xml Document,然后對Document進(jìn)行解釋,形成BeanDefinition,并對Definition進(jìn)行注冊;
          */

          ConfigurableListableBeanFactory beanFactory 
          = obtainFreshBeanFactory();

                      
          //內(nèi)部工廠的預(yù)處理,主要是注冊一些Spring組件
                      prepareBeanFactory(beanFactory);

                      
          try {
              
          // Allows post-processing of the bean factory in context subclasses.
                          postProcessBeanFactory(beanFactory);
          //調(diào)用BeanFactory的后置處理,其處理的流程是:
          /** *//**
          (1)先對在prepareBeanFactory階段注冊的后置處理組件先進(jìn)行調(diào)用;
          (2)然后從容器加載BeanFactoryPostProcessor類型的bean,進(jìn)行調(diào)用處理,這類例子我們經(jīng)常用到,例如PropertyPlaceholderConfigurer在分離數(shù)據(jù)庫配置中的使用;
          */

                          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) {
                          beanFactory.destroySingletons();
                          cancelRefresh(ex);
                          
          throw ex;
                      }

                  }

              }


          2.2.2 obtainFreshBeanFactory方法分析
              
          該方法是在refresh方法中調(diào)用的,它主要完成了以下任務(wù):
              1.內(nèi)部容器的初始化,包括對已經(jīng)存在的內(nèi)部容器管理的資源(緩存的singleton bean以及bean name)的銷毀、內(nèi)部容器的銷毀、創(chuàng)建新的內(nèi)部容器、容器的定制等;
              2.資源定位解釋,即把configLocations解釋成一個或者多個Resource;
              
          3.BeanDefinition加載與注冊,把Resource加載解釋成xml Document,然后對Document進(jìn)行解釋,形成BeanDefinition,并對Definition進(jìn)行注冊;BeanDefinition就是spring對bean的定義,在spring構(gòu)造bean以及維護(hù)bean的引用關(guān)系時用到。

              obtainFreshBeanFactory方法由抽象類AbstractApplicationContext定義,代碼如下:

          protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
                  refreshBeanFactory();
                  ConfigurableListableBeanFactory beanFactory 
          = getBeanFactory();
                   …… 
                  
          return beanFactory;
          }


          protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

          public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

              從代碼中可以看到方法refreshBeanFactorygetBeanFactory都被定義為抽象方法,這是典型的模板方法模式。其中子類AbstractRefreshableApplicationContextGenericApplicationContext都實(shí)現(xiàn)這些方法,接下來以AbstractRefreshableApplicationContext的實(shí)現(xiàn)作進(jìn)一步說明。
          2.2.2.1 refreshBeanFactory方法分析

          /** Bean factory for this context */ 
          //上下文中的beanFactory,這個factory就是所的內(nèi)部容器,可以在圖2中看到它的繼承關(guān)系
          private DefaultListableBeanFactory beanFactory;

          protected final void refreshBeanFactory() throws BeansException {
                  
          if (hasBeanFactory()) {//檢查beanFactory是否為空,如果不為空,則銷毀singletonBeans緩存、singletonFactories工廠緩存、beanFactory等;
                      destroyBeans();
                      closeBeanFactory();
                  }

                  
          try {
          // 創(chuàng)建internal bean factory,即屬性beanFactory
                  DefaultListableBeanFactory beanFactory = createBeanFactory();               customizeBeanFactory(beanFactory);
                  loadBeanDefinitions(beanFactory);
          // 加載BeanDefinition,下面重點(diǎn)討論的
                  synchronized (this.beanFactoryMonitor) {
                      
          this.beanFactory = beanFactory;
                      }

                  }

                  
          catch (IOException ex) {
                      
          throw new ApplicationContextException(
                              
          "I/O error parsing XML document for application context [" + getDisplayName() + "]", ex);
                  }

              }

           

          容器可以有雙親(AbstractApplicationContext類的parent),每個容器下都有一個beanFactory(AbstractRefreshableApplicationContext),該beanFactory的類型為DefaultListableBeanFactory,我們稱之為內(nèi)部容器。它才是真正意義上bean的管理容器,因?yàn)榫彺嫦嚓P(guān)bean的信息的載體是由其父類DefaultSingletonBeanRegistry負(fù)責(zé)的,這一點(diǎn)在下面的destroyBeans方法分析中將能看到。

          refreshBeanFactory()方法詳細(xì)說明:

          <1>檢查內(nèi)部的beanFactory是否存在,如果存在則要銷毀beanFactory內(nèi)存在的相關(guān)資

          (關(guān)于銷毀的資源參看destroyBeans方法的說明)以及關(guān)閉beanFactory

          <2>構(gòu)造新的內(nèi)部bean factory,并定制該工廠,定制的選項(xiàng)主要包括:allowBeanDefinitionOverriding(是否允許bean definition覆蓋)

          allowCircularReferences(是否允許bean之間循環(huán)引用)

          <3>加載bean definition

          destroyBeans()方法解析

              在解析之前我們先明確一件事情,springbean分為單實(shí)例(singleton)與原型(prototype)兩種類型。對于singleton類型的bean,容器只保留一個實(shí)例,而原型類型的bean是容器每次接受請求通過defination創(chuàng)建的一個新實(shí)例。因此,我們都能夠推斷出容器是要緩存singleton bean的,事實(shí)上spring也確實(shí)這樣做的。

             destroyBeans主要完成了以下任務(wù),銷毀singleton beansearly singleton beanssingleton factory beansdisposable beans以及disposable beans的依賴beansdestroyBeans方法由DefaultListableBeanFactory父類DefaultSingletonBeanRegistry實(shí)現(xiàn)的,包括對singleton beans的相關(guān)緩存實(shí)現(xiàn),其代碼如下:

           

          protected void destroyBeans() {
                  getBeanFactory().destroySingletons();
          //destroySingletons()的實(shí)現(xiàn)在下面
          //這里的getBeanFactory()獲得的是上面提到的內(nèi)部容器beanFactory
          }


          /** *//*************以下代碼摘自DefaultSingletonBeanRegistry**********/
          //緩存singleton對象集合,映射關(guān)系為bean name--> bean instance
          /** *//**ConcurrentMap的創(chuàng)建:jdk5以及以上使用juc包的ConcurrentMap;否則,存在edu的ConcurrentMap則使用edu的ConcurrentMap,否則使用Collection.synchronizedMap(HashMap);*/
          private final Map singletonObjects = CollectionFactory.createConcurrentMapIfPossible(16);

          /** *//** Cache of singleton factories: bean name --> ObjectFactory */
          /** *//**緩存singleton factory bean集合,這是不是說明一個singleton的beanName需要對應(yīng)一個真實(shí)的singleton bean以及一個singleton factory bean呢?答案是否定的,F(xiàn)actoryBean存在的意義在于:在開發(fā)中可能存在需要類似于工廠模式創(chuàng)建類的需求,即一個對象是由工廠代理創(chuàng)建的,尤其是在第三方庫結(jié)合的時候,可以用FactoryBean來創(chuàng)建合適的bean.*/
          private final Map singletonFactories = new HashMap();

          /** *//** Cache of early singleton objects: bean name --> bean instance */
          //緩存早期的singlton bean,那么早期是如何定義的呢?spring又是如何管理的呢?實(shí)際上,earlySingletonObjects 存儲的是FactoryBean的getObject方法獲得的bean,singletonFactories 存儲FactoryBean(詳見:getSingleton()方法)
          //當(dāng)調(diào)用外部容器的getBean方法時,外部容器將調(diào)用轉(zhuǎn)交給DefaultListalbeBeanFactory,它會依次檢查singletonObjects 、earlySingletonObjects 、singletonFactories 是否存在。
          private final Map earlySingletonObjects = new HashMap();

          /** *//** Set of registered singletons, containing the bean names in registration order */
          //注冊的singleton bean集合,主要包含以注冊順序存儲的bean names
          private final Set registeredSingletons = new LinkedHashSet(16);

          /** *//** Names of beans that are currently in creation */
          //當(dāng)前創(chuàng)建的singleton bean的name
          private final Set singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet());

          /** *//** List of suppressed Exceptions, available for associating related causes */
          private Set suppressedExceptions;

          /** *//** Flag that indicates whether we're currently within destroySingletons */
          //表示當(dāng)前是否處于銷毀singleton的狀態(tài)
          private boolean singletonsCurrentlyInDestruction = false;

          /** *//** Disposable bean instances: bean name --> disposable instance */
          //處置bean,主要在bean銷毀時需要調(diào)用該bean的destroy方法,即實(shí)現(xiàn)了disposable接口
          private final Map disposableBeans = new LinkedHashMap(16);

          /** *//** Map between dependent bean names: bean name --> Set of dependent bean names */
          //用于保存bean的依賴關(guān)系,即一個bean依賴于哪幾個bean
          private final Map dependentBeanMap = CollectionFactory.createConcurrentMapIfPossible(16);
          //上一個的反向存儲,可以理解為從屬于
          /** *//** Map between depending bean names: bean name --> Set of bean names for the bean's dependencies */
          private final Map dependenciesForBeanMap = CollectionFactory.createConcurrentMapIfPossible(16);

          public void destroySingletons() {//銷毀單例
                  synchronized (this.singletonObjects) {
                      
          this.singletonsCurrentlyInDestruction = true;
                  }

                  
          synchronized (this.disposableBeans) {
              String[] disposableBeanNames 
          = StringUtils.toStringArray(this.disposableBeans.keySet());
                      
          for (int i = disposableBeanNames.length - 1; i >= 0; i--{
                          destroySingleton(disposableBeanNames[i]);
                      }

                  }

                  
          synchronized (this.singletonObjects) {
                      
          this.singletonObjects.clear();  //清除singleton對象
                      this.singletonFactories.clear();//清除single factory bean
                      this.earlySingletonObjects.clear();//清除早期的single對象
                      this.registeredSingletons.clear();
                      
          this.singletonsCurrentlyInDestruction = false;
                  }

          }



          下一節(jié) 《Spring 源碼閱讀(IOC容器)-容器啟動2

          posted on 2012-02-07 11:10 zhangxl 閱讀(730) 評論(0)  編輯  收藏 所屬分類: IOC/AOP

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          <2012年2月>
          2930311234
          567891011
          12131415161718
          19202122232425
          26272829123
          45678910

          常用鏈接

          留言簿(1)

          隨筆分類(17)

          隨筆檔案(28)

          文章分類(30)

          文章檔案(30)

          相冊

          收藏夾(2)

          hibernate

          java基礎(chǔ)

          mysql

          xml

          關(guān)注

          壓力測試

          算法

          最新隨筆

          搜索

          •  

          積分與排名

          • 積分 - 96749
          • 排名 - 600

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 宽城| 白水县| 阳信县| 丹阳市| 修武县| 都兰县| 师宗县| 临沭县| 洱源县| 锡林郭勒盟| 岳池县| 恭城| 登封市| 边坝县| 会泽县| 玛多县| 大理市| 班戈县| 崇信县| 治多县| 石泉县| 化州市| 独山县| 泰和县| 昌图县| 清镇市| 钟山县| 河南省| 孙吴县| 文化| 禄丰县| 双城市| 舞阳县| 兴和县| 耒阳市| 定西市| 恭城| 辽宁省| 金溪县| 博兴县| 扶风县|