爪哇咖啡館

          我為J狂

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            11 隨筆 :: 2 文章 :: 39 評(píng)論 :: 0 Trackbacks
          一、反射的概念 :
          反射的概念是由Smith在1982年首次提出的,主要是指程序可以訪問、檢測(cè)和修改它本身狀態(tài)或行為的一種能力。這一概念的提出很快引發(fā)了計(jì)算機(jī)科學(xué)領(lǐng)域關(guān)于應(yīng)用反射性的研究。它首先被程序語(yǔ)言的設(shè)計(jì)領(lǐng)域所采用,并在Lisp和面向?qū)ο蠓矫嫒〉昧顺煽?jī)。其中LEAD/LEAD++ 、OpenC++ 、MetaXa和OpenJava等就是基于反射機(jī)制的語(yǔ)言。最近,反射機(jī)制也被應(yīng)用到了視窗系統(tǒng)、操作系統(tǒng)和文件系統(tǒng)中。
          反射本身并不是一個(gè)新概念,它可能會(huì)使我們聯(lián)想到光學(xué)中的反射概念,盡管計(jì)算機(jī)科學(xué)賦予了反射概念新的含義,但是,從現(xiàn)象上來(lái)說(shuō),它們確實(shí)有某些相通之處,這些有助于我們的理解。在計(jì)算機(jī)科學(xué)領(lǐng)域,反射是指一類應(yīng)用,它們能夠自描述和自控制。也就是說(shuō),這類應(yīng)用通過(guò)采用某種機(jī)制來(lái)實(shí)現(xiàn)對(duì)自己行為的描述(self-representation)和監(jiān)測(cè)(examination),并能根據(jù)自身行為的狀態(tài)和結(jié)果,調(diào)整或修改應(yīng)用所描述行為的狀態(tài)和相關(guān)的語(yǔ)義。可以看出,同一般的反射概念相比,計(jì)算機(jī)科學(xué)領(lǐng)域的反射不單單指反射本身,還包括對(duì)反射結(jié)果所采取的措施。所有采用反射機(jī)制的系統(tǒng)(即反射系統(tǒng))都希望使系統(tǒng)的實(shí)現(xiàn)更開放。可以說(shuō),實(shí)現(xiàn)了反射機(jī)制的系統(tǒng)都具有開放性,但具有開放性的系統(tǒng)并不一定采用了反射機(jī)制,開放性是反射系統(tǒng)的必要條件。一般來(lái)說(shuō),反射系統(tǒng)除了滿足開放性條件外還必須滿足原因連接(Causally-connected)。所謂原因連接是指對(duì)反射系統(tǒng)自描述的改變能夠立即反映到系統(tǒng)底層的實(shí)際狀態(tài)和行為上的情況,反之亦然。開放性和原因連接是反射系統(tǒng)的兩大基本要素。
          Java中,反射是一種強(qiáng)大的工具。它使您能夠創(chuàng)建靈活的代碼,這些代碼可以在運(yùn)行時(shí)裝配,無(wú)需在組件之間進(jìn)行源代表鏈接。反射允許我們?cè)诰帉懪c執(zhí)行時(shí),使我們的程序代碼能夠接入裝載到JVM中的類的內(nèi)部信息,而不是源代碼中選定的類協(xié)作的代碼。這使反射成為構(gòu)建靈活的應(yīng)用的主要工具。但需注意的是:如果使用不當(dāng),反射的成本很高。
          二、Java中的類反射:
          Reflection 是 Java 程序開發(fā)語(yǔ)言的特征之一,它允許運(yùn)行中的 Java 程序?qū)ψ陨磉M(jìn)行檢查,或者說(shuō)“自審”,并能直接操作程序的內(nèi)部屬性。Java 的這一能力在實(shí)際應(yīng)用中也許用得不是很多,但是在其它的程序設(shè)計(jì)語(yǔ)言中根本就不存在這一特性。例如,Pascal、C 或者 C++ 中就沒有辦法在程序中獲得函數(shù)定義相關(guān)的信息。
          1.檢測(cè)類:
          1.1 reflection的工作機(jī)制
          考慮下面這個(gè)簡(jiǎn)單的例子,讓我們看看 reflection 是如何工作的。
          代碼:
          import java.lang.reflect.*;
          public class DumpMethods {
             public static void main(String args[]) {
                     try {
                           Class c = Class.forName(args[0]);
                           Method m[] = c.getDeclaredMethods();
                           for (int i = 0; i < m.length; i++)
                                 System.out.println(m[i].toString());
                     } catch (Throwable e) {
                           System.err.println(e);
                     }
               }
          }
          按如下語(yǔ)句執(zhí)行:
          java DumpMethods java.util.Stack
          它的結(jié)果輸出為:
          public java.lang.Object java.util.Stack.push(java.lang.Object)
          public synchronized java.lang.Object java.util.Stack.pop()
          public synchronized java.lang.Object java.util.Stack.peek()
          public boolean java.util.Stack.empty()
          public synchronized int java.util.Stack.search(java.lang.Object)

          這樣就列出了java.util.Stack 類的各方法名以及它們的限制符和返回類型。
          這個(gè)程序使用 Class.forName 載入指定的類,然后調(diào)用 getDeclaredMethods 來(lái)獲取這個(gè)類中定義了的方法列表。java.lang.reflect.Methods 是用來(lái)描述某個(gè)類中單個(gè)方法的一個(gè)類。
          1.2 Java類反射中的主要方法
          對(duì)于以下三類組件中的任何一類來(lái)說(shuō) -- 構(gòu)造函數(shù)、字段和方法 -- java.lang.Class 提供四種獨(dú)立的反射調(diào)用,以不同的方式來(lái)獲得信息。調(diào)用都遵循一種標(biāo)準(zhǔn)格式。以下是用于查找構(gòu)造函數(shù)的一組反射調(diào)用:            Constructor getConstructor(Class[] params) -- 獲得使用特殊的參數(shù)類型的公共構(gòu)造函數(shù),
          Constructor[] getConstructors() -- 獲得類的所有公共構(gòu)造函數(shù)
          Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定參數(shù)類型的構(gòu)造函數(shù)(與接入級(jí)別無(wú)關(guān))
          Constructor[] getDeclaredConstructors() -- 獲得類的所有構(gòu)造函數(shù)(與接入級(jí)別無(wú)關(guān))
          獲得字段信息的Class 反射調(diào)用不同于那些用于接入構(gòu)造函數(shù)的調(diào)用,在參數(shù)類型數(shù)組中使用了字段名:
          Field getField(String name) -- 獲得命名的公共字段
          Field[] getFields() -- 獲得類的所有公共字段
          Field getDeclaredField(String name) -- 獲得類聲明的命名的字段
          Field[] getDeclaredFields() -- 獲得類聲明的所有字段
          用于獲得方法信息函數(shù):
          Method getMethod(String name, Class[] params) -- 使用特定的參數(shù)類型,獲得命名的公共方法
          Method[] getMethods() -- 獲得類的所有公共方法
          Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數(shù)類型,獲得類聲明的命名的方法
          Method[] getDeclaredMethods() -- 獲得類聲明的所有方法

          1.3開始使用 Reflection:
          用于 reflection 的類,如 Method,可以在 java.lang.relfect 包中找到。使用這些類的時(shí)候必須要遵循三個(gè)步驟:第一步是獲得你想操作的類的 java.lang.Class 對(duì)象。在運(yùn)行中的 Java 程序中,用 java.lang.Class 類來(lái)描述類和接口等。
          下面就是獲得一個(gè) Class 對(duì)象的方法之一:
          Class c = Class.forName("java.lang.String");
          這條語(yǔ)句得到一個(gè) String 類的類對(duì)象。還有另一種方法,如下面的語(yǔ)句:
          Class c = int.class;
          或者
          Class c = Integer.TYPE;
          它們可獲得基本類型的類信息。其中后一種方法中訪問的是基本類型的封裝類 (如 Integer) 中預(yù)先定義好的 TYPE 字段。
          第二步是調(diào)用諸如 getDeclaredMethods 的方法,以取得該類中定義的所有方法的列表。
          一旦取得這個(gè)信息,就可以進(jìn)行第三步了——使用 reflection API 來(lái)操作這些信息,如下面這段代碼:
          Class c = Class.forName("java.lang.String");
          Method m[] = c.getDeclaredMethods();
          System.out.println(m[0].toString());
          它將以文本方式打印出 String 中定義的第一個(gè)方法的原型。
          2.處理對(duì)象:
          如果要作一個(gè)開發(fā)工具像debugger之類的,你必須能發(fā)現(xiàn)filed values,以下是三個(gè)步驟:
          a.創(chuàng)建一個(gè)Class對(duì)象
          b.通過(guò)getField 創(chuàng)建一個(gè)Field對(duì)象
          c.調(diào)用Field.getXXX(Object)方法(XXX是Int,Float等,如果是對(duì)象就省略;Object是指實(shí)例).
          例如:
          代碼:
          import java.lang.reflect.*;
          import java.awt.*;
          class SampleGet {
             public static void main(String[] args) {
                  Rectangle r = new Rectangle(100, 325);
                  printHeight(r);
             }
             static void printHeight(Rectangle r) {
                  Field heightField;
                  Integer heightValue;
                  Class c = r.getClass();
                  try {
                     heightField = c.getField("height");
                     heightValue = (Integer) heightField.get(r);
                     System.out.println("Height: " + heightValue.toString());
                  } catch (NoSuchFieldException e) {
                        System.out.println(e);
                  } catch (SecurityException e) {
                        System.out.println(e);
                  } catch (IllegalAccessException e) {
                        System.out.println(e);
                  }
             }
          }

          三、安全性和反射:
          在處理反射時(shí)安全性是一個(gè)較復(fù)雜的問題。反射經(jīng)常由框架型代碼使用,由于這一點(diǎn),我們可能希望框架能夠全面接入代碼,無(wú)需考慮常規(guī)的接入限制。但是,在其它情況下,不受控制的接入會(huì)帶來(lái)嚴(yán)重的安全性風(fēng)險(xiǎn),例如當(dāng)代碼在不值得信任的代碼共享的環(huán)境中運(yùn)行時(shí)。
          由于這些互相矛盾的需求,Java編程語(yǔ)言定義一種多級(jí)別方法來(lái)處理反射的安全性。基本模式是對(duì)反射實(shí)施與應(yīng)用于源代碼接入相同的限制:
          從任意位置到類公共組件的接入
          類自身外部無(wú)任何到私有組件的接入
          受保護(hù)和打包(缺省接入)組件的有限接入
          不過(guò)至少有些時(shí)候,圍繞這些限制還有一種簡(jiǎn)單的方法。我們可以在我們所寫的類中,擴(kuò)展一個(gè)普通的基本類java.lang.reflect.AccessibleObject 類。這個(gè)類定義了一種setAccessible方法,使我們能夠啟動(dòng)或關(guān)閉對(duì)這些類中其中一個(gè)類的實(shí)例的接入檢測(cè)。唯一的問題在于如果使用了安全性管理器,它將檢測(cè)正在關(guān)閉接入檢測(cè)的代碼是否許可了這樣做。如果未許可,安全性管理器拋出一個(gè)例外。
          下面是一段程序,在TwoString 類的一個(gè)實(shí)例上使用反射來(lái)顯示安全性正在運(yùn)行:
          代碼:
          public class ReflectSecurity {
               public static void main(String[] args) {
                     try {
                           TwoString ts = new TwoString("a", "b");
                           Field field = clas.getDeclaredField("m_s1");
          //               field.setAccessible(true);
                           System.out.println("Retrieved value is " +
                                 field.get(inst));
                     } catch (Exception ex) {
                           ex.printStackTrace(System.out);
                     }
               }
          }
          如果我們編譯這一程序時(shí),不使用任何特定參數(shù)直接從命令行運(yùn)行,它將在field .get(inst)調(diào)用中拋出一個(gè)IllegalAccessException異常。如果我們不注釋field.setAccessible(true)代碼行,那么重新編譯并重新運(yùn)行該代碼,它將編譯成功。最后,如果我們?cè)诿钚刑砑恿薐VM參數(shù)-Djava.security.manager以實(shí)現(xiàn)安全性管理器,它仍然將不能通過(guò)編譯,除非我們定義了ReflectSecurity類的許可權(quán)限。
          四、反射性能:
          反射是一種強(qiáng)大的工具,但也存在一些不足。一個(gè)主要的缺點(diǎn)是對(duì)性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什么并且它滿足我們的要求。這類操作總是慢于只直接執(zhí)行相同的操作。
          下面的程序是字段接入性能測(cè)試的一個(gè)例子,包括基本的測(cè)試方法。每種方法測(cè)試字段接入的一種形式 -- accessSame 與同一對(duì)象的成員字段協(xié)作,accessOther 使用可直接接入的另一對(duì)象的字段,accessReflection 使用可通過(guò)反射接入的另一對(duì)象的字段。在每種情況下,方法執(zhí)行相同的計(jì)算 -- 循環(huán)中簡(jiǎn)單的加/乘順序。
          程序如下:
          代碼:
          public int accessSame(int loops) {
               m_value = 0;
               for (int index = 0; index < loops; index++) {
                     m_value = (m_value + ADDITIVE_VALUE) *
                           MULTIPLIER_VALUE;
               }
               return m_value;
          }

          public int accessReference(int loops) {
               TimingClass timing = new TimingClass();
               for (int index = 0; index < loops; index++) {
                     timing.m_value = (timing.m_value + ADDITIVE_VALUE) *
                           MULTIPLIER_VALUE;
               }
               return timing.m_value;
          }

          public int accessReflection(int loops) throws Exception {
               TimingClass timing = new TimingClass();
               try {
                     Field field = TimingClass.class.
                           getDeclaredField("m_value");
                     for (int index = 0; index < loops; index++) {
                           int value = (field.getInt(timing) +
                                 ADDITIVE_VALUE) * MULTIPLIER_VALUE;
                           field.setInt(timing, value);
                     }
                     return timing.m_value;
               } catch (Exception ex) {
                     System.out.println("Error using reflection");
                     throw ex;
               }
          }
          在上面的例子中,測(cè)試程序重復(fù)調(diào)用每種方法,使用一個(gè)大循環(huán)數(shù),從而平均多次調(diào)用的時(shí)間衡量結(jié)果。平均值中不包括每種方法第一次調(diào)用的時(shí)間,因此初始化時(shí)間不是結(jié)果中的一個(gè)因素。下面的圖清楚的向我們展示了每種方法字段接入的時(shí)間:
          圖 1:字段接入時(shí)間 :

          我們可以看出:在前兩副圖中(Sun JVM),使用反射的執(zhí)行時(shí)間超過(guò)使用直接接入的1000倍以上。通過(guò)比較,IBM JVM可能稍好一些,但反射方法仍舊需要比其它方法長(zhǎng)700倍以上的時(shí)間。任何JVM上其它兩種方法之間時(shí)間方面無(wú)任何顯著差異,但I(xiàn)BM JVM幾乎比Sun JVM快一倍。最有可能的是這種差異反映了Sun Hot Spot JVM的專業(yè)優(yōu)化,它在簡(jiǎn)單基準(zhǔn)方面表現(xiàn)得很糟糕。反射性能是Sun開發(fā)1.4 JVM時(shí)關(guān)注的一個(gè)方面,它在反射方法調(diào)用結(jié)果中顯示。在這類操作的性能方面,Sun 1.4.1 JVM顯示了比1.3.1版本很大的改進(jìn)。
          如果為為創(chuàng)建使用反射的對(duì)象編寫了類似的計(jì)時(shí)測(cè)試程序,我們會(huì)發(fā)現(xiàn)這種情況下的差異不象字段和方法調(diào)用情況下那么顯著。使用newInstance()調(diào)用創(chuàng)建一個(gè)簡(jiǎn)單的java.lang.Object實(shí)例耗用的時(shí)間大約是在Sun 1.3.1 JVM上使用new Object()的12倍,是在IBM 1.4.0 JVM的四倍,只是Sun 1.4.1 JVM上的兩部。使用Array.newInstance(type, size)創(chuàng)建一個(gè)數(shù)組耗用的時(shí)間是任何測(cè)試的JVM上使用new type[size]的兩倍,隨著數(shù)組大小的增加,差異逐步縮小。
          結(jié)束語(yǔ):
          Java語(yǔ)言反射提供一種動(dòng)態(tài)鏈接程序組件的多功能方法。它允許程序創(chuàng)建和控制任何類的對(duì)象(根據(jù)安全性限制),無(wú)需提前硬編碼目標(biāo)類。這些特性使得反射特別適用于創(chuàng)建以非常普通的方式與對(duì)象協(xié)作的庫(kù)。例如,反射經(jīng)常在持續(xù)存儲(chǔ)對(duì)象為數(shù)據(jù)庫(kù)、XML或其它外部格式的框架中使用。Java reflection 非常有用,它使類和數(shù)據(jù)結(jié)構(gòu)能按名稱動(dòng)態(tài)檢索相關(guān)信息,并允許在運(yùn)行著的程序中操作這些信息。Java 的這一特性非常強(qiáng)大,并且是其它一些常用語(yǔ)言,如 C、C++、Fortran 或者 Pascal 等都不具備的。
          但反射有兩個(gè)缺點(diǎn)。第一個(gè)是性能問題。用于字段和方法接入時(shí)反射要遠(yuǎn)慢于直接代碼。性能問題的程度取決于程序中是如何使用反射的。如果它作為程序運(yùn)行中相對(duì)很少涉及的部分,緩慢的性能將不會(huì)是一個(gè)問題。即使測(cè)試中最壞情況下的計(jì)時(shí)圖顯示的反射操作只耗用幾微秒。僅反射在性能關(guān)鍵的應(yīng)用的核心邏輯中使用時(shí)性能問題才變得至關(guān)重要。
          許多應(yīng)用中更嚴(yán)重的一個(gè)缺點(diǎn)是使用反射會(huì)模糊程序內(nèi)部實(shí)際要發(fā)生的事情。程序人員希望在源代碼中看到程序的邏輯,反射等繞過(guò)了源代碼的技術(shù)會(huì)帶來(lái)維護(hù)問題。反射代碼比相應(yīng)的直接代碼更復(fù)雜,正如性能比較的代碼實(shí)例中看到的一樣。解決這些問題的最佳方案是保守地使用反射——僅在它可以真正增加靈活性的地方——記錄其在目標(biāo)類中的使用。

          利用反射實(shí)現(xiàn)類的動(dòng)態(tài)加載

          Bromon原創(chuàng) 請(qǐng)尊重版權(quán)
          最近在成都寫一個(gè)移動(dòng)增值項(xiàng)目,俺負(fù)責(zé)后臺(tái)server端。功能很簡(jiǎn)單,手機(jī)用戶通過(guò)GPRS打開Socket與服務(wù)器連接,我則根據(jù)用戶傳過(guò)來(lái)的數(shù)據(jù)做出響應(yīng)。做過(guò)類似項(xiàng)目的兄弟一定都知道,首先需要定義一個(gè)類似于MSNP的通訊協(xié)議,不過(guò)今天的話題是如何把這個(gè)系統(tǒng)設(shè)計(jì)得具有高度的擴(kuò)展性。由于這個(gè)項(xiàng)目本身沒有進(jìn)行過(guò)較為完善的客戶溝通和需求分析,所以以后肯定會(huì)有很多功能上的擴(kuò)展,通訊協(xié)議肯定會(huì)越來(lái)越龐大,而我作為一個(gè)不那么勤快的人,當(dāng)然不想以后再去修改寫好的程序,所以這個(gè)項(xiàng)目是實(shí)踐面向?qū)ο笤O(shè)計(jì)的好機(jī)會(huì)。
          首先定義一個(gè)接口來(lái)隔離類:
          代碼:
          package org.bromon.reflect;
          public interface Operator{
          public java.util.List act(java.util.List params)
          }

          根據(jù)設(shè)計(jì)模式的原理,我們可以為不同的功能編寫不同的類,每個(gè)類都繼承Operator接口,客戶端只需要針對(duì)Operator接口編程就可以避免很多麻煩。比如這個(gè)類:
          代碼:
          package org.bromon.reflect.*;
          public class Success implements Operator{
          public java.util.List act(java.util.List params){
            List result=new ArrayList();
            result.add(new String(“操作成功”));
            return result;
          }
          }

          我們還可以寫其他很多類,但是有個(gè)問題,接口是無(wú)法實(shí)例化的,我們必須手動(dòng)控制具體實(shí)例化哪個(gè)類,這很不爽,如果能夠向應(yīng)用程序傳遞一個(gè)參數(shù),讓自己去選擇實(shí)例化一個(gè)類,執(zhí)行它的act方法,那我們的工作就輕松多了。
          很幸運(yùn),我使用的是Java,只有Java才提供這樣的反射機(jī)制,或者說(shuō)內(nèi)省機(jī)制,可以實(shí)現(xiàn)我們的無(wú)理要求。編寫一個(gè)配置文件emp.properties:
          #成功響應(yīng)
          1000=Success
          #向客戶發(fā)送普通文本消息
          2000=Load
          #客戶向服務(wù)器發(fā)送普通文本消息
          3000=Store
          文件中的鍵名是客戶將發(fā)給我的消息頭,客戶發(fā)送1000給我,那么我就執(zhí)行Success類的act方法,類似的如果發(fā)送2000給我,那就執(zhí)行Load類的act方法,這樣一來(lái)系統(tǒng)就完全符合開閉原則了,如果要添加新的功能,完全不需要修改已有代碼,只需要在配置文件中添加對(duì)應(yīng)規(guī)則,然后編寫新的類,實(shí)現(xiàn)act方法就ok,即使我棄這個(gè)項(xiàng)目而去,它將來(lái)也可以很好的擴(kuò)展。這樣的系統(tǒng)具備了非常良好的擴(kuò)展性和可插入性。
          下面這個(gè)例子體現(xiàn)了動(dòng)態(tài)加載的功能,程序在執(zhí)行過(guò)程中才知道應(yīng)該實(shí)例化哪個(gè)類:
          代碼:
          package org.bromon.reflect.*;
          import java.lang.reflect.*;
          public class TestReflect{
          //加載配置文件,查詢消息頭對(duì)應(yīng)的類名
          private String loadProtocal(String header){
            String result=null;
          try{
            Properties prop=new Properties();
            FileInputStream fis=new FileInputStream("emp.properties");
            prop.load(fis);
            result=prop.getProperty(header);
            fis.close();
          }catch(Exception e){
            System.out.println(e);
            }
            return result;
          }
          //針對(duì)消息作出響應(yīng),利用反射導(dǎo)入對(duì)應(yīng)的類
          public String response(String header,String content){
             String result=null;
             String s=null;
             try {
          /*
          * 導(dǎo)入屬性文件emp.properties,查詢header所對(duì)應(yīng)的類的名字
          * 通過(guò)反射機(jī)制動(dòng)態(tài)加載匹配的類,所有的類都被Operator接口隔離
          * 可以通過(guò)修改屬性文件、添加新的類(繼承MsgOperator接口)來(lái)擴(kuò)展協(xié)議
          */
            s="org.bromon.reflect."+this.loadProtocal(header);
            //加載類
            Class c=Class.forName(s);
            //創(chuàng)建類的事例
            Operator mo=(Operator)c.newInstance();
             //構(gòu)造參數(shù)列表
            Class params[]=new Class[1];
            params[0]=Class.forName("java.util.List");
          //查詢act方法
            Method m=c.getMethod("act",params);
            Object args[]=new Object[1];
            args[0]=content;
            //調(diào)用方法并且獲得返回
            Object returnObject=m.invoke(mo,args);
          }catch(Exception e){
            System.out.println("Handler-response:"+e);
          }
          return result;
          }
          public static void main(String args[]){
            TestReflect tr=new TestReflect();
            tr.response(args[0],”消息內(nèi)容”);
          }
          }

          測(cè)試一下:java TestReflect 1000
          這個(gè)程序是針對(duì)Operator編程的,所以無(wú)需做任何修改,直接提供Load和Store類,就可以支持2000、3000做參數(shù)的調(diào)用。
          有了這樣的內(nèi)省機(jī)制,可以把接口的作用發(fā)揮到極至,設(shè)計(jì)模式也更能體現(xiàn)出威力,而不僅僅供我們飯后閑聊。
          posted on 2007-08-11 14:53 哲藤峰子 閱讀(412) 評(píng)論(0)  編輯  收藏

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


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 扎赉特旗| 大足县| 夹江县| 黄平县| 扬中市| 陆川县| 大渡口区| 富顺县| 皮山县| 册亨县| 宜宾县| 茂名市| 桂东县| 东乡县| 长阳| 城固县| 郎溪县| 高淳县| 富宁县| 新野县| 且末县| 镇平县| 平乐县| 环江| 昔阳县| 湘潭市| 威远县| 武山县| 迁安市| 长治县| 庆安县| 正阳县| 孟津县| 桃源县| 楚雄市| 确山县| 霍山县| 朝阳区| 威信县| 长宁区| 洛川县|