SpringBoot接管SpringMvc

SpringBoot接管SpringMvc

Spring Web MVC framework(一般簡稱爲「Spring MVC」)是一個豐富的「model 視圖控制器」web framework。 Spring MVC 容許您建立特殊的@Controller@RestController beans 來處理傳入的 HTTP 請求。控制器中的方法使用@RequestMapping annotations 映射到 HTTP。html

如下 code 顯示了爲 JSON 數據提供服務的典型@RestControllerjava

@RestController
@RequestMapping(value="/users")
public class MyRestController {
​
    @RequestMapping(value="/{user}", method=RequestMethod.GET)
    public User getUser(@PathVariable Long user) {
        // ...
    }
​
    @RequestMapping(value="/{user}/customers", method=RequestMethod.GET)
    List<Customer> getUserCustomers(@PathVariable Long user) {
        // ...
    }
​
    @RequestMapping(value="/{user}", method=RequestMethod.DELETE)
    public User deleteUser(@PathVariable Long user) {
        // ...
    }
​
}

 

Spring MVC 是核心 Spring Framework 的一部分,詳細信息可在reference 文檔中找到。還有幾個 guides 覆蓋了spring.io/guides的 Spring MVC。jquery

Spring MVC Auto-configuration

Spring Boot 爲 Spring MVC 提供 auto-configuration,適用於大多數 applications。git

auto-configuration 在 Spring 的默認值之上添加如下 features:github

  • 包含ContentNegotiatingViewResolverBeanNameViewResolver beans。web

  • 支持提供靜態資源,包括對 WebJars 的支持(稍後在本文檔中 ))。spring

  • 自動註冊ConverterGenericConverterFormatter beans。apache

  • 支持HttpMessageConverters(稍後在本文檔中)。json

  • 自動註冊MessageCodesResolver(稍後在本文檔中).api

  • 靜態index.html支持。

  • 自定義Favicon支持(稍後在本文檔中)。

  • 自動使用ConfigurableWebBindingInitializer bean(稍後在本文檔中)。

若是你想保留 Spring Boot MVC features 而且想要添加額外的MVC configuration(攔截器,格式化程序,視圖控制器和其餘 features),你能夠添加本身的@Configuration class 類型爲WebMvcConfigurer而不是 @EnableWebMvc。若是您但願提供RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver的自定義實例,則能夠聲明WebMvcRegistrationsAdapter實例以提供此類組件。

若是要徹底控制 Spring MVC,能夠添加本身的@Configuration註釋@EnableWebMvc

SpringBoot對SpringMVC的自動配置不須要了,全部都是咱們本身配置;全部的SpringMVC的自動配置都失效了

咱們須要在配置類中添加@EnableWebMvc便可;

//使用WebMvcConfigurerAdapter能夠來擴展SpringMVC的功能
@EnableWebMvc
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {
​
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
       // super.addViewControllers(registry);
        //瀏覽器發送 /atguigu 請求來到 success
        registry.addViewController("/topcheer").setViewName("success");
    }
}

 

原理:

爲何@EnableWebMvc自動配置就失效了;

1)@EnableWebMvc的核心

@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

 

2)、

@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
}

3)、

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
        WebMvcConfigurerAdapter.class })
//容器中沒有這個組件的時候,這個自動配置類才生效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
}

 

4)、@EnableWebMvc將WebMvcConfigurationSupport組件導入進來;

5)、導入的WebMvcConfigurationSupport只是SpringMVC最基本的功能;

HttpMessageConverters

Spring MVC 使用HttpMessageConverter接口轉換 HTTP 請求和響應。明智的默認設置包含在開箱即用中。例如,objects 能夠自動轉換爲 JSON(經過使用 Jackson library)或 XML(若是可用,則使用 Jackson XML 擴展,或者若是 Jackson XML 擴展不可用,則使用 JAXB)。默認狀況下,strings 在UTF-8中編碼。

若是須要添加或自定義轉換器,可使用 Spring Boot 的HttpMessageConverters class,以下面的清單所示:

import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;
​
@Configuration
public class MyConfiguration {
​
    @Bean
    public HttpMessageConverters customConverters() {
        HttpMessageConverter<?> additional = ...
        HttpMessageConverter<?> another = ...
        return new HttpMessageConverters(additional, another);
    }
​
}

 

context 中存在的任何HttpMessageConverter bean 都會添加到轉換器列表中。您也能夠以相同的方式覆蓋默認轉換器。

自定義 JSON 序列化程序和反序列化程序

若是使用 Jackson 序列化和反序列化 JSON 數據,則可能須要編寫本身的JsonSerializerJsonDeserializer classes。自定義序列化程序一般是經過模塊在 Jackson 註冊,但 Spring Boot 提供了另外一種@JsonComponent 註釋,能夠更容易地直接註冊 Spring Beans。

您能夠直接在JsonSerializerJsonDeserializer __mplement 上使用@JsonComponent annotation。您也能夠在包含 serializers/deserializers 做爲內部 classes 的 classes 上使用它,以下面的示例所示:

import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.jackson.*;
​
@JsonComponent
public class Example {
​
    public static class Serializer extends JsonSerializer<SomeObject> {
        // ...
    }
​
    public static class Deserializer extends JsonDeserializer<SomeObject> {
        // ...
    }
​
}

 

ApplicationContext中的全部@JsonComponent beans 都會自動在 Jackson 中註冊。由於@JsonComponent是 meta-annotated 和@Component,因此適用一般的 component-scanning 規則。

也能夠用FastJson進行序列化和反序列化

 @Override
​
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
​
    /*
     先把JackSon的消息轉換器刪除.
     備註: (1)源碼分析可知,返回json的過程爲:
                Controller調用結束後返回一個數據對象,for循環遍歷conventers,找到支持application/json的HttpMessageConverter,而後將返回的數據序列化成json。
                具體參考org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor的writeWithMessageConverters方法
           (2)因爲是list結構,咱們添加的fastjson在最後。所以必需要將jackson的轉換器刪除,否則會先匹配上jackson,致使沒使用fastjson
​
    */
        for (int i = converters.size() - 1; i >= 0; i--) {
            if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
                converters.remove(i);
            }
        }
        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
        //自定義fastjson配置
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(
                SerializerFeature.WriteMapNullValue,        // 是否輸出值爲null的字段,默認爲false,咱們將它打開
                SerializerFeature.WriteNullListAsEmpty,     // 將Collection類型字段的字段空值輸出爲[]
                SerializerFeature.WriteNullStringAsEmpty,   // 將字符串類型字段的空值輸出爲空字符串
                SerializerFeature.WriteNullNumberAsZero,    // 將數值類型字段的空值輸出爲0
                SerializerFeature.WriteDateUseDateFormat,
                SerializerFeature.DisableCircularReferenceDetect    // 禁用循環引用
​
        );
​
        fastJsonHttpMessageConverter.setFastJsonConfig(config);
​
        // 添加支持的MediaTypes;不添加時默認爲*/*,也就是默認支持所有
// 可是MappingJackson2HttpMessageConverter裏面支持的MediaTypes爲application/json
​
​
        List<MediaType> fastMediaTypes = new ArrayList<>();
        MediaType mediaType = MediaType.parseMediaType("text/html;charset=UTF-8");
        fastMediaTypes.add(mediaType);
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
        // fastJsonHttpMessageConverter.setDefaultCharset(Charset.forName("UTF-8"));
        converters.add(fastJsonHttpMessageConverter);
​
    }

 

Spring Boot 還提供了JsonObjectSerializerJsonObjectDeserializer base classes,它們在序列化 objects 時爲標準 Jackson 版本提供了有用的替代方法。有關詳細信息,請參閱 Javadoc 中的JsonObjectSerializerJsonObjectDeserializer

MessageCodesResolver

Spring MVC 有一個生成錯誤代碼的策略,用於從 binding 錯誤中呈現錯誤消息:MessageCodesResolver。若是設置spring.mvc.message-codes-resolver.format property PREFIX_ERROR_CODEPOSTFIX_ERROR_CODE,Spring Boot 會爲您建立一個(請參閱DefaultMessageCodesResolver.Format中的枚舉)。

靜態內容

默認狀況下,Spring Boot 從 classpath 中的/static(或/public/resources/META-INF/resources)目錄或ServletContext的根目錄中提供靜態內容。它使用來自 Spring MVC 的ResourceHttpRequestHandler,以便您能夠經過添加本身的WebMvcConfigurer並覆蓋addResourceHandlers方法來修改該行爲。

在 stand-alone web application 中,容器中的默認 servlet 也被啓用並充當回退,若是 Spring 決定不處理它,則從ServletContext的根目錄提供內容。大多數 time,這都不會發生(除非你修改默認的 MVC configuration),由於 Spring 老是能夠經過DispatcherServlet來處理請求。

默認狀況下,資源映射到/**,但您可使用spring.mvc.static-path-pattern property 對其進行調整。例如,將全部資源從新定位到/resources/**能夠實現以下:

spring.mvc.static-path-pattern=/resources/**

您還可使用spring.resources.static-locations property 自定義靜態資源位置(將默認值替換爲目錄位置列表)。根 Servlet context 路徑"/"也會自動添加爲位置。

除了前面提到的「標準」靜態資源位置以外,還爲Webjars 內容作了一個特例。若是 jar files 包含在 Webjars 格式中,則中包含路徑的全部資源都將從 jar files 提供。

若是 application 打包爲 jar,請不要使用src/main/webapp目錄。雖然這個目錄是一個 common 標準,但它只能用 war 打包,若是生成一個 jar,它會被大多數 build 工具默默忽略。

Spring Boot 還支持 Spring MVC 提供的高級資源處理 features,容許使用 cache-busting 靜態資源等用例或使用 version 不可知 URL 進行 Webjars。

要爲 Webjars 使用 version 不可知 URL,請添加webjars-locator-core依賴項。而後聲明你的 Webjar。使用 jQuery 做爲 example,添加"/webjars/jquery/jquery.min.js"會致使"/webjars/jquery/x.y.z/jquery.min.js"。其中x.y.z是 Webjar version。

若是使用 JBoss,則須要聲明webjars-locator-jboss-vfs依賴項而不是webjars-locator-core。不然,全部 Webjars 都將解析爲404

要使用緩存清除,如下 configuration 會爲全部靜態資源配置緩存清除解決方案,從而在 URL 中有效添加內容哈希,例如``:

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

因爲ResourceUrlEncodingFilter對於 Thymeleaf 和 FreeMarker 來講是 auto-configured,所以在運行時會在模板中重寫資源連接。您應該在使用 JSP 時手動聲明此過濾器。目前不支持其餘模板引擎,但可使用自定義模板 macros/helpers 並使用ResourceUrlProvider

當使用例如 JavaScript 模塊加載器動態加載資源時,不能重命名 files。這就是爲何其餘策略也獲得支持並能夠合併的緣由。 「固定」策略在 URL 中添加靜態 version string 而不更改文件 name,以下面的示例所示:

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
spring.resources.chain.strategy.fixed.enabled=true
spring.resources.chain.strategy.fixed.paths=/js/lib/
spring.resources.chain.strategy.fixed.version=v12

使用此 configuration,位於"/js/lib/"下的 JavaScript 模塊使用固定版本控制策略("/v12/js/lib/mymodule.js"),而其餘資源仍使用內容 1(``)。

有關更多支持的選項,請參閱資源屬性

這個 feature 已在專用的博客文章和 Spring Framework 的reference 文檔中進行了詳細描述。

歡迎頁面

Spring Boot 支持靜態和模板化歡迎頁面。它首先在配置的靜態內容位置中查找index.html文件。若是找不到,則查找index模板。若是找到任何一個,它將自動用做 application 的歡迎頁面。

Custom Favicon

Spring Boot 在配置的靜態內容位置和 classpath 的根(在該 order 中)中查找favicon.ico。若是存在這樣的文件,它將自動用做 application 的 favicon。

路徑匹配和內容談判

Spring MVC 能夠經過查看請求路徑並將其與 application 中定義的映射相匹配來將傳入的 HTTP 請求映射處處理程序(對於 example,註解在 Controller 方法上)。

Spring Boot 選擇默認禁用後綴 pattern 匹配,這意味着像"GET /projects/spring-boot.json"這樣的請求將不會與@GetMapping("/projects/spring-boot")映射匹配。這被認爲是Spring MVC applications 的最佳實踐。這個 feature 在過去主要用於 HTTP clients,它沒有發送適當的「Accept」請求 headers;咱們須要確保將正確的 Content Type 發送到 client。現在,Content Negotiation 更加可靠。

還有其餘方法能夠處理不一致發送正確的「接受」請求 headers 的 HTTP 客戶端。咱們可使用查詢參數來確保像"GET /projects/spring-boot?format=json"這樣的請求將映射到@GetMapping("/projects/spring-boot"),而不是使用後綴匹配:

spring.mvc.contentnegotiation.favor-parameter=true

# We can change the parameter name, which is "format" by default:
# spring.mvc.contentnegotiation.parameter-name=myparam

# We can also register additional file extensions/media types with:
spring.mvc.contentnegotiation.media-types.markdown=text/markdown

若是您瞭解警告並仍但願您的 application 使用後綴 pattern 匹配,則須要如下 configuration:

spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-suffix-pattern=true

或者,不是打開全部後綴模式,而是僅支持已註冊的後綴模式更安全:

spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-registered-suffix-pattern=true

# You can also register additional file extensions/media types with:
# spring.mvc.contentnegotiation.media-types.adoc=text/asciidoc

ConfigurableWebBindingInitializer

Spring MVC 使用WebBindingInitializer爲特定請求初始化WebDataBinder。若是您建立本身的ConfigurableWebBindingInitializer @Bean,Spring Boot 會自動配置 Spring MVC 以使用它。

模板引擎

與 REST web services 同樣,您也可使用 Spring MVC 來提供動態 HTML 內容。 Spring MVC 支持各類模板技術,包括 Thymeleaf,FreeMarker 和 JSP。此外,許多其餘模板引擎包括他們本身的 Spring MVC 集成。

Spring Boot 包括對如下模板引擎的 auto-configuration 支持:

若是可能,應該避免使用 JSP。使用嵌入式 servlet 容器時有幾個已知限制

當您使用其中一個模板引擎和默認的 configuration 時,您的模板將從src/main/resources/templates自動獲取。

根據您運行 application 的方式,IntelliJ IDEA 以不一樣方式命令 classpath。 從主方法中運行 IDE 中的 application 致使與使用 Maven 或 Gradle 或從其打包的 jar 運行 application 時不一樣的 ordering。這可能致使 Spring Boot 沒法在 classpath 上找到模板。若是遇到此問題,能夠從新排序 IDE 中的 classpath 以首先放置模塊的 classes 和 resources。或者,您能夠配置模板前綴以搜索 classpath 上的每一個templates目錄,以下所示:classpath*:/templates/

錯誤處理

默認狀況下,Spring Boot 提供/error映射,以合理的方式處理全部錯誤,並在 servlet 容器中註冊爲「global」錯誤頁面。對於機器客戶端,它會生成一個 JSON 響應,其中包含錯誤,HTTP 狀態和 exception 消息的詳細信息。對於瀏覽器客戶端,有一個「whitelabel」錯誤視圖,以 HTML 格式呈現相同的數據(要自定義它,添加一個解析爲errorView)。要徹底替換默認行爲,能夠實現ErrorController並註冊該類型的 bean 定義,或者添加ErrorAttributes類型的 bean 以使用現有機制但替換內容。

BasicErrorController能夠用做自定義ErrorController的 base class。若是要爲新的 content type 添加處理程序(默認狀況下是專門處理text/html併爲其餘全部內容提供後備),這將特別有用。爲此,請擴展BasicErrorController,添加具備produces屬性的@RequestMapping的公共方法,並建立新類型的 bean。

您還能夠定義一個使用@ControllerAdvice註釋的 class,以便爲特定控制器 and/or exception 類型自定義 JSON 文檔 return,以下面的示例所示:

@ControllerAdvice(basePackageClasses = AcmeController.class)
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {
​
    @ExceptionHandler(YourException.class)
    @ResponseBody
    ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
    }
​
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }
​
}

 

在前面的示例中,若是由與AcmeController在同一個包中定義的控制器拋出,則使用CustomErrorType POJO 的 JSON 表示而不是ErrorAttributes表示。

自定義錯誤頁面

若是要爲給定狀態 code 顯示自定義 HTML 錯誤頁面,能夠將文件添加到/error文件夾。錯誤頁面能夠是靜態 HTML(即,添加到任何靜態資源文件夾下),也可使用模板構建。文件的 name 應該是確切的狀態 code 或系列掩碼。

例如,要 map 404到靜態 HTML 文件,您的文件夾結構以下:

src/
+- main/
    +- java/
    |   + <source code>
    +- resources/
        +- public/
            +- error/
            |   +- 404.html
            +- <other public assets>

要使用 FreeMarker 模板 map 全部5xx錯誤,您的文件夾結構以下:

src/
+- main/
    +- java/
    |   + <source code>
    +- resources/
        +- templates/
            +- error/
            |   +- 5xx.ftl
            +- <other templates>

對於更復雜的映射,您還能夠添加實現ErrorViewResolver接口的 beans,以下面的示例所示:

public class MyErrorViewResolver implements ErrorViewResolver {

@Override
public ModelAndView resolveErrorView(HttpServletRequest request,
HttpStatus status, Map<String, Object> model) {
// Use the request or status to optionally return a ModelAndView
return ...
}

}

您還可使用常規的 Spring MVC features,例如@ExceptionHandler 方法@ControllerAdviceErrorController而後選擇任何未處理的 exceptions。

Spring MVC 以外的映射錯誤頁面

對於不使用 Spring MVC 的 applications,可使用ErrorPageRegistrar接口直接註冊ErrorPages。這種抽象直接與底層嵌入式 servlet 容器一塊兒工做,即便你沒有 Spring MVC DispatcherServlet也能正常工做。

@Bean
public ErrorPageRegistrar errorPageRegistrar(){
    return new MyErrorPageRegistrar();
}
​
// ...
private static class MyErrorPageRegistrar implements ErrorPageRegistrar {
​
    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
    }
​
}

 

若是你註冊一個ErrorPage的路徑由Filter處理(如 common 與bb 框架,如 Jersey 和 Wicket),那麼Filter必須顯式註冊爲ERROR調度程序,以下所示例:

@Bean
public FilterRegistrationBean myFilter() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new MyFilter());
    ...
    registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
    return registration;
}

 

請注意,默認FilterRegistrationBean不包括ERROR調度程序類型。

CAUTION:When 部署到 servlet 容器,Spring Boot 使用其錯誤頁面過濾器將具備錯誤狀態的請求轉發到相應的錯誤頁面。若是還沒有提交響應,則只能將請求轉發到正確的錯誤頁面。缺省狀況下,WebSphere Application Server 8.0 及更高版本在成功完成 servlet 的服務方法後提交響應。您應該經過將com.ibm.ws.webcontainer.invokeFlushAfterService設置爲false來禁用此行爲。

Spring HATEOAS

若是您開發使用超媒體的 RESTful API,Spring Boot 爲 Spring HATEOAS 提供 auto-configuration,適用於大多數 applications。 auto-configuration 取代了使用@EnableHypermediaSupport的須要,並註冊了許多 beans 以簡化 building hypermedia-based applications,包括LinkDiscoverers(用於 client 側支持)和ObjectMapper配置爲正確地將響應編組到所需的表示中。經過設置各類spring.jackson.* properties 或(若是存在)Jackson2ObjectMapperBuilder bean 來自定義ObjectMapper

您可使用@EnableHypermediaSupport控制 Spring HATEOAS 的 configuration。請注意,這樣作會禁用前面描述的ObjectMapper自定義。

CORS 支持

Cross-origin 資源共享(CORS)是由大多數瀏覽器實現的W3C 規範,它容許您以靈活的方式指定哪一種 cross-domain 請求被受權,而不是使用一些安全性較低且功能較弱的方法,如 IFRAME 或 JSONP。

截至 version 4.2,Spring MVC 支持 CORS。在 Spring Boot application 中使用控制器方法 CORS configuration@CrossOrigin annotations 不須要任何特定的 configuration。能夠經過使用自定義的addCorsMappings(CorsRegistry)方法註冊WebMvcConfigurer bean 來定義Global CORS configuration,以下面的示例所示:

@Configuration
public class MyConfiguration {
​
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**");
            }
        };
    }
}
相關文章
相關標籤/搜索