最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

ResponseBodyAdvice的使用原理源碼解析

 更新時間:2023年03月13日 14:31:19   作者:半夏之沫  
這篇文章主要為大家介紹了ResponseBodyAdvice的使用原理源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

ResponseBodyAdvice接口可以在將handler方法的返回值寫入response前對返回值進行處理,例如將返回值封裝成一個與客戶端約定好的對象以便于客戶端處理響應(yīng)數(shù)據(jù)。本篇文章將學(xué)習(xí)如何使用ResponseBodyAdvice以及其實現(xiàn)原理。

SpringBoot版本:2.4.1

正文

一. ResponseBodyAdvice的使用

假如已經(jīng)存在一個Controller,如下所示。

@RestController
public class DemoController {
    @RequestMapping(value = "/api/v1/demo1/getdefault", method = RequestMethod.GET)
    public ResponseEntity<Demo1> getDefaultDemo1() {
        return new ResponseEntity<>(Demo1.defaultDemo1, HttpStatus.INTERNAL_SERVER_ERROR);
    }
    @RequestMapping(value = "/api/v1/demo2/getdefault", method = RequestMethod.GET)
    public Demo2 getDefaultDemo2() {
        return Demo2.defaultDemo2;
    }
}
public class Demo1 {
    private int id;
    private String name;
    public static Demo1 defaultDemo1 = new Demo1(1, "Admin");
    public Demo1() {}
    public Demo1(int id, String name) {		this.id = id;
		this.name = name;
    }
    // 省略getter和setter
}
public class Demo2 {
    private int id;
    private String desc;
    public static Demo2 defaultDemo2 = new Demo2(1, "Root");
    public Demo2() {}
    public Demo2(int id, String desc) {
		this.id = id;
        this.desc = desc;
    }
    // 省略getter和setter
}

上述Controller中有兩個方法,并且返回值分別為ResponseEntity<Demo1>和Demo2。此時客戶端收到響應(yīng)之后,針對響應(yīng)體的處理變得十分不方便,如果增加更多的方法,并且返回值都不相同,那么客戶端將需要根據(jù)不同的請求來特定的處理響應(yīng)體。因此為了方便客戶端處理響應(yīng)數(shù)據(jù),服務(wù)器端專門創(chuàng)建了一個返回結(jié)果類ReturnResult,并且規(guī)定服務(wù)器端的所有handler方法執(zhí)行后往response中寫入的響應(yīng)體都必須為ReturnResult。在這種情況下,使用ResponseBodyAdvice可以在不修改已有業(yè)務(wù)代碼的情況下輕松實現(xiàn)上述需求。假設(shè)自定義的返回結(jié)果類ReturnResult如下所示。

public class ReturnResult<T> {
    private int statusCode;
    private T body;
    public ReturnResult() {}
    public ReturnResult(T body) {
        this.body = body;
    }
    // 省略getter和setter
}

ReturnResult的body就是原本需要寫入response的響應(yīng)內(nèi)容,現(xiàn)在整個ReturnResult為需要寫入response的響應(yīng)內(nèi)容,相當(dāng)于ReturnResult對handler方法的返回值進行了一層封裝。

現(xiàn)在創(chuàng)建一個ReturnResultAdvice類并實現(xiàn)ResponseBodyAdvice接口,如下所示。

@ControllerAdvice
public class ReturnResultAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(@Nullable MethodParameter returnType, @Nullable Class converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, @Nullable MethodParameter returnType,
                                  @Nullable MediaType selectedContentType, @Nullable Class selectedConverterType,
                                  @Nullable ServerHttpRequest request, @Nullable ServerHttpResponse response) {
        if (body == null) {
            return null;
        }
        if (body instanceof ReturnResult) {
            return body;
        }
        return new ReturnResult<>(body);
    }
}

ReturnResultAdvice的beforeBodyWrite() 方法會在handler方法返回值寫入response前被調(diào)用。

此時調(diào)用DemoController的接口,會發(fā)現(xiàn)響應(yīng)數(shù)據(jù)結(jié)構(gòu)統(tǒng)一為ReturnResult。

小節(jié):由@ControllerAdvice注解修飾并實現(xiàn)ResponseBodyAdvice接口的類所實現(xiàn)的beforeBodyWrite()方法會在handler方法返回值寫入response前被調(diào)用,并且handler方法返回值會作為入?yún)魅?code>beforeBodyWrite(),從而可以在返回值寫入response前對返回值進行一些定制操作,例如對返回值進行一層封裝。

二. ResponseBodyAdvice的原理

首先說明一下為什么第一小節(jié)中DemoController的getDefaultDemo1() 方法的返回值類型為ResponseEntity<Demo1>,但是實際往response寫的響應(yīng)體內(nèi)容為ResponseEntity中的body。首先所有ResponseBodyAdvice接口的調(diào)用是發(fā)生在AbstractMessageConverterMethodProcessor的writeWithMessageConverters() 方法中,這個方法的聲明如下所示。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
            ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException;

其中value就是需要寫入響應(yīng)體的值,同時也是ResponseBodyAdvice要處理的值。然后如果handler方法的返回值是非ResponseEntity對象且handler方法由@ResponseBody注解修飾,那么writeWithMessageConverters() 方法的調(diào)用發(fā)生在RequestResponseBodyMethodProcessor#handleReturnValue方法中;

如果handler方法的返回值是ResponseEntity對象,那么writeWithMessageConverters() 方法的調(diào)用發(fā)生在HttpEntityMethodProcessor#handleReturnValue中,分別看一下在這兩個方法中調(diào)用writeWithMessageConverters() 時傳入的參數(shù),就可以解釋之前的疑問了。

RequestResponseBodyMethodProcessor#handleReturnValue

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    ......
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

HttpEntityMethodProcessor#handleReturnValue()

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    ......
    HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;
    ......
    writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage);
    ......
}

現(xiàn)在正式開始對ResponseBodyAdvice的原理進行分析。

已知所有ResponseBodyAdvice接口的調(diào)用是發(fā)生在AbstractMessageConverterMethodProcessor的writeWithMessageConverters() 方法中,其部分源碼如下所示。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    ......
    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                    (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ?
                    ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                    converter.canWrite(valueType, selectedMediaType)) {
                // ResponseBodyAdvice的調(diào)用發(fā)生在這里
                body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                        (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                        inputMessage, outputMessage);
                if (body != null) {
                    Object theBody = body;
                    LogFormatUtils.traceDebug(logger, traceOn ->
                            "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
                    addContentDispositionHeader(inputMessage, outputMessage);
                    if (genericConverter != null) {
                        genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                    }
                    else {
                        ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                    }
                }
                else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Nothing to write: null body");
                    }
                }
                return;
            }
        }
    }
    ......
}

AbstractMessageConverterMethodProcessor的getAdvice() 方法會返回其在構(gòu)造函數(shù)中加載好的RequestResponseBodyAdviceChain對象,下面看一下RequestResponseBodyAdviceChain的beforeBodyWrite() 方法。

public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,
        Class<? extends HttpMessageConverter<?>> converterType,
        ServerHttpRequest request, ServerHttpResponse response) {
    return processBody(body, returnType, contentType, converterType, request, response);
}
private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,
        Class<? extends HttpMessageConverter<?>> converterType,
        ServerHttpRequest request, ServerHttpResponse response) {
    // 從加載好的ResponseBodyAdvice中獲取適用于當(dāng)前handler的ResponseBodyAdvice
    for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
        if (advice.supports(returnType, converterType)) {
            // 執(zhí)行ResponseBodyAdvice的beforeBodyWrite()方法以處理handler方法返回值
            body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
                    contentType, converterType, request, response);
        }
    }
    return body;
}
private <A> List<A> getMatchingAdvice(MethodParameter parameter, Class<? extends A> adviceType) {
    // 獲取ResponseBodyAdvice集合
    List<Object> availableAdvice = getAdvice(adviceType);
    if (CollectionUtils.isEmpty(availableAdvice)) {
        return Collections.emptyList();
    }
    List<A> result = new ArrayList<>(availableAdvice.size());
    for (Object advice : availableAdvice) {
        // 判斷ResponseBodyAdvice是否由@ControllerAdvice注解修飾
        if (advice instanceof ControllerAdviceBean) {
            ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;
            // 判斷ResponseBodyAdvice是否適用于當(dāng)前handler
            if (!adviceBean.isApplicableToBeanType(parameter.getContainingClass())) {
                continue;
            }
            advice = adviceBean.resolveBean();
        }
        if (adviceType.isAssignableFrom(advice.getClass())) {
            result.add((A) advice);
        }
    }
    return result;
}

在RequestResponseBodyAdviceChain中,beforeBodyWrite() 方法調(diào)用了processBody() 方法,processBody() 方法會遍歷所有加載好并且適用于當(dāng)前handler的ResponseBodyAdvice并執(zhí)行,至此,所有由@ControllerAdvice注解修飾的ResponseBodyAdvice接口會在這里執(zhí)行。

小節(jié):由@ControllerAdvice注解修飾的ResponseBodyAdvice接口會被SpringMVC框架加載到RequestResponseBodyMethodProcessorHttpEntityMethodProcessor這兩個返回值處理器中,當(dāng)這兩個返回值處理器將返回值寫入response前,適用于當(dāng)前handler的ResponseBodyAdvice接口會被調(diào)用,從而可以完成對返回值的定制化改造。

三. ResponseBodyAdvice的加載

由第二小節(jié)可知,正是因為RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor這兩個返回值處理器會將由@ControllerAdvice注解修飾的ResponseBodyAdvice接口加載,才能夠?qū)崿F(xiàn)將返回值寫入response前調(diào)用這些ResponseBodyAdvice接口對返回值進行一些操作。那么本小節(jié)將對esponseBodyAdvice接口的加載進行學(xué)習(xí)。

首先給出結(jié)論:ResponseBodyAdvice的加載發(fā)生在RequestMappingHandlerAdapterafterPropertiesSet()方法中。

已知,RequestMappingHandlerAdapter實現(xiàn)了InitializingBean接口,因此RequestMappingHandlerAdapter實現(xiàn)了afterPropertiesSet() 方法。該方法實現(xiàn)如下。

public void afterPropertiesSet() {
    // 加載ControllerAdviceBean相關(guān)內(nèi)容(同時就會將由@ControllerAdvice注解修飾的ResponseBodyAdvice接口加載)
    initControllerAdviceCache();
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        // 獲取返回值處理器,在這里就會完成RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor的初始化,初始化的同時就會完成ResponseBodyAdvice接口的加載
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

上述實現(xiàn)中,initControllerAdviceCache() 方法會加載ControllerAdviceBean相關(guān)內(nèi)容到RequestMappingHandlerAdapter中,這其中就包含由@ControllerAdvice注解修飾的ResponseBodyAdvice接口。然后在getDefaultReturnValueHandlers() 方法中會創(chuàng)建返回值處理器,在創(chuàng)建RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor時會使用加載好的ResponseBodyAdvice接口完成這兩個返回值處理器的初始化。上述兩個方法的部分源碼如下所示。

initControllerAdviceCache()

private void initControllerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }
    // 獲取由@ControllerAdvice注解修飾的bean
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
        if (!attrMethods.isEmpty()) {
            this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
        }
        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
        if (!binderMethods.isEmpty()) {
            this.initBinderAdviceCache.put(adviceBean, binderMethods);
        }
        // 如果ControllerAdviceBean實現(xiàn)了ResponseBodyAdvice接口,那么這個ControllerAdviceBean需要加載到requestResponseBodyAdvice中
        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            requestResponseBodyAdviceBeans.add(adviceBean);
        }
    }
    if (!requestResponseBodyAdviceBeans.isEmpty()) {
        this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }
    ......
}

getDefaultReturnValueHandlers()

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
    ......
    // 創(chuàng)建并加載HttpEntityMethodProcessor
    handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
            this.contentNegotiationManager, this.requestResponseBodyAdvice));
    ...
    // 創(chuàng)建并加載RequestResponseBodyMethodProcessor
    handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
            this.contentNegotiationManager, this.requestResponseBodyAdvice));
    ......
    return handlers;
}

根據(jù)getDefaultReturnValueHandlers() 方法可知,在創(chuàng)建HttpEntityMethodProcessor或者RequestResponseBodyMethodProcessor時,會將RequestMappingHandlerAdapter加載好的ResponseBodyAdvice傳入構(gòu)造函數(shù),并且,無論是HttpEntityMethodProcessor還是RequestResponseBodyMethodProcessor,其構(gòu)造函數(shù)最終都會調(diào)用到父類AbstractMessageConverterMethodArgumentResolver的構(gòu)造函數(shù),并在其中初始化一個RequestResponseBodyAdviceChain以完成ResponseBodyAdvice的加載。構(gòu)造函數(shù)源碼如下所示。

HttpEntityMethodProcessor#HttpEntityMethodProcessor

public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters,
        @Nullable ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
    super(converters, manager, requestResponseBodyAdvice);
}

AbstractMessageConverterMethodProcessor#AbstractMessageConverterMethodProcessor

protected AbstractMessageConverterMethodProcessor(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters,
        @Nullable ContentNegotiationManager manager, @Nullable List&lt;Object&gt; requestResponseBodyAdvice) {
    super(converters, requestResponseBodyAdvice);
    this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
    this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
    this.safeExtensions.addAll(SAFE_EXTENSIONS);
}

AbstractMessageConverterMethodArgumentResolver#AbstractMessageConverterMethodArgumentResolver

public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
        @Nullable List<Object> requestResponseBodyAdvice) {
    Assert.notEmpty(converters, "'messageConverters' must not be empty");
    this.messageConverters = converters;
    this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);
    this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
}

小節(jié):RequestMappingHandlerAdapter會在其實現(xiàn)的afterPropertiesSet()方法中加載由@ControllerAdvice注解修飾的ResponseBodyAdvice接口,然后會創(chuàng)建并加載返回值處理器,在創(chuàng)建RequestResponseBodyMethodProcessorHttpEntityMethodProcessor這兩個返回值處理器時會傳入加載好的ResponseBodyAdvice,從而完成了ResponseBodyAdvice的加載。

總結(jié)

如果需要使用ResponseBodyAdvice來對handler方法的返回值做處理,則需要創(chuàng)建一個類并實現(xiàn)ResponseBodyAdvice接口,同時該類還需要被@ControllerAdvice注解修飾,這樣的一個ResponseBodyAdvice接口會被RequestMappingHandlerAdapter加載,以及在初始化RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor這兩個返回值處理器時被加載。在通過這兩個返回值處理器將返回值寫入response前,加載好的ResponseBodyAdvice接口的beforeBodyWrite() 方法會被返回值處理器調(diào)用,完成對返回值的定制化處理。

以上就是ResponseBodyAdvice的使用原理源碼解析的詳細內(nèi)容,更多關(guān)于ResponseBodyAdvice原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java文本處理之計算文本句子數(shù)

    java文本處理之計算文本句子數(shù)

    這篇文章主要為大家詳細介紹了java文本處理之計算文本句子數(shù),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • 使用JPA+querydsl如何實現(xiàn)多條件動態(tài)查詢

    使用JPA+querydsl如何實現(xiàn)多條件動態(tài)查詢

    這篇文章主要介紹了使用JPA+querydsl如何實現(xiàn)多條件動態(tài)查詢,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java微信授權(quán)登陸的實現(xiàn)示例

    Java微信授權(quán)登陸的實現(xiàn)示例

    微信授權(quán)登錄,官方文檔寫的比較簡潔。所以對于會的人一目了然,而對于新手剛?cè)腴T的人來說是舉步維艱。本文詳細的介紹了Java微信授權(quán)登陸的實現(xiàn)示例,感興趣的朋友可以了解一下
    2021-06-06
  • JDBC使用游標(biāo)實現(xiàn)分頁查詢的方法

    JDBC使用游標(biāo)實現(xiàn)分頁查詢的方法

    這篇文章主要介紹了JDBC使用游標(biāo)實現(xiàn)分頁查詢的方法,實例分析了jdbc查詢過程中游標(biāo)的使用及查詢分頁相關(guān)實現(xiàn)技巧,需要的朋友可以參考下
    2016-08-08
  • Spring實戰(zhàn)之清除緩存操作示例

    Spring實戰(zhàn)之清除緩存操作示例

    這篇文章主要介紹了Spring實戰(zhàn)之清除緩存操作,結(jié)合實例形式詳細分析了spring清除緩存操作具體步驟、配置、領(lǐng)域模型及相關(guān)使用技巧,需要的朋友可以參考下
    2020-01-01
  • Java8中的類型注解淺析

    Java8中的類型注解淺析

    這篇文章主要介紹了Java8中的類型注解淺析,java8之前,注解只能是在聲明的地方所使用,java8里面的注解則可以應(yīng)用在任何地方,需要的朋友可以參考下
    2014-06-06
  • Java httpClient連接池支持多線程高并發(fā)的實現(xiàn)

    Java httpClient連接池支持多線程高并發(fā)的實現(xiàn)

    本文主要介紹了Java httpClient連接池支持多線程高并發(fā)的實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Spring Boot之FilterRegistrationBean-自定義Filter詳解

    Spring Boot之FilterRegistrationBean-自定義Filter詳解

    這篇文章主要介紹了Spring Boot之FilterRegistrationBean-自定義Filter詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • ActiveMQ簡單入門(新手必看篇)

    ActiveMQ簡單入門(新手必看篇)

    下面小編就為大家?guī)硪黄狝ctiveMQ簡單入門(新手必看篇)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • Spring AOP結(jié)合注解實現(xiàn)接口層操作日志記錄

    Spring AOP結(jié)合注解實現(xiàn)接口層操作日志記錄

    在項目開發(fā)中我們需要記錄接口的操作日志:包含請求參數(shù)、響應(yīng)參數(shù)、接口所屬模塊、接口功能描述、請求地址、ip地址等信息;實現(xiàn)思路很簡單就是基于注解和aop的方式去記錄日志,主要的難點在于日志表結(jié)構(gòu)、注解的設(shè)計已經(jīng)aop實現(xiàn)的一些比較好的實現(xiàn)方式的借鑒
    2022-08-08

最新評論

东乌珠穆沁旗| 邢台县| 外汇| 河源市| 九龙坡区| 无极县| 班玛县| 林周县| 旬阳县| 宝鸡市| 喜德县| 津南区| 育儿| 舒城县| 同江市| 宁乡县| 凤凰县| 巨鹿县| 石屏县| 兰西县| 湖州市| 华坪县| 吉林市| 马山县| 疏附县| 温州市| 潢川县| 托克逊县| 中阳县| 百色市| 鹤壁市| 浪卡子县| 清原| 朝阳区| 阳原县| 三台县| 广昌县| 文水县| 年辖:市辖区| 衢州市| 沭阳县|