The important thing in life is to have a great aim , and the determination

          常用鏈接

          統計

          IT技術鏈接

          保險相關

          友情鏈接

          基金知識

          生活相關

          最新評論

          Spring源代碼解析(五):Spring聲明式事務處理

          我們看看Spring中的事務處理的代碼,使用Spring管理事務有聲明式和編程式兩種方式,聲明式事務處理通過AOP的實現把事物管理代碼作為方面封裝來橫向插入到業務代碼中,使得事務管理代碼和業務代碼解藕。在這種方式我們結合IoC容器和Spirng已有的FactoryBean來對事務管理進行屬性配置,比如傳播行為,隔離級別等。其中最簡單的方式就是通過配置TransactionProxyFactoryBean來實現聲明式事物;
          在整個源代碼分析中,我們可以大致可以看到Spring實現聲明式事物管理有這么幾個部分:

              * 對在上下文中配置的屬性的處理,這里涉及的類是TransactionAttributeSourceAdvisor,這是一個通知器,用它來對屬性值進行處理,屬性信息放在TransactionAttribute中來使用,而這些屬性的處理往往是和對切入點的處理是結合起來的。對屬性的處理放在類TransactionAttributeSource中完成。
              * 創建事物的過程,這個過程是委托給具體的事物管理器來創建的,但Spring通過TransactionStatus來傳遞相關的信息。
              * 對事物的處理通過對相關信息的判斷來委托給具體的事物管理器完成。

          我們下面看看具體的實現,在TransactionFactoryBean中:
          Java代碼 復制代碼 收藏代碼
          1. public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean   
          2.         implements FactoryBean, BeanFactoryAware {   
          3. //這里是Spring事務處理而使用的AOP攔截器,中間封裝了Spring對事務處理的代碼來支持聲明式事務處理的實現   
          4.     private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();   
          5.   
          6.     private Pointcut pointcut;   
          7.   
          8. //這里Spring把TransactionManager注入到TransactionInterceptor中去   
          9.     public void setTransactionManager(PlatformTransactionManager transactionManager) {   
          10.         this.transactionInterceptor.setTransactionManager(transactionManager);   
          11.     }   
          12.   
          13. //這里把在bean配置文件中讀到的事務管理的屬性信息注入到TransactionInterceptor中去   
          14.     public void setTransactionAttributes(Properties transactionAttributes) {   
          15.         this.transactionInterceptor.setTransactionAttributes(transactionAttributes);   
          16.     }   
          17.   
          18.     .........中間省略了其他一些方法.......   
          19.   
          20.     //這里創建Spring AOP對事務處理的Advisor   
          21.     protected Object createMainInterceptor() {   
          22.         this.transactionInterceptor.afterPropertiesSet();   
          23.         if (this.pointcut != null) {   
          24.             //這里使用默認的通知器   
          25.             return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);   
          26.         }   
          27.         else {   
          28.             // 使用上面定義好的TransactionInterceptor作為攔截器,同時使用TransactionAttributeSourceAdvisor   
          29.             return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);   
          30.         }   
          31.     }   
          32. }  

          那什么時候Spring的TransactionInterceptor被注入到Spring AOP中成為Advisor中的一部分呢?我們看到在TransactionProxyFactoryBean中,這個方法在IOC初始化bean的時候被執行:
          Java代碼 復制代碼 收藏代碼
          1. public void afterPropertiesSet() {   
          2.     .......   
          3.     //TransactionProxyFactoryBean實際上使用ProxyFactory完成AOP的基本功能。   
          4.     ProxyFactory proxyFactory = new ProxyFactory();   
          5.   
          6.     if (this.preInterceptors != null) {   
          7.         for (int i = 0; i < this.preInterceptors.length; i++) {   
          8.             proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.preInterceptors[i]));   
          9.         }   
          10.     }   
          11.   
          12.     //這里是Spring加入通知器的地方   
          13.     //有兩種通知器可以被加入DefaultPointcutAdvisor或者TransactionAttributeSourceAdvisor   
          14.     //這里把Spring處理聲明式事務處理的AOP代碼都放到ProxyFactory中去,怎樣加入advisor我們可以參考ProxyFactory的父類AdvisedSupport()   
          15.     //由它來維護一個advice的鏈表,通過這個鏈表的增刪改來抽象我們對整個通知器配置的增刪改操作。   
          16.     proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));   
          17.   
          18.     if (this.postInterceptors != null) {   
          19.         for (int i = 0; i < this.postInterceptors.length; i++) {   
          20.             proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(this.postInterceptors[i]));   
          21.         }   
          22.     }   
          23.   
          24.     proxyFactory.copyFrom(this);   
          25.       
          26.     //這里創建AOP的目標源   
          27.     TargetSource targetSource = createTargetSource(this.target);   
          28.     proxyFactory.setTargetSource(targetSource);   
          29.   
          30.     if (this.proxyInterfaces != null) {   
          31.         proxyFactory.setInterfaces(this.proxyInterfaces);   
          32.     }   
          33.     else if (!isProxyTargetClass()) {   
          34.         proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass()));   
          35.     }   
          36.   
          37.     this.proxy = getProxy(proxyFactory);   
          38. }  

          Spring 已經定義了一個transctionInterceptor作為攔截器或者AOP advice的實現,在IOC容器中定義的其他屬性比如transactionManager和事務管理的屬性都會傳到已經定義好的 TransactionInterceptor那里去進行處理。以上反映了基本的Spring AOP的定義過程,其中pointcut和advice都已經定義好,同時也通過通知器配置到ProxyFactory中去了。
          下面讓我們回到TransactionProxyFactoryBean中看看TransactionAttributeSourceAdvisor是怎樣定義的,這樣我們可以理解具體的屬性是怎樣起作用,這里我們分析一下類TransactionAttributeSourceAdvisor:
          Java代碼 復制代碼 收藏代碼
          1. public class TransactionAttributeSourceAdvisor extends AbstractPointcutAdvisor {   
          2.     //和其他Advisor一樣,同樣需要定義AOP中的用到的Interceptor和Pointcut   
          3.     //Interceptor使用傳進來的TransactionInterceptor   
          4.     //而對于pointcut,這里定義了一個內部類,參見下面的代碼     
          5.     private TransactionInterceptor transactionInterceptor;   
          6.   
          7.     private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut();   
          8.       
          9.     .........   
          10.     //定義的PointCut內部類   
          11.         private class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {   
          12.        .......   
          13.       //方法匹配的實現,使用了TransactionAttributeSource類   
          14.         public boolean matches(Method method, Class targetClass) {   
          15.             TransactionAttributeSource tas = getTransactionAttributeSource();   
          16.             //這里使用TransactionAttributeSource來對配置屬性進行處理   
          17.             return (tas != null && tas.getTransactionAttribute(method, targetClass) != null);   
          18.         }   
          19.     ........省略了equal,hashcode,tostring的代碼   
          20.     }  

          這里我們看看屬性值是怎樣被讀入的:AbstractFallbackTransactionAttributeSource負責具體的屬性讀入任務,我們可以有兩種讀入方式,比如annotation和直接配置.我們下面看看直接配置的讀入方式,在Spring中同時對讀入的屬性值進行了緩存處理,這是一個decorator模式:
          Java代碼 復制代碼 收藏代碼
          1. public final TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {   
          2.     //這里先查一下緩存里有沒有事務管理的屬性配置,如果有從緩存中取得TransactionAttribute   
          3.     Object cacheKey = getCacheKey(method, targetClass);   
          4.     Object cached = this.cache.get(cacheKey);   
          5.     if (cached != null) {   
          6.         if (cached == NULL_TRANSACTION_ATTRIBUTE) {   
          7.             return null;   
          8.         }   
          9.         else {   
          10.             return (TransactionAttribute) cached;   
          11.         }   
          12.     }   
          13.     else {   
          14.         // 這里通過對方法和目標對象的信息來計算事務緩存屬性   
          15.         TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);   
          16.         //把得到的事務緩存屬性存到緩存中,下次可以直接從緩存中取得。   
          17.         if (txAtt == null) {   
          18.             this.cache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);   
          19.         }   
          20.         else {   
          21.             ...........   
          22.             this.cache.put(cacheKey, txAtt);   
          23.         }   
          24.         return txAtt;   
          25.     }   
          26. }  

          別急,基本的處理在computeTransactionAttribute()中:
          Java代碼 復制代碼 收藏代碼
          1. private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) {   
          2.     //這里檢測是不是public方法   
          3.     if(allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {   
          4.         return null;   
          5.     }   
          6.       
          7.     Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);   
          8.       
          9.     // First try is the method in the target class.   
          10.     TransactionAttribute txAtt = findTransactionAttribute(findAllAttributes(specificMethod));   
          11.     if (txAtt != null) {   
          12.         return txAtt;   
          13.     }   
          14.   
          15.     // Second try is the transaction attribute on the target class.   
          16.     txAtt = findTransactionAttribute(findAllAttributes(specificMethod.getDeclaringClass()));   
          17.     if (txAtt != null) {   
          18.         return txAtt;   
          19.     }   
          20.   
          21.     if (specificMethod != method) {   
          22.         // Fallback is to look at the original method.   
          23.         txAtt = findTransactionAttribute(findAllAttributes(method));   
          24.         if (txAtt != null) {   
          25.             return txAtt;   
          26.         }   
          27.         // Last fallback is the class of the original method.   
          28.         return findTransactionAttribute(findAllAttributes(method.getDeclaringClass()));   
          29.     }   
          30.     return null;   
          31. }  

          經過一系列的嘗試我們可以通過findTransactionAttribute()通過調用findAllAttribute()得到TransactionAttribute的對象,如果返回的是null,這說明該方法不是我們需要事務處理的方法。
          在完成把需要的通知器加到ProxyFactory中去的基礎上,我們看看具體的看事務處理代碼怎樣起作用,在TransactionInterceptor中:
          Java代碼 復制代碼 收藏代碼
          1. public Object invoke(final MethodInvocation invocation) throws Throwable {   
          2.     //這里得到目標對象   
          3.     Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);   
          4.   
          5.     //這里同樣的通過判斷是否能夠得到TransactionAttribute來決定是否對當前方法進行事務處理,有可能該屬性已經被緩存,   
          6.     //具體可以參考上面對getTransactionAttribute的分析,同樣是通過TransactionAttributeSource   
          7.     final TransactionAttribute txAttr =   
          8.             getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);   
          9.     final String joinpointIdentification = methodIdentification(invocation.getMethod());   
          10.   
          11.     //這里判斷我們使用了什么TransactionManager   
          12.     if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {   
          13.         // 這里創建事務,同時把創建事務過程中得到的信息放到TransactionInfo中去   
          14.         TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);   
          15.         Object retVal = null;   
          16.         try {   
          17.               retVal = invocation.proceed();   
          18.         }   
          19.         catch (Throwable ex) {   
          20.             // target invocation exception   
          21.             completeTransactionAfterThrowing(txInfo, ex);   
          22.             throw ex;   
          23.         }   
          24.         finally {   
          25.             cleanupTransactionInfo(txInfo);   
          26.         }   
          27.         commitTransactionAfterReturning(txInfo);   
          28.         return retVal;   
          29.     }   
          30.   
          31.     else {   
          32.         // 使用的是Spring定義的PlatformTransactionManager同時實現了回調接口,我們通過其回調函數完成事務處理,就像我們使用編程式事務處理一樣。   
          33.         try {   
          34.             Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr,   
          35.                     new TransactionCallback() {   
          36.                         public Object doInTransaction(TransactionStatus status) {   
          37.                             //同樣的需要一個TransactonInfo   
          38.                             TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status);   
          39.                             try {   
          40.                                 return invocation.proceed();   
          41.                             }   
          42.                          .....這里省去了異常處理和事務信息的清理代碼   
          43.                     });   
          44.          ...........   
          45.     }   
          46. }  

          這里面涉及到事務的創建,我們可以在TransactionAspectSupport實現的事務管理代碼:
          Java代碼 復制代碼 收藏代碼
          1. protected TransactionInfo createTransactionIfNecessary(   
          2.         TransactionAttribute txAttr, final String joinpointIdentification) {   
          3.   
          4.     // If no name specified, apply method identification as transaction name.   
          5.     if (txAttr != null && txAttr.getName() == null) {   
          6.         txAttr = new DelegatingTransactionAttribute(txAttr) {   
          7.             public String getName() {   
          8.                 return joinpointIdentification;   
          9.             }   
          10.         };   
          11.     }   
          12.   
          13.     TransactionStatus status = null;   
          14.     if (txAttr != null) {   
          15.     //這里使用了我們定義好的事務配置信息,有事務管理器來創建事務,同時返回TransactionInfo   
          16.         status = getTransactionManager().getTransaction(txAttr);   
          17.     }   
          18.     return prepareTransactionInfo(txAttr, joinpointIdentification, status);   
          19. }  

          首先通過TransactionManager得到需要的事務,事務的創建根據我們定義的事務配置決定,在 AbstractTransactionManager中給出一個標準的創建過程,當然創建什么樣的事務還是需要具體的 PlatformTransactionManager來決定,但這里給出了創建事務的模板:
          Java代碼 復制代碼 收藏代碼
          1. public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {   
          2.     Object transaction = doGetTransaction();   
          3.     ......   
          4.   
          5.     if (definition == null) {   
          6.         //如果事務信息沒有被配置,我們使用Spring默認的配置方式   
          7.         definition = new DefaultTransactionDefinition();   
          8.     }   
          9.   
          10.     if (isExistingTransaction(transaction)) {   
          11.         // Existing transaction found -> check propagation behavior to find out how to behave.   
          12.         return handleExistingTransaction(definition, transaction, debugEnabled);   
          13.     }   
          14.   
          15.     // Check definition settings for new transaction.   
          16.     //下面就是使用配置信息來創建我們需要的事務;比如傳播屬性和同步屬性等   
          17.     //最后把創建過程中的信息收集起來放到TransactionStatus中返回;     
          18.     if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {   
          19.         throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());   
          20.     }   
          21.   
          22.     // No existing transaction found -> check propagation behavior to find out how to behave.   
          23.     if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {   
          24.         throw new IllegalTransactionStateException(   
          25.                 "Transaction propagation 'mandatory' but no existing transaction found");   
          26.     }   
          27.     else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||   
          28.             definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||   
          29.         definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {   
          30.         //這里是事務管理器創建事務的地方,并將創建過程中得到的信息放到TransactionStatus中去,包括創建出來的事務   
          31.         doBegin(transaction, definition);   
          32.         boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);   
          33.         return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);   
          34.     }   
          35.     else {   
          36.         boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);   
          37.         return newTransactionStatus(definition, nullfalse, newSynchronization, debugEnabled, null);   
          38.     }   
          39. }  

          接著通過調用prepareTransactionInfo完成事務創建的準備,創建過程中得到的信息存儲在TransactionInfo對象中進行傳遞同時把信息和當前線程綁定;
          Java代碼 復制代碼 收藏代碼
          1. protected TransactionInfo prepareTransactionInfo(   
          2.         TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {   
          3.   
          4.     TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification);   
          5.     if (txAttr != null) {   
          6.     .....   
          7.         // 同樣的需要把在getTransaction中得到的TransactionStatus放到TransactionInfo中來。   
          8.         txInfo.newTransactionStatus(status);   
          9.     }   
          10.     else {   
          11.     .......   
          12.    }   
          13.   
          14.     // 綁定事務創建信息到當前線程   
          15.     txInfo.bindToThread();   
          16.     return txInfo;   
          17. }  

          將創建事務的信息返回,然后看到其他的事務管理代碼:
          Java代碼 復制代碼 收藏代碼
          1. protected void commitTransactionAfterReturning(TransactionInfo txInfo) {   
          2.     if (txInfo != null && txInfo.hasTransaction()) {   
          3.         if (logger.isDebugEnabled()) {   
          4.             logger.debug("Invoking commit for transaction on " + txInfo.getJoinpointIdentification());   
          5.         }   
          6.         this.transactionManager.commit(txInfo.getTransactionStatus());   
          7.     }   
          8. }  

          通過transactionManager對事務進行處理,包括異常拋出和正常的提交事務,具體的事務管理器由用戶程序設定。
          Java代碼 復制代碼 收藏代碼
          1. protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {   
          2.     if (txInfo != null && txInfo.hasTransaction()) {   
          3.         if (txInfo.transactionAttribute.rollbackOn(ex)) {   
          4.             ......   
          5.             try {   
          6.                 this.transactionManager.rollback(txInfo.getTransactionStatus());   
          7.             }   
          8.             ..........   
          9.   }   
          10.         else {   
          11.             .........   
          12.             try {   
          13.                 this.transactionManager.commit(txInfo.getTransactionStatus());   
          14.             }   
          15.    ...........   
          16. }   
          17.   
          18. protected void commitTransactionAfterReturning(TransactionInfo txInfo) {   
          19.     if (txInfo != null && txInfo.hasTransaction()) {   
          20.         ......   
          21.         this.transactionManager.commit(txInfo.getTransactionStatus());   
          22.     }   
          23. }  

          Spring通過以上代碼對transactionManager進行事務處理的過程進行了AOP包裝,到這里我們看到為了方便客戶實現聲明式的事務處理,Spring還是做了許多工作的。如果說使用編程式事務處理,過程其實比較清楚,我們可以參考書中的例子:
          Java代碼 復制代碼 收藏代碼
          1. TransactionDefinition td = new DefaultTransactionDefinition();   
          2. TransactionStatus status = transactionManager.getTransaction(td);   
          3. try{   
          4.       ......//這里是我們的業務方法   
          5. }catch (ApplicationException e) {   
          6.    transactionManager.rollback(status);   
          7.    throw e   
          8. }   
          9. transactionManager.commit(status);   
          10.  ........  

          我們看到這里選取了默認的事務配置DefaultTransactionDefinition,同時在創建事物的過程中得到TransactionStatus,然后通過直接調用事務管理器的相關方法就能完成事務處理。
          聲明式事務處理也同樣實現了類似的過程,只是因為采用了聲明的方法,需要增加對屬性的讀取處理,并且需要把整個過程整合到Spring AOP框架中和IoC容器中去的過程。
          下面我們選取一個具體的transactionManager - DataSourceTransactionManager來看看其中事務處理的實現:
          同樣的通過使用AbstractPlatformTransactionManager使用模板方法,這些都體現了對具體平臺相關的事務管理器操作的封裝,比如commit:
          Java代碼 復制代碼 收藏代碼
          1. public final void commit(TransactionStatus status) throws TransactionException {   
          2.     ......   
          3.     DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;   
          4.     if (defStatus.isLocalRollbackOnly()) {   
          5.         ......   
          6.         processRollback(defStatus);   
          7.         return;   
          8.     }   
          9.          .......   
          10.         processRollback(defStatus);   
          11.     ......   
          12.     }   
          13.   
          14.     processCommit(defStatus);   
          15. }  

          通過對TransactionStatus的具體狀態的判斷,來決定具體的事務處理:
          Java代碼 復制代碼 收藏代碼
          1. private void processCommit(DefaultTransactionStatus status) throws TransactionException {   
          2.     try {   
          3.         boolean beforeCompletionInvoked = false;   
          4.         try {   
          5.             triggerBeforeCommit(status);   
          6.             triggerBeforeCompletion(status);   
          7.             beforeCompletionInvoked = true;   
          8.             boolean globalRollbackOnly = false;   
          9.             if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {   
          10.                 globalRollbackOnly = status.isGlobalRollbackOnly();   
          11.             }   
          12.             if (status.hasSavepoint()) {   
          13.             ........   
          14.                status.releaseHeldSavepoint();   
          15.             }   
          16.             else if (status.isNewTransaction()) {   
          17.             ......   
          18.                 doCommit(status);   
          19.             }   
          20.         .........   
          21. }  

          這些模板方法的實現由具體的transactionManager來實現,比如在DataSourceTransactionManager:
          Java代碼 復制代碼 收藏代碼
          1. protected void doCommit(DefaultTransactionStatus status) {   
          2.     //這里得到存在TransactionInfo中已經創建好的事務   
          3.     DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();   
          4.   
          5.     //這里得到和事務綁定的數據庫連接   
          6.     Connection con = txObject.getConnectionHolder().getConnection();   
          7.     ........   
          8.     try {   
          9.     //這里通過數據庫連接來提交事務   
          10.         con.commit();   
          11.     }   
          12.    .......   
          13. }   
          14.   
          15. protected void doRollback(DefaultTransactionStatus status) {   
          16.     DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();   
          17.     Connection con = txObject.getConnectionHolder().getConnection();   
          18.     if (status.isDebug()) {   
          19.         logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");   
          20.     }   
          21.     try {   
          22.     //這里通過數據庫連接來回滾事務   
          23.         con.rollback();   
          24.     }   
          25.     catch (SQLException ex) {   
          26.         throw new TransactionSystemException("Could not roll back JDBC transaction", ex);   
          27.     }   
          28. }  

          我們看到在DataSourceTransactionManager中最后還是交給connection來實現事務的提交和rollback。整個聲明式事務處理是事務處理在Spring AOP中的應用,我們看到了一個很好的使用Spring AOP的例子,在Spring聲明式事務處理的源代碼中我們可以看到:
          1.怎樣封裝各種不同平臺下的事務處理代碼
          2.怎樣讀取屬性值和結合事務處理代碼來完成既定的事務處理策略
          3.怎樣靈活的使用SpringAOP框架。
          如果能夠結合前面的Spring AOP的源代碼來學習,理解可能會更深刻些。

          posted on 2011-10-26 23:40 鴻雁 閱讀(588) 評論(0)  編輯  收藏


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


          網站導航:
           
          主站蜘蛛池模板: 芦溪县| 金湖县| 平湖市| 河北区| 青海省| 新安县| 建始县| 金阳县| 宿迁市| 瑞昌市| 唐海县| 盐津县| 德化县| 阿拉尔市| 定边县| 涟水县| 江陵县| 南皮县| 安溪县| 黄龙县| 晋州市| 镇宁| 江西省| 临澧县| 临沧市| 萨迦县| 通州市| 金秀| 治多县| 子洲县| 全南县| 和平区| 息烽县| 诏安县| 绿春县| 梨树县| 海南省| 康保县| 东阿县| 合水县| 太原市|