教你理(lǐ)清SpringBoot與SpringMVC的(de)關系

2018-12-28

spring boot就是一(yī)個大框架裏面包含了許許多多的(de)東西,其中spring就是最核心的(de)內(nèi)容之一(yī),當然就包含spring mvc。spring mvc 是隻是spring 處理(lǐ)web層請求的(de)一(yī)個模塊。因此他們的(de)關系大概就是這樣:spring mvc  < spring <springboot。



理(lǐ)清SpringBoot與SpringMVC的(de)關系


Spring 框架就像一(yī)個家族,有衆多衍生産品例如(rú) boot、security、jpa等等。但他們的(de)基礎都是Spring 的(de) ioc和(hé) aop ioc 提供了依賴注入的(de)容器 aop ,解決了面向橫切面的(de)編程,然後在此兩者的(de)基礎上實現了其他延伸産品的(de)高(gāo)級功能。


Spring MVC是基于 Servlet 的(de)一(yī)個 MVC 框架 主要解決 WEB 開發的(de)問題,因為(wèi) Spring 的(de)配置非常複雜,各種XML、 JavaConfig、hin處理(lǐ)起來比較繁瑣。


于是為(wèi)了簡化開發者的(de)使用,從而創造性地(dì)推出了Spring boot,約定優于配置,簡化了spring的(de)配置流程。


說得更簡便一(yī)些:Spring 最初利用“工廠模式”(DI)和(hé)“代理(lǐ)模式”(AOP)解耦應用組件。


大家覺得挺好用,于是按照這種模式搞了一(yī)個 MVC框架(一(yī)些用Spring 解耦的(de)組件),用開發 web 應用( SpringMVC )。


然後有發現每次開發都寫很多樣闆代碼,為(wèi)了簡化工作流程,于是開發出了一(yī)些“懶人整合包”(starter),這套就是 Spring Boot。


Spring MVC的(de)功能Spring MVC提供了一(yī)種輕度耦合的(de)方式來開發web應用。Spring MVC是Spring的(de)一(yī)個模塊,式一(yī)個web框架。


通過Dispatcher Servlet, ModelAndView 和(hé) View Resolver,開發web應用變得很容易。解決的(de)問題領域是網站應用程序或者服務開發——URL路由、Session、模闆引擎、靜态Web資源等等。


Spring Boot的(de)功能Spring Boot實現了自(zì)動配置,降低(dī)了項目搭建的(de)複雜度。


衆所周知Spring框架需要進行大量的(de)配置,Spring Boot引入自(zì)動配置的(de)概念,讓項目設置變得很容易。


Spring Boot本身并不提供Spring框架的(de)核心特性以及擴展功能,隻是用于快速、敏捷地(dì)開發新一(yī)代基于Spring框架的(de)應用程序。也就是說,它并不是用來替代Spring的(de)解決方案,而是和(hé)Spring框架緊密結合用于提升Spring開發者體驗的(de)工具。


同時它集成了大量常用的(de)第三方庫配置(例如(rú)Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot應用中這些第三方庫幾乎可(kě)以零配置的(de)開箱即用(out-of-the-box),大部分的(de)Spring Boot應用都隻需要非常少量的(de)配置代碼,開發者能夠更加專注于業務邏輯。


Spring Boot隻是承載者,輔助你簡化項目搭建過程的(de)。如(rú)果承載的(de)是WEB項目,使用Spring MVC作為(wèi)MVC框架,那麽工作流程和(hé)你上面描述的(de)是完全一(yī)樣的(de),因為(wèi)這部分工作是Spring MVC做(zuò)的(de)而不是Spring Boot。對使用者來說,換用Spring Boot以後,項目初始化方法變了,配置文件變了,另外就是不需要單獨安裝Tomcat這類容器服務器了,maven打出jar包直接跑起來就是個網站,但你最核心的(de)業務邏輯實現與業務流程實現沒有任何變化。


所以,用最簡練的(de)語言概括就是:


Spring 是一(yī)個“引擎”;


Spring MVC 是基于Spring的(de)一(yī)個 MVC 框架 ;


Spring Boot 是基于Spring4的(de)條件注冊的(de)一(yī)套快速開發整合包。



Spring MVC自(zì)動配置


Spring Boot為(wèi)Spring MVC提供的(de)auto-configuration适用于大多數應用,并在Spring默認功能上添加了以下特性:


1.引入

ContentNegotiatingViewResolver和(hé)BeanNameViewResolver beans。


2.對靜态資源的(de)支持,包括對WebJars的(de)支持。


3.自(zì)動注冊Converter,GenericConverter,Formatter beans。


4.對HttpMessageConverters的(de)支持。


5.自(zì)動注冊

MessageCodeResolver。


6.對靜态index.html的(de)支持。


7.對自(zì)定義Favicon的(de)支持。


8.自(zì)動使用

ConfigurableWebBindingInitializer bean。


如(rú)果保留Spring Boot MVC特性,你隻需添加其他的(de)MVC配置(攔截器,格式化處理(lǐ)器,視(shì)圖控制器等)。


你可(kě)以添加自(zì)己的(de)WebMvcConfigurerAdapter類型的(de)@Configuration類,而不需要注解@EnableWebMvc。如(rú)果希望使用自(zì)定義的(de)RequestMappingHandlerMapping,RequestMappingHandlerAdapter,或ExceptionHandlerExceptionResolver,你可(kě)以聲明一(yī)個WebMvcRegistrationsAdapter實例提供這些組件。


如(rú)果想全面控制Spring MVC,你可(kě)以添加自(zì)己的(de)@Configuration,并使用@EnableWebMvc注解。


HttpMessageConverters


Spring MVC使用HttpMessageConverter接口轉換HTTP請求和(hé)響應,合适的(de)默認配置可(kě)以開箱即用,例如(rú)對象自(zì)動轉換為(wèi)JSON(使用Jackson庫)或XML(如(rú)果Jackson XML擴展可(kě)用,否則使用JAXB),字符串默認使用UTF-8編碼。


可(kě)以使用Spring Boot的(de)HttpMessageConverters類添加或自(zì)定義轉換類:


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);
   }
}


上下文中出現的(de)所有HttpMessageConverter bean都将添加到converters列表,你可(kě)以通過這種方式覆蓋默認的(de)轉換器列表(converters)。



自(zì)定義JSON序列化器和(hé)反序列化器


如(rú)果使用Jackson序列化,反序列化JSON數據,你可(kě)能想編寫自(zì)己的(de)JsonSerializer和(hé)JsonDeserializer類。自(zì)定義序列化器(serializers)通常通過Module注冊到Jackson,但Spring Boot提供了@JsonComponent注解這一(yī)替代方式,它能輕松的(de)将序列化器注冊為(wèi)Spring Beans。


MessageCodesResolver


Spring MVC有一(yī)個實現策略,用于從綁定的(de)errors産生用來渲染錯誤信息的(de)錯誤碼:MessageCodesResolver。Spring Boot會自(zì)動為(wèi)你創建該實現,隻要設置spring.mvc.message-codes-resolver.format屬性為(wèi)PREFIX_ERROR_CODE或POSTFIX_ERROR_CODE(具體查看DefaultMessageCodesResolver.Format枚舉值)。


靜态內(nèi)容


默認情況下,Spring Boot從classpath下的(de)/static(/public,/resources或/META-INF/resources)文件夾,或從ServletContext根目錄提供靜态內(nèi)容。這是通過Spring MVC的(de)ResourceHttpRequestHandler實現的(de),你可(kě)以自(zì)定義WebMvcConfigurerAdapter并覆寫addResourceHandlers方法來改變該行為(wèi)(加載靜态文件)。


在單機web應用中,容器會啓動默認的(de)servlet,并用它加載ServletContext根目錄下的(de)內(nèi)容以響應那些Spring不處理(lǐ)的(de)請求。大多數情況下這都不會發生(除非你修改默認的(de)MVC配置),因為(wèi)Spring總能夠通過DispatcherServlet處理(lǐ)這些請求。


你可(kě)以設置spring.resources.staticLocations屬性自(zì)定義靜态資源的(de)位置(配置一(yī)系列目錄位置代替默認的(de)值),如(rú)果你這樣做(zuò),默認的(de)歡迎頁面将從自(zì)定義位置加載,所以隻要這些路徑中的(de)任何地(dì)方有一(yī)個index.html,它都會成為(wèi)應用的(de)主頁。


此外,除了上述标準的(de)靜态資源位置,有個例外情況是Webjars內(nèi)容。任何在/webjars/**路徑下的(de)資源都将從jar文件中提供,隻要它們以Webjars的(de)格式打包。


注 如(rú)果你的(de)應用将被打包成jar,那就不要使用src/main/webapp文件夾。盡管該文件夾是通常的(de)标準格式,但它僅在打包成war的(de)情況下起作用,在打包成jar時,多數構建工具都會默認忽略它。


Spring Boot也支持Spring MVC提供的(de)高(gāo)級資源處理(lǐ)特性,可(kě)用于清除緩存的(de)靜态資源或對WebJar使用版本無感知的(de)URLs。


如(rú)果想使用針對WebJars版本無感知的(de)URLs(version agnostic),隻需要添加webjars-locator依賴,然後聲明你的(de)Webjar。以jQuery為(wèi)例,"/webjars/jquery/dist/jquery.min.js"實際為(wèi)"/webjars/jquery/x.y.z/dist/jquery.min.js",x.y.z為(wèi)Webjar的(de)版本。


注 如(rú)果使用JBoss,你需要聲明webjars-locator-jboss-vfs依賴而不是webjars-locator,否則所有的(de)Webjars将解析為(wèi)404。


以下的(de)配置為(wèi)所有的(de)靜态資源提供一(yī)種緩存清除(cache busting)方案,實際上是将內(nèi)容hash添加到URLs中,比如(rú)<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>:


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


注 實現該功能的(de)是ResourceUrlEncodingFilter,它在模闆運行期會重寫資源鏈接,Thymeleaf,Velocity和(hé)FreeMarker會自(zì)動配置該filter,JSP需要手動配置。其他模闆引擎還沒自(zì)動支持,不過你可(kě)以使用ResourceUrlProvider自(zì)定義模塊宏或幫助類。


當使用比如(rú)JavaScript模塊加載器動态加載資源時,重命名文件是不行的(de),這也是提供其他策略并能結合使用的(de)原因。下面是一(yī)個"fixed"策略,在URL中添加一(yī)個靜态version字符串而不需要改變文件名:


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



使用以上策略,JavaScript模塊加載器加載"/js/lib/"下的(de)文件時會使用一(yī)個固定的(de)版本策略"/v12/js/lib/mymodule.js",其他資源仍舊(jiù)使用內(nèi)容hash的(de)方式<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>。查看ResourceProperties獲取更多支持的(de)選項。


歡迎頁面


Spring Boot支持靜态和(hé)模闆歡迎頁面。它首先index.html在配置的(de)靜态內(nèi)容位置中查找 文件。如(rú)果找不到,則會查找index模闆。如(rú)果找到任何一(yī)個,它将自(zì)動用作應用程序的(de)歡迎頁面。、


自(zì)定義Favicon


Spring Boot favicon.ico在配置的(de)靜态內(nèi)容位置和(hé)類路徑的(de)根目錄(按此順序)中查找a 。如(rú)果存在這樣的(de)文件,它會自(zì)動用作應用程序的(de)圖标。


路徑匹配和(hé)內(nèi)容協商


Spring MVC可(kě)以通過查看請求路徑并将它匹配到應用程序中定義的(de)映射(例如(rú)@GetMapping Controller方法上的(de)注釋),将傳入的(de)HTTP請求映射到處理(lǐ)程序。


Spring Boot選擇默認禁用後綴模式匹配,這意味着請求"GET /projects/spring-boot.json"不會匹配 @GetMapping("/projects/spring-boot")映射。這被認為(wèi)是Spring MVC應用程序的(de) 最佳實踐。此功能在過去(qù)對于沒有發送正确的(de)“Accept”請求标頭的(de)HTTP客戶端來說非常有用; 我們需要确保将正确的(de)內(nèi)容類型發送到客戶端。如(rú)今,內(nèi)容協商更可(kě)靠。


還有其他一(yī)些方法可(kě)以處理(lǐ)不一(yī)緻地(dì)發送适當的(de)“接受”請求标頭的(de)HTTP客戶端。我們可(kě)以使用查詢參數來确保類似的(de)請求"GET /projects/spring-boot?format=json" 将映射到@GetMapping("/projects/spring-boot")以下內(nèi)容,而不是使用後綴匹配:


spring.mvc.contentnegotiation.favor-parameter = true
#我們可(kě)以更改參數名稱,默認為(wèi)“格式”:
#spring.mvc.contentnegotiation.parameter-name = myparam
#我們還可(kě)以通過以下方式注冊其他文件擴展名/媒體類型:
spring.mvc.contentnegotiation.media-types.markdown = text / markdown


如(rú)果您了解注意事項并仍然希望應用程序使用後綴模式匹配,則需要進行以下配置:


spring.mvc.contentnegotiation.favor-path-extension = true
#您也可(kě)以将該功能限制為(wèi)已知擴展
#spring.mvc.pathmatch.use-registered-suffix-pattern = true
#我們還可(kě)以通過以下方式注冊其他文件擴展名/媒體類型:
#spring.mvc.contentnegotiation.media-types.adoc = text / asciidoc


ConfigurableWebBindingInitializer


Spring MVC使用WebBindingInitializer為(wèi)每個特殊的(de)請求初始化相應的(de)WebDataBinder,如(rú)果你創建自(zì)己的(de)ConfigurableWebBindingInitializer @Bean,Spring Boot會自(zì)動配置Spring MVC使用它。


模闆引擎


正如(rú)REST web服務,你也可(kě)以使用Spring MVC提供動态HTML內(nèi)容。Spring MVC支持各種各樣的(de)模闆技術,包括Velocity, FreeMarker和(hé)JSPs,很多其他的(de)模闆引擎也提供它們自(zì)己的(de)Spring MVC集成。


Spring Boot為(wèi)以下的(de)模闆引擎提供自(zì)動配置支持:


FreeMarker

Groovy


Thymeleaf


Velocity(1.4已不再支持)


Mustache


注:由于在內(nèi)嵌servlet容器中使用JSPs存在一(yī)些已知的(de)限制,所以建議盡量不使用它們。


使用以上引擎中的(de)任何一(yī)種,并采用默認配置,則模塊會從src/main/resources/templates自(zì)動加載。


注:IntelliJ IDEA根據你運行應用的(de)方式會對classpath進行不同的(de)排序。在IDE裏通過main方法運行應用,跟從Maven,或Gradle,或打包好的(de)jar中運行相比會導緻不同的(de)順序,這可(kě)能導緻Spring Boot不能從classpath下成功地(dì)找到模闆。


如(rú)果遇到這個問題,你可(kě)以在IDE裏重新對classpath進行排序,将模塊的(de)類和(hé)資源放到第一(yī)位。或者,你可(kě)以配置模塊的(de)前綴為(wèi)classpath*:/templates/,這樣會查找classpath下的(de)所有模闆目錄。


錯誤處理(lǐ)


Spring Boot默認提供一(yī)個/error映射用來以合适的(de)方式處理(lǐ)所有的(de)錯誤,并将它注冊為(wèi)servlet容器中全局的(de) 錯誤頁面。對于機器客戶端(相對于浏覽器而言,浏覽器偏重于人的(de)行為(wèi)),它會産生一(yī)個具有詳細錯誤,HTTP狀态,異常信息的(de)JSON響應。


對于浏覽器客戶端,它會産生一(yī)個白色标簽樣式(whitelabel)的(de)錯誤視(shì)圖,該視(shì)圖将以HTML格式顯示同樣的(de)數據(可(kě)以添加一(yī)個解析為(wèi)'error'的(de)View來自(zì)定義它)。為(wèi)了完全替換默認的(de)行為(wèi),你可(kě)以實現ErrorController,并注冊一(yī)個該類型的(de)bean定義,或簡單地(dì)添加一(yī)個ErrorAttributes類型的(de)bean以使用現存的(de)機制,隻是替換顯示的(de)內(nèi)容。

注BasicErrorController可(kě)以作為(wèi)自(zì)定義ErrorController的(de)基類,如(rú)果你想添加對新context type的(de)處理(lǐ)(默認處理(lǐ)text/html),這會很有幫助。


你隻需要繼承BasicErrorController,添加一(yī)個public方法,并注解帶有produces屬性的(de)@RequestMapping,然後創建該新類型的(de)bean。

你也可(kě)以定義一(yī)個@ControllerAdvice去(qù)自(zì)定義某個特殊controller或exception類型的(de)JSON文檔:


@ControllerAdvice(basePackageClasses = FooController.class)
public class FooControllerAdvice 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);
   }
}


在以上示例中,如(rú)果跟FooController相同package的(de)某個controller抛出YourException,一(yī)個CustomerErrorType類型的(de)POJO的(de)json展示将代替ErrorAttributes展示。


自(zì)定義錯誤頁面


如(rú)果想為(wèi)某個給定的(de)狀态碼展示一(yī)個自(zì)定義的(de)HTML錯誤頁面,你需要将文件添加到/error文件夾下。錯誤頁面既可(kě)以是靜态HTML(比如(rú),任何靜态資源文件夾下添加的(de)),也可(kě)以是使用模闆構建的(de),文件名必須是明确的(de)狀态碼或一(yī)系列标簽。


例如(rú),映射404到一(yī)個靜态HTML文件,你的(de)目錄結構可(kě)能如(rú)下:


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

使用FreeMarker模闆映射所有5xx錯誤,你需要如(rú)下的(de)目錄結構:


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


對于更複雜的(de)映射,你可(kě)以添加實現ErrorViewResolver接口的(de)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 ...
   }
}


你也可(kě)以使用Spring MVC特性,比如(rú)@ExceptionHandler方法和(hé)@ControllerAdvice,ErrorController将處理(lǐ)所有未處理(lǐ)的(de)異常。


映射Spring MVC以外的(de)錯誤頁面


對于不使用Spring MVC的(de)應用,你可(kě)以通過ErrorPageRegistrar接口直接注冊ErrorPages。該抽象直接工作于底層內(nèi)嵌servlet容器,即使你沒有Spring MVC的(de)DispatcherServlet,它們仍舊(jiù)可(kě)以工作。


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 ...
   }
}
@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"));
   }
}


注.如(rú)果你注冊一(yī)個ErrorPage,該頁面需要被一(yī)個Filter處理(lǐ)(在一(yī)些非Spring web框架中很常見,比如(rú)Jersey,Wicket),那麽該Filter需要明确注冊為(wèi)一(yī)個ERROR分發器(dispatcher),例如(rú):


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

(默認的(de)FilterRegistrationBean不包含ERROR dispatcher類型)。


WebSphere應用服務器的(de)錯誤處理(lǐ)


當部署到一(yī)個servlet容器時,Spring Boot通過它的(de)錯誤頁面過濾器将帶有錯誤狀态的(de)請求轉發到恰當的(de)錯誤頁面。request隻有在response還沒提交時才能轉發(forwarded)到正确的(de)錯誤頁面,而WebSphere應用服務器8.0及後續版本默認情況會在servlet方法成功執行後提交response,你需要設置com.ibm.ws.webcontainer.invokeFlushAfterService屬性為(wèi)false來關閉該行為(wèi)。



Spring HATEOAS


如(rú)果正在開發基于超媒體的(de)RESTful API,你可(kě)能需要Spring HATEOAS,而Spring Boot會為(wèi)其提供自(zì)動配置,這在大多數應用中都運作良好。 自(zì)動配置取代了@EnableHypermediaSupport,隻需注冊一(yī)定數量的(de)beans就能輕松構建基于超媒體的(de)應用,這些beans包括LinkDiscoverers(客戶端支持),ObjectMapper(用于将響應編排為(wèi)想要的(de)形式)。ObjectMapper可(kě)以根據spring.jackson.*屬性或Jackson2ObjectMapperBuilder bean進行自(zì)定義。


通過注解@EnableHypermediaSupport,你可(kě)以控制Spring HATEOAS的(de)配置,但這會禁用上述ObjectMapper的(de)自(zì)定義功能。



CORS支持


跨域資源共享(CORS)是一(yī)個大多數浏覽器都實現了的(de)W3C标準,它允許你以靈活的(de)方式指定跨域請求如(rú)何被授權,而不是采用那些不安全,性能低(dī)的(de)方式,比如(rú)IFRAME或JSONP。


從4.2版本開始,Spring MVC對CORS提供開箱即用的(de)支持。不用添加任何特殊配置,隻需要在Spring Boot應用的(de)controller方法上注解@CrossOrigin,并添加CORS配置。通過注冊一(yī)個自(zì)定義addCorsMappings(CorsRegistry)方法的(de)WebMvcConfigurer bean可(kě)以指定全局CORS配置:


@Configuration
public class MyConfiguration {
   @Bean
   public WebMvcConfigurer corsConfigurer() {
       return new WebMvcConfigurerAdapter() {
           @Override
           public void addCorsMappings(CorsRegistry registry) {
               registry.addMapping("/api/**");
           }
       };
   }
}


您的(de)項目需求咨詢熱線:0760-88610046(國(guó)家高(gāo)新技術企業)

*請認真填寫需求,我們會在24小時內(nèi)與您取得聯系。