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

SpringBoot web開發(fā)源碼深入分析

 更新時(shí)間:2022年10月09日 16:34:04   作者:Decade0712  
Web開發(fā)的核心內(nèi)容主要包括內(nèi)嵌的Servlet容器和SpringMVCSpringBoot使用起來非常簡(jiǎn)潔,大部分配置都有SpringBoot自動(dòng)裝配,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧

一、MVC自動(dòng)配置

1、默認(rèn)支持的功能

Spring Boot為Spring MVC提供了自動(dòng)配置,默認(rèn)支持以下功能

  • ContentNegotiatingViewResolver和BeanNameViewResolver視圖解析器
  • 支持靜態(tài)資源,包括webjars
  • 轉(zhuǎn)換器的自動(dòng)注冊(cè)、自定義轉(zhuǎn)換器GenericConverter與格式化
  • 支持http消息轉(zhuǎn)換(請(qǐng)求與響應(yīng))
  • MessageCodesResolver錯(cuò)誤消息
  • 首頁(yè)映射
  • 圖標(biāo)自定義
  • 自動(dòng)使用ConfigurableWebBindingInitializer,博主百度了一下,它的主要作用就是初始化WebDataBinder,將請(qǐng)求的參數(shù)轉(zhuǎn)化為對(duì)應(yīng)的JavaBean,并且會(huì)結(jié)合類型、格式轉(zhuǎn)換等API一起使用

2、靜態(tài)資源與首頁(yè)相關(guān)源碼解析

SpringBoot啟動(dòng)時(shí)會(huì)默認(rèn)加載 xxxAutoConfiguration 類(自動(dòng)配置類),SpringMVC功能的自動(dòng)配置類為 WebMvcAutoConfiguration

@AutoConfiguration(
    after = {DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class}
)
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
public class WebMvcAutoConfiguration {
	...
}

然后我們可以看到他有一個(gè)靜態(tài)內(nèi)部類WebMvcAutoConfigurationAdapter,可以看到這是一個(gè)配置類

@Configuration(
    proxyBeanMethods = false
)
@Import({WebMvcAutoConfiguration.EnableWebMvcConfiguration.class})
// 將配置文件的相關(guān)屬性和括號(hào)中的兩個(gè)類進(jìn)行了綁定,然后注冊(cè)到容器中。WebMvcProperties和spring.mvc開頭的配置、WebProperties和spring.web開頭的配置
@EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
	...
}

然后我們發(fā)現(xiàn),這個(gè)配置類只有一個(gè)有參構(gòu)造器,在這種情況下,我們默認(rèn)有參構(gòu)造器所有參數(shù)的值都會(huì)從容器中獲取

// 這里的WebProperties 和WebMvcProperties都在上面和配置進(jìn)行綁定過了,如果我們沒有配置該配置項(xiàng),那就去類中取默認(rèn)配置的值
// ListableBeanFactory beanFactory Spring的beanFactory
// 其他的可以自己去了解下,博主這里沒有特地去搜了
public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties, ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider, ObjectProvider<WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider, ObjectProvider<DispatcherServletPath> dispatcherServletPath, ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {
	this.resourceProperties = webProperties.getResources();
	this.mvcProperties = mvcProperties;
	this.beanFactory = beanFactory;
	this.messageConvertersProvider = messageConvertersProvider;
	this.resourceHandlerRegistrationCustomizer = (WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer)resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
	this.dispatcherServletPath = dispatcherServletPath;
	this.servletRegistrations = servletRegistrations;
	this.mvcProperties.checkConfiguration();
}

那么我們的靜態(tài)資源映射以及webjars都是在哪里進(jìn)行配置的呢,我們往下看找到一個(gè)方法

public void addResourceHandlers(ResourceHandlerRegistry registry) {
	// 判斷這個(gè)isAddMappings屬性是否為false,默認(rèn)值是true,如果我們?cè)趛aml文件或者properties中改為false,那么就會(huì)進(jìn)這個(gè)條件語(yǔ)句,后面的靜態(tài)資源路徑以及webjars都不會(huì)生效了
    if (!this.resourceProperties.isAddMappings()) {
        logger.debug("Default resource handling disabled");
    } else {
    	// webjars的規(guī)則
        this.addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
        // 默認(rèn)靜態(tài)資源地址的處理規(guī)則
        this.addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
            registration.addResourceLocations(this.resourceProperties.getStaticLocations());
            if (this.servletContext != null) {
                ServletContextResource resource = new ServletContextResource(this.servletContext, "/");
                registration.addResourceLocations(new Resource[]{resource});
            }
        });
    }
}

那我們歡迎頁(yè)是在哪里配置的呢?

我們發(fā)現(xiàn),在這個(gè)WebMvcAutoConfiguration下面還有一個(gè)靜態(tài)內(nèi)部類EnableWebMvcConfiguration,它也是一個(gè)配置類

這里面有一個(gè)方法welcomePageHandlerMapping()

HandlerMapping(處理映射器):根據(jù)URL找到對(duì)應(yīng)的處理器

@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
    WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
    welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
    welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
    return welcomePageHandlerMapping;
}

點(diǎn)進(jìn)WelcomePageHandlerMapping的構(gòu)造方法可以看到,它的邏輯大體上為,如果welcomePage不等于null,而且staticPathPattern是默認(rèn)的/**,就會(huì)去我們的靜態(tài)資源文件夾找index.html,否則就去找有沒有能處理/index接口的映射器

這里的staticPathPattern和spring.mvc.static-path-pattern是綁定在一起的

WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {
    if (welcomePage != null && "/**".equals(staticPathPattern)) {
        logger.info("Adding welcome page: " + welcomePage);
        this.setRootViewName("forward:index.html");
    } else if (this.welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {
        logger.info("Adding welcome page template: index");
        this.setRootViewName("index");
    }
}

3、Rest映射及源碼分析

Rest風(fēng)格支持:使用HTTP請(qǐng)求方式動(dòng)詞來表示對(duì)資源的操作

具體的可以看我之前的 【SpringMVC】Restful風(fēng)格及中文亂碼問題

  • 原來獲取用戶信息–/getUSer、刪除用戶–/deleteUser、修改用戶–editUser、保存用戶/saveUser
  • 使用REST風(fēng)格獲取用戶信息–GET、刪除用戶–DELETE、修改用戶–PUT、保存用戶POST

核心源碼部分:WebMvcAutoConfiguration類下的hiddenHttpMethodFilter()方法

核心配置:如果要從頁(yè)面發(fā)起PUT、DELETE請(qǐng)求,需要在yaml文件中將spring.mvc.hiddenmethod.filter.enabled設(shè)置為true,如果是客戶端工具如postman發(fā)起,則無需開啟

@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
@ConditionalOnProperty(
    prefix = "spring.mvc.hiddenmethod.filter",
    name = {"enabled"}
)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
    return new OrderedHiddenHttpMethodFilter();
}

我們點(diǎn)開OrderedHiddenHttpMethodFilter可以看到它繼承了HiddenHttpMethodFilter這個(gè)類

我們接著跟進(jìn)去 發(fā)現(xiàn)里面有一個(gè)doFilterInternal()方法,請(qǐng)求進(jìn)來都會(huì)被這個(gè)方法攔截

package org.springframework.web.filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
    private static final List<String> ALLOWED_METHODS;
    public static final String DEFAULT_METHOD_PARAM = "_method";
    private String methodParam = "_method";
    public HiddenHttpMethodFilter() {
    }
    public void setMethodParam(String methodParam) {
        Assert.hasText(methodParam, "'methodParam' must not be empty");
        this.methodParam = methodParam;
    }
	// 它會(huì)對(duì)請(qǐng)求方法進(jìn)行判斷
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest requestToUse = request;
        // 如果是表單是post請(qǐng)求且請(qǐng)求正常,那么它會(huì)判斷請(qǐng)求參數(shù)里面是否存在_method這個(gè)參數(shù)
        if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
            String paramValue = request.getParameter(this.methodParam);
            // 判斷_method參數(shù)是否為空
            if (StringUtils.hasLength(paramValue)) {
            	// 將參數(shù)轉(zhuǎn)成大寫
                String method = paramValue.toUpperCase(Locale.ENGLISH);
                // 判斷_method參數(shù)是否是PUT、DELETE、PATCH其中的一個(gè),如果滿足就使用requesWrapper重寫了HttpServletRequest的getMethod方法,返回的是傳入的值
                if (ALLOWED_METHODS.contains(method)) {
                    requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
                }
            }
        }
		// 這里過濾器放行時(shí)的request是處理后得到的HttpMethodRequestWrapper
        filterChain.doFilter((ServletRequest)requestToUse, response);
    }
    static {
        ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
    }
	// HttpServletRequestWrapper類還是實(shí)現(xiàn)了HttpServletRequest接口
    private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
        private final String method;
        public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
            super(request);
            this.method = method;
        }
        public String getMethod() {
            return this.method;
        }
    }
}

我們可以寫一個(gè)html頁(yè)面還有一個(gè)控制器測(cè)試一下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
Hello World!
<form action="/test/user" method="get">
    <input type="submit" value="REST-GET">
</form>
<form action="/test/user" method="post">
    <input type="submit" value="REST-POST">
</form>
<form action="/test/user" method="post">
    <input name="_method" type="hidden" value="PUT">
    <input type="submit" value="REST-PUT">
</form>
<form action="/test/user" method="post">
    <input name="_method" type="hidden" value="DELETE">
    <input type="submit" value="REST-DELETE">
</form>
</body>
</html>
package com.decade.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value = "/test")
public class TestController {
    @RequestMapping(value = "/user", method = RequestMethod.GET)
    @ResponseBody
    public String queryUser() {
        return "get查詢用戶的信息";
    }
    @RequestMapping(value = "/user", method = RequestMethod.POST)
    @ResponseBody
    public String editUser() {
        return "post保存用戶的信息";
    }
    @RequestMapping(value = "/user", method = RequestMethod.DELETE)
    @ResponseBody
    public String deleteUser() {
        return "delete刪除用戶的信息";
    }
    @RequestMapping(value = "/user", method = RequestMethod.PUT)
    @ResponseBody
    public String saveUser() {
        return "put編輯用戶的信息";
    }
}

驗(yàn)證的時(shí)候遇到一個(gè)問題,那就是如果引入了spring-boot-starter-security這個(gè)依賴

那么調(diào)用POST、PUT和DELETE接口時(shí)就會(huì)出錯(cuò)

博主查了一下,這是因?yàn)镾pring Boot 與 SpringSecurity整合后,為了防御csrf攻擊,只有GET|OPTIONS|HEAD|TRACE|CONNECTION可以通過

其他方法請(qǐng)求時(shí),需要有token

我將SpringSecurity的依賴注掉之后,驗(yàn)證就通過了

拓展:如果我們要修改HiddenHttpMethodFilter里過濾方法中判斷的參數(shù)名稱,我們可以自己寫一個(gè)配置類,例如我們想將它由_method改為_m,那可以這么寫

package com.decade.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;
@Configuration(proxyBeanMethods = false)
public class MyMvcConfig {
    @Bean
    public HiddenHttpMethodFilter createHiddenHttpMethodFilter() {
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
        // 利用setMethodParam設(shè)置自己想要的參數(shù)名
        hiddenHttpMethodFilter.setMethodParam("_m");
        return hiddenHttpMethodFilter;
    }
}

4、請(qǐng)求映射原理

1)接下來我們研究一下,Spring Boot是怎么將一個(gè)個(gè)請(qǐng)求匹配到對(duì)應(yīng)的處理器(即controller)的

根據(jù)我們之前SpringMVC的學(xué)習(xí) 我們可以知道 所有的請(qǐng)求都會(huì)被DispatcherServlet攔截,我們一直跟下去可以發(fā)現(xiàn),DispatcherServlet實(shí)際上也是繼承了HttpServlet

我們著重分析一下DispatcherServlet中的doDispatch()方法,我們發(fā)現(xiàn)有一個(gè)getHandler()方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    try {
        try {
            ModelAndView mv = null;
            Object dispatchException = null;
            try {
            	// 檢查是否是文件上傳請(qǐng)求
                processedRequest = this.checkMultipart(request);
                multipartRequestParsed = processedRequest != request;
                // 決定是哪個(gè)handler處理當(dāng)前請(qǐng)求,HandlerMapping(處理映射器):根據(jù)URL找到對(duì)應(yīng)的處理器
                mappedHandler = this.getHandler(processedRequest);
                if (mappedHandler == null) {
                    this.noHandlerFound(processedRequest, response);
                    return;
                }

我們嘗試發(fā)起一個(gè)請(qǐng)求,使用debug模式可以發(fā)現(xiàn),這個(gè)getHandler()方法就是對(duì)容器中的handlerMapping進(jìn)行一個(gè)遍歷,查看哪個(gè)處理映射器能處理這個(gè)請(qǐng)求

我們可以看到這里有WelcomePageHandlerMapping(與首頁(yè)相關(guān)的)等映射器

通過分析RequestMappingHandlerMapping,我們可以發(fā)現(xiàn),他這里面保存了所有@RequestMapping 路徑和與之對(duì)應(yīng)控制器類下的方法的映射規(guī)則

我們繼續(xù)深入到AbstractHandlerMapping這個(gè)類下的getHandler()---->getHandlerInternal()方法

然后AbstractHandlerMethodMapping這個(gè)類繼承了AbstractHandlerMapping,并完成了關(guān)于getHandlerInternal()的重寫,接著就是lookupHandlerMethod()----->addMatchingMappings()

------>getMatchingMapping()

然后又跟到getMatchingMapping()------->RequestMappingInfo.getMatchingCondition()

最后,我們發(fā)現(xiàn)在RequestMappingInfo這個(gè)類中,getMatchingCondition()這個(gè)方法會(huì)對(duì)請(qǐng)求類型做一個(gè)篩選,這樣就能將相同路徑不同請(qǐng)求方法的接口區(qū)分開來,如果存在相同請(qǐng)求類型且請(qǐng)求路徑也相同,那么系統(tǒng)就會(huì)報(bào)錯(cuò)

同樣的,如果我們需要自定義映射處理,我們也可以自己給容器中放HandlerMapping

2)問題:那么我們還可以思考一下,我們最開始遍歷的那些handlerMapping是從哪里來的呢?

我們的目光還是回到DispatcherServlet,這里面有一個(gè)initHandlerMappings()

這里他會(huì)從容器中獲取實(shí)現(xiàn)了HandlerMapping接口的處理映射器

這樣 我們就基本完成了spring boot關(guān)于web開發(fā)的源碼分析

到此這篇關(guān)于SpringBoot web開發(fā)源碼深入分析的文章就介紹到這了,更多相關(guān)SpringBoot web開發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 關(guān)于ConditionalOnMissingBean失效問題的追蹤

    關(guān)于ConditionalOnMissingBean失效問題的追蹤

    這篇文章主要介紹了關(guān)于ConditionalOnMissingBean失效問題的追蹤方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • MyBatis-Plus分頁(yè)插件不生效的解決方法

    MyBatis-Plus分頁(yè)插件不生效的解決方法

    這篇文章主要介紹了MyBatis-Plus分頁(yè)插件不生效的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Future與FutureTask接口實(shí)現(xiàn)示例詳解

    Future與FutureTask接口實(shí)現(xiàn)示例詳解

    這篇文章主要為大家介紹了Future與FutureTask接口實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Java多線程編程中易混淆的3個(gè)關(guān)鍵字總結(jié)

    Java多線程編程中易混淆的3個(gè)關(guān)鍵字總結(jié)

    這篇文章主要介紹了Java多線程編程中易混淆的3個(gè)關(guān)鍵字總結(jié),本文總結(jié)了、volatile、ThreadLocal、synchronized等3個(gè)關(guān)鍵字,對(duì)這幾個(gè)容易混淆概念的關(guān)鍵字分別做了講解,需要的朋友可以參考下
    2015-03-03
  • java微信公眾號(hào)開發(fā)(搭建本地測(cè)試環(huán)境)

    java微信公眾號(hào)開發(fā)(搭建本地測(cè)試環(huán)境)

    這篇文章主要介紹了java微信公眾號(hào)開發(fā),主要內(nèi)容有測(cè)試公眾號(hào)與本地測(cè)試環(huán)境搭建,需要的朋友可以參考下
    2015-12-12
  • springboot 整合 seata的配置過程

    springboot 整合 seata的配置過程

    本文給大家介紹springboot 整合 seata的配置過程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-08-08
  • SpringSecurity根據(jù)自定義異常返回登錄錯(cuò)誤提示信息(賬戶鎖定)

    SpringSecurity根據(jù)自定義異常返回登錄錯(cuò)誤提示信息(賬戶鎖定)

    本文介紹了在SpringSecurity中根據(jù)自定義異常返回登錄錯(cuò)誤提示信息的方法,特別是在賬戶鎖定時(shí),通過記錄輸錯(cuò)次數(shù)、重寫校驗(yàn)方法并拋出自定義異常,感興趣的可以了解一下
    2025-01-01
  • 使用Java生成32位16進(jìn)制密鑰的代碼實(shí)現(xiàn)

    使用Java生成32位16進(jìn)制密鑰的代碼實(shí)現(xiàn)

    在許多加密和安全應(yīng)用中,生成隨機(jī)的密鑰是至關(guān)重要的一步,密鑰通常以16進(jìn)制形式表示,并且具有特定的長(zhǎng)度,在這篇博客中,我們將探討如何使用Java生成一個(gè)32位長(zhǎng)度的16進(jìn)制密鑰,并展示詳細(xì)的代碼示例和運(yùn)行結(jié)果,需要的朋友可以參考下
    2024-08-08
  • 深入解析Java類加載的案例與實(shí)戰(zhàn)教程

    深入解析Java類加載的案例與實(shí)戰(zhàn)教程

    本篇文章主要介紹Tomcat類加載器架構(gòu),以及基于類加載和字節(jié)碼相關(guān)知識(shí),去分析動(dòng)態(tài)代理的原理,對(duì)Java類加載相關(guān)知識(shí)感興趣的朋友一起看看吧
    2022-05-05
  • SpringBoot項(xiàng)目Docker部署三種方式

    SpringBoot項(xiàng)目Docker部署三種方式

    本文主要介紹了SpringBoot項(xiàng)目Docker部署三種方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-08-08

最新評(píng)論

天祝| 青神县| 二连浩特市| 丽水市| 乐亭县| 丰城市| 枣阳市| 虞城县| 肥城市| 四川省| 建水县| 县级市| 卓尼县| 汽车| 奇台县| 哈密市| 丹巴县| 邵阳市| 南雄市| 五河县| 额敏县| 成武县| 靖西县| 遂溪县| 霍州市| 旺苍县| 伊金霍洛旗| 静海县| 广水市| 遵化市| 宁津县| 孝义市| 安阳县| 理塘县| 滦平县| 湖口县| 万盛区| 莎车县| 娄烦县| 靖江市| 浦北县|