教你理清SpringBoot與SpringMVC的關系

2018-12-28

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



理清SpringBoot與SpringMVC的關系


Spring 框架就像一個家族,有衆多衍生産品例如 boot、security、jpa等等。但他們的基礎都(dōu)是Spring 的 ioc和 aop ioc 提供了依賴注入的容器 aop ,解決了面(miàn)向(xiàng)橫切面(miàn)的編程,然後(hòu)在此兩(liǎng)者的基礎上實現了其他延伸産品的高級功能(néng)。


Spring MVC是基于 Servlet 的一個 MVC 框架 主要解決 WEB 開(kāi)發(fā)的問題,因爲 Spring 的配置非常複雜,各種(zhǒng)XML、 JavaConfig、hin處理起(qǐ)來比較繁瑣。


于是爲了簡化開(kāi)發(fā)者的使用,從而創造性地推出了Spring boot,約定優于配置,簡化了spring的配置流程。


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


大家覺得挺好(hǎo)用,于是按照這(zhè)種(zhǒng)模式搞了一個 MVC框架(一些用Spring 解耦的組件),用開(kāi)發(fā) web 應用( SpringMVC )。


然後(hòu)有發(fā)現每次開(kāi)發(fā)都(dōu)寫很多樣闆代碼,爲了簡化工作流程,于是開(kāi)發(fā)出了一些“懶人整合包”(starter),這(zhè)套就是 Spring Boot。


Spring MVC的功能(néng)Spring MVC提供了一種(zhǒng)輕度耦合的方式來開(kāi)發(fā)web應用。Spring MVC是Spring的一個模塊,式一個web框架。


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


Spring Boot的功能(néng)Spring Boot實現了自動配置,降低了項目搭建的複雜度。


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


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


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


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


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


Spring 是一個“引擎”;


Spring MVC 是基于Spring的一個 MVC 框架 ;


Spring Boot 是基于Spring4的條件注冊的一套快速開(kāi)發(fā)整合包。



Spring MVC自動配置


Spring Boot爲Spring MVC提供的auto-configuration适用于大多數應用,并在Spring默認功能(néng)上添加了以下特性:


1.引入

ContentNegotiatingViewResolver和BeanNameViewResolver beans。


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


3.自動注冊Converter,GenericConverter,Formatter beans。


4.對(duì)HttpMessageConverters的支持。


5.自動注冊

MessageCodeResolver。


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


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


8.自動使用

ConfigurableWebBindingInitializer bean。


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


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


如果想全面(miàn)控制Spring MVC,你可以添加自己的@Configuration,并使用@EnableWebMvc注解。


HttpMessageConverters


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


可以使用Spring Boot的HttpMessageConverters類添加或自定義轉換類:


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


上下文中出現的所有HttpMessageConverter bean都(dōu)將(jiāng)添加到converters列表,你可以通過(guò)這(zhè)種(zhǒng)方式覆蓋默認的轉換器列表(converters)。



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


如果使用Jackson序列化,反序列化JSON數據,你可能(néng)想編寫自己的JsonSerializer和JsonDeserializer類。自定義序列化器(serializers)通常通過(guò)Module注冊到Jackson,但Spring Boot提供了@JsonComponent注解這(zhè)一替代方式,它能(néng)輕松的將(jiāng)序列化器注冊爲Spring Beans。


MessageCodesResolver


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


靜态内容


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


在單機web應用中,容器會啓動默認的servlet,并用它加載ServletContext根目錄下的内容以響應那些Spring不處理的請求。大多數情況下這(zhè)都(dōu)不會發(fā)生(除非你修改默認的MVC配置),因爲Spring總能(néng)夠通過(guò)DispatcherServlet處理這(zhè)些請求。


你可以設置spring.resources.staticLocations屬性自定義靜态資源的位置(配置一系列目錄位置代替默認的值),如果你這(zhè)樣做,默認的歡迎頁面(miàn)將(jiāng)從自定義位置加載,所以隻要這(zhè)些路徑中的任何地方有一個index.html,它都(dōu)會成(chéng)爲應用的主頁。


此外,除了上述标準的靜态資源位置,有個例外情況是Webjars内容。任何在/webjars/**路徑下的資源都(dōu)將(jiāng)從jar文件中提供,隻要它們以Webjars的格式打包。


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


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


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


注 如果使用JBoss,你需要聲明webjars-locator-jboss-vfs依賴而不是webjars-locator,否則所有的Webjars將(jiāng)解析爲404。


以下的配置爲所有的靜态資源提供一種(zhǒng)緩存清除(cache busting)方案,實際上是將(jiāng)内容hash添加到URLs中,比如<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>:


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


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


當使用比如JavaScript模塊加載器動态加載資源時,重命名文件是不行的,這(zhè)也是提供其他策略并能(néng)結合使用的原因。下面(miàn)是一個"fixed"策略,在URL中添加一個靜态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/"下的文件時會使用一個固定的版本策略"/v12/js/lib/mymodule.js",其他資源仍舊使用内容hash的方式<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>。查看ResourceProperties獲取更多支持的選項。


歡迎頁面(miàn)


Spring Boot支持靜态和模闆歡迎頁面(miàn)。它首先index.html在配置的靜态内容位置中查找 文件。如果找不到,則會查找index模闆。如果找到任何一個,它將(jiāng)自動用作應用程序的歡迎頁面(miàn)。、


自定義Favicon


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


路徑匹配和内容協商


Spring MVC可以通過(guò)查看請求路徑并將(jiāng)它匹配到應用程序中定義的映射(例如@GetMapping Controller方法上的注釋),將(jiāng)傳入的HTTP請求映射到處理程序。


Spring Boot選擇默認禁用後(hòu)綴模式匹配,這(zhè)意味着請求"GET /projects/spring-boot.json"不會匹配 @GetMapping("/projects/spring-boot")映射。這(zhè)被認爲是Spring MVC應用程序的 最佳實踐。此功能(néng)在過(guò)去對(duì)于沒(méi)有發(fā)送正确的“Accept”請求标頭的HTTP客戶端來說非常有用; 我們需要确保將(jiāng)正确的内容類型發(fā)送到客戶端。如今,内容協商更可靠。


還(hái)有其他一些方法可以處理不一緻地發(fā)送适當的“接受”請求标頭的HTTP客戶端。我們可以使用查詢參數來确保類似的請求"GET /projects/spring-boot?format=json" 將(jiāng)映射到@GetMapping("/projects/spring-boot")以下内容,而不是使用後(hòu)綴匹配:


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


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


spring.mvc.contentnegotiation.favor-path-extension = true
#您也可以將(jiāng)該功能(néng)限制爲已知擴展
#spring.mvc.pathmatch.use-registered-suffix-pattern = true
#我們還(hái)可以通過(guò)以下方式注冊其他文件擴展名/媒體類型:
#spring.mvc.contentnegotiation.media-types.adoc = text / asciidoc


ConfigurableWebBindingInitializer


Spring MVC使用WebBindingInitializer爲每個特殊的請求初始化相應的WebDataBinder,如果你創建自己的ConfigurableWebBindingInitializer @Bean,Spring Boot會自動配置Spring MVC使用它。


模闆引擎


正如REST web服務,你也可以使用Spring MVC提供動态HTML内容。Spring MVC支持各種(zhǒng)各樣的模闆技術,包括Velocity, FreeMarker和JSPs,很多其他的模闆引擎也提供它們自己的Spring MVC集成(chéng)。


Spring Boot爲以下的模闆引擎提供自動配置支持:


FreeMarker

Groovy


Thymeleaf


Velocity(1.4已不再支持)


Mustache


注:由于在内嵌servlet容器中使用JSPs存在一些已知的限制,所以建議盡量不使用它們。


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


注:IntelliJ IDEA根據你運行應用的方式會對(duì)classpath進(jìn)行不同的排序。在IDE裡(lǐ)通過(guò)main方法運行應用,跟從Maven,或Gradle,或打包好(hǎo)的jar中運行相比會導緻不同的順序,這(zhè)可能(néng)導緻Spring Boot不能(néng)從classpath下成(chéng)功地找到模闆。


如果遇到這(zhè)個問題,你可以在IDE裡(lǐ)重新對(duì)classpath進(jìn)行排序,將(jiāng)模塊的類和資源放到第一位。或者,你可以配置模塊的前綴爲classpath*:/templates/,這(zhè)樣會查找classpath下的所有模闆目錄。


錯誤處理


Spring Boot默認提供一個/error映射用來以合适的方式處理所有的錯誤,并將(jiāng)它注冊爲servlet容器中全局的 錯誤頁面(miàn)。對(duì)于機器客戶端(相對(duì)于浏覽器而言,浏覽器偏重于人的行爲),它會産生一個具有詳細錯誤,HTTP狀态,異常信息的JSON響應。


對(duì)于浏覽器客戶端,它會産生一個白色标簽樣式(whitelabel)的錯誤視圖,該視圖將(jiāng)以HTML格式顯示同樣的數據(可以添加一個解析爲'error'的View來自定義它)。爲了完全替換默認的行爲,你可以實現ErrorController,并注冊一個該類型的bean定義,或簡單地添加一個ErrorAttributes類型的bean以使用現存的機制,隻是替換顯示的内容。

注BasicErrorController可以作爲自定義ErrorController的基類,如果你想添加對(duì)新context type的處理(默認處理text/html),這(zhè)會很有幫助。


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

你也可以定義一個@ControllerAdvice去自定義某個特殊controller或exception類型的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);
   }
}


在以上示例中,如果跟FooController相同package的某個controller抛出YourException,一個CustomerErrorType類型的POJO的json展示將(jiāng)代替ErrorAttributes展示。


自定義錯誤頁面(miàn)


如果想爲某個給定的狀态碼展示一個自定義的HTML錯誤頁面(miàn),你需要將(jiāng)文件添加到/error文件夾下。錯誤頁面(miàn)既可以是靜态HTML(比如,任何靜态資源文件夾下添加的),也可以是使用模闆構建的,文件名必須是明确的狀态碼或一系列标簽。


例如,映射404到一個靜态HTML文件,你的目錄結構可能(néng)如下:


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

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


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


對(duì)于更複雜的映射,你可以添加實現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特性,比如@ExceptionHandler方法和@ControllerAdvice,ErrorController將(jiāng)處理所有未處理的異常。


映射Spring MVC以外的錯誤頁面(miàn)


對(duì)于不使用Spring MVC的應用,你可以通過(guò)ErrorPageRegistrar接口直接注冊ErrorPages。該抽象直接工作于底層内嵌servlet容器,即使你沒(méi)有Spring MVC的DispatcherServlet,它們仍舊可以工作。


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


注.如果你注冊一個ErrorPage,該頁面(miàn)需要被一個Filter處理(在一些非Spring web框架中很常見,比如Jersey,Wicket),那麼(me)該Filter需要明确注冊爲一個ERROR分發(fā)器(dispatcher),例如:


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

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


WebSphere應用服務器的錯誤處理


當部署到一個servlet容器時,Spring Boot通過(guò)它的錯誤頁面(miàn)過(guò)濾器將(jiāng)帶有錯誤狀态的請求轉發(fā)到恰當的錯誤頁面(miàn)。request隻有在response還(hái)沒(méi)提交時才能(néng)轉發(fā)(forwarded)到正确的錯誤頁面(miàn),而WebSphere應用服務器8.0及後(hòu)續版本默認情況會在servlet方法成(chéng)功執行後(hòu)提交response,你需要設置com.ibm.ws.webcontainer.invokeFlushAfterService屬性爲false來關閉該行爲。



Spring HATEOAS


如果正在開(kāi)發(fā)基于超媒體的RESTful API,你可能(néng)需要Spring HATEOAS,而Spring Boot會爲其提供自動配置,這(zhè)在大多數應用中都(dōu)運作良好(hǎo)。 自動配置取代了@EnableHypermediaSupport,隻需注冊一定數量的beans就能(néng)輕松構建基于超媒體的應用,這(zhè)些beans包括LinkDiscoverers(客戶端支持),ObjectMapper(用于將(jiāng)響應編排爲想要的形式)。ObjectMapper可以根據spring.jackson.*屬性或Jackson2ObjectMapperBuilder bean進(jìn)行自定義。


通過(guò)注解@EnableHypermediaSupport,你可以控制Spring HATEOAS的配置,但這(zhè)會禁用上述ObjectMapper的自定義功能(néng)。



CORS支持


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


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


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


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

*請認真填寫需求,我們會在24小時内與您取得聯系。