教你理清SpringBoot与SpringMVC的关系

原创
2018/11/21 21:17
阅读数 168

spring boot就是一个大框架里面包含了许许多多的东西,其中spring就是最核心的内容之一,当然就包含spring mvc。spring mvc 是只是spring 处理web层请求的一个模块。因此他们的关系大概就是这样:spring mvc  < spring <springboot。

理清SpringBoot与SpringMVC的关系

Spring 框架就像一个家族,有众多衍生产品例如 boot、security、jpa等等。但他们的基础都是Spring 的 ioc和 aop ioc 提供了依赖注入的容器 aop ,解决了面向横切面的编程,然后在此两者的基础上实现了其他延伸产品的高级功能。

Spring MVC是基于 Servlet 的一个 MVC 框架 主要解决 WEB 开发的问题,因为 Spring 的配置非常复杂,各种XML、 JavaConfig、hin处理起来比较繁琐。

于是为了简化开发者的使用,从而创造性地推出了Spring boot,约定优于配置,简化了spring的配置流程。

说得更简便一些:Spring 最初利用“工厂模式”(DI)和“代理模式”(AOP)解耦应用组件。

大家觉得挺好用,于是按照这种模式搞了一个 MVC框架(一些用Spring 解耦的组件),用开发 web 应用( SpringMVC )。

然后有发现每次开发都写很多样板代码,为了简化工作流程,于是开发出了一些“懒人整合包”(starter),这套就是 Spring Boot。

Spring MVC的功能Spring MVC提供了一种轻度耦合的方式来开发web应用。Spring MVC是Spring的一个模块,式一个web框架。

通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。解决的问题领域是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。

Spring Boot的功能Spring Boot实现了自动配置,降低了项目搭建的复杂度。

众所周知Spring框架需要进行大量的配置,Spring Boot引入自动配置的概念,让项目设置变得很容易。

Spring Boot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,而是和Spring框架紧密结合用于提升Spring开发者体验的工具。

同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。Spring Boot只是承载者,辅助你简化项目搭建过程的。如果承载的是WEB项目,使用Spring MVC作为MVC框架,那么工作流程和你上面描述的是完全一样的,因为这部分工作是Spring MVC做的而不是Spring Boot。对使用者来说,换用Spring Boot以后,项目初始化方法变了,配置文件变了,另外就是不需要单独安装Tomcat这类容器服务器了,maven打出jar包直接跑起来就是个网站,但你最核心的业务逻辑实现与业务流程实现没有任何变化。

所以,用最简练的语言概括就是:

Spring 是一个“引擎”;

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

Spring Boot 是基于Spring4的条件注册的一套快速开发整合包。

Spring MVC自动配置

Spring Boot为Spring MVC提供的auto-configuration适用于大多数应用,并在Spring默认功能上添加了以下特性:

 引入ContentNegotiatingViewResolver和BeanNameViewResolver beans。

 对静态资源的支持,包括对WebJars的支持。

 自动注册Converter,GenericConverter,Formatter beans。

 对HttpMessageConverters的支持。

 自动注册MessageCodeResolver。

 对静态index.html的支持。

 对自定义Favicon的支持。

 自动使用ConfigurableWebBindingInitializer bean。

如果保留Spring Boot MVC特性,你只需添加其他的MVC配置(拦截器,格式化处理器,视图控制器等)。你可以添加自己的WebMvcConfigurerAdapter类型的@Configuration类,而不需要注解@EnableWebMvc。如果希望使用自定义的RequestMappingHandlerMapping,RequestMappingHandlerAdapter,或ExceptionHandlerExceptionResolver,你可以声明一个WebMvcRegistrationsAdapter实例提供这些组件。

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

HttpMessageConverters

Spring MVC使用HttpMessageConverter接口转换HTTP请求和响应,合适的默认配置可以开箱即用,例如对象自动转换为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都将添加到converters列表,你可以通过这种方式覆盖默认的转换器列表(converters)。

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

如果使用Jackson序列化,反序列化JSON数据,你可能想编写自己的JsonSerializer和JsonDeserializer类。自定义序列化器(serializers)通常通过Module注册到Jackson,但Spring Boot提供了@JsonComponent注解这一替代方式,它能轻松的将序列化器注册为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根目录提供静态内容。这是通过Spring MVC的ResourceHttpRequestHandler实现的,你可以自定义WebMvcConfigurerAdapter并覆写addResourceHandlers方法来改变该行为(加载静态文件)。

在单机web应用中,容器会启动默认的servlet,并用它加载ServletContext根目录下的内容以响应那些Spring不处理的请求。大多数情况下这都不会发生(除非你修改默认的MVC配置),因为Spring总能够通过DispatcherServlet处理这些请求。

你可以设置spring.resources.staticLocations属性自定义静态资源的位置(配置一系列目录位置代替默认的值),如果你这样做,默认的欢迎页面将从自定义位置加载,所以只要这些路径中的任何地方有一个index.html,它都会成为应用的主页。

此外,除了上述标准的静态资源位置,有个例外情况是Webjars内容。任何在/webjars/**路径下的资源都将从jar文件中提供,只要它们以Webjars的格式打包。

注 如果你的应用将被打包成jar,那就不要使用src/main/webapp文件夹。尽管该文件夹是通常的标准格式,但它仅在打包成war的情况下起作用,在打包成jar时,多数构建工具都会默认忽略它。

Spring Boot也支持Spring MVC提供的高级资源处理特性,可用于清除缓存的静态资源或对WebJar使用版本无感知的URLs。

如果想使用针对WebJars版本无感知的URLs(version agnostic),只需要添加webjars-locator依赖,然后声明你的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将解析为404。

以下的配置为所有的静态资源提供一种缓存清除(cache busting)方案,实际上是将内容hash添加到URLs中,比如<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>:

spring.resources.chain.strategy.content.enabled=true  

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

注 实现该功能的是ResourceUrlEncodingFilter,它在模板运行期会重写资源链接,Thymeleaf,Velocity和FreeMarker会自动配置该filter,JSP需要手动配置。其他模板引擎还没自动支持,不过你可以使用ResourceUrlProvider自定义模块宏或帮助类。

当使用比如JavaScript模块加载器动态加载资源时,重命名文件是不行的,这也是提供其他策略并能结合使用的原因。下面是一个"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获取更多支持的选项。

欢迎页面

Spring Boot支持静态和模板欢迎页面。它首先index.html在配置的静态内容位置中查找 文件。如果找不到,则会查找index模板。如果找到任何一个,它将自动用作应用程序的欢迎页面。、

自定义Favicon

Spring Boot favicon.ico在配置的静态内容位置和类路径的根目录(按此顺序)中查找a 。如果存在这样的文件,它会自动用作应用程序的图标。

路径匹配和内容协商

Spring MVC可以通过查看请求路径并将它匹配到应用程序中定义的映射(例如@GetMapping Controller方法上的注释),将传入的HTTP请求映射到处理程序。

Spring Boot选择默认禁用后缀模式匹配,这意味着请求"GET /projects/spring-boot.json"不会匹配 @GetMapping("/projects/spring-boot")映射。这被认为是Spring MVC应用程序的 最佳实践。此功能在过去对于没有发送正确的“Accept”请求标头的HTTP客户端来说非常有用; 我们需要确保将正确的内容类型发送到客户端。如今,内容协商更可靠。

还有其他一些方法可以处理不一致地发送适当的“接受”请求标头的HTTP客户端。我们可以使用查询参数来确保类似的请求"GET /projects/spring-boot?format=json" 将映射到@GetMapping("/projects/spring-boot")以下内容,而不是使用后缀匹配:

spring.mvc.contentnegotiation.favor-parameter = true  

#我们可以更改参数名称,默认为“格式”:  

#spring.mvc.contentnegotiation.parameter-name = myparam  

#我们还可以通过以下方式注册其他文件扩展名/媒体类型:  

spring.mvc.contentnegotiation.media-types.markdown = text / markdown 

如果您了解注意事项并仍然希望应用程序使用后缀模式匹配,则需要进行以下配置:

spring.mvc.contentnegotiation.favor-path-extension = true  

#您也可以将该功能限制为已知扩展  

#spring.mvc.pathmatch.use-registered-suffix-pattern = true  

#我们还可以通过以下方式注册其他文件扩展名/媒体类型:  

#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支持各种各样的模板技术,包括Velocity, FreeMarker和JSPs,很多其他的模板引擎也提供它们自己的Spring MVC集成。

Spring Boot为以下的模板引擎提供自动配置支持:

 FreeMarker

 Groovy

 Thymeleaf

 Velocity(1.4已不再支持)

 Mustache

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

使用以上引擎中的任何一种,并采用默认配置,则模块会从src/main/resources/templates自动加载。

注:IntelliJ IDEA根据你运行应用的方式会对classpath进行不同的排序。在IDE里通过main方法运行应用,跟从Maven,或Gradle,或打包好的jar中运行相比会导致不同的顺序,这可能导致Spring Boot不能从classpath下成功地找到模板。如果遇到这个问题,你可以在IDE里重新对classpath进行排序,将模块的类和资源放到第一位。或者,你可以配置模块的前缀为classpath*:/templates/,这样会查找classpath下的所有模板目录。

错误处理

Spring Boot默认提供一个/error映射用来以合适的方式处理所有的错误,并将它注册为servlet容器中全局的 错误页面。对于机器客户端(相对于浏览器而言,浏览器偏重于人的行为),它会产生一个具有详细错误,HTTP状态,异常信息的JSON响应。对于浏览器客户端,它会产生一个白色标签样式(whitelabel)的错误视图,该视图将以HTML格式显示同样的数据(可以添加一个解析为'error'的View来自定义它)。为了完全替换默认的行为,你可以实现ErrorController,并注册一个该类型的bean定义,或简单地添加一个ErrorAttributes类型的bean以使用现存的机制,只是替换显示的内容。

注BasicErrorController可以作为自定义ErrorController的基类,如果你想添加对新context type的处理(默认处理text/html),这会很有帮助。你只需要继承BasicErrorController,添加一个public方法,并注解带有produces属性的@RequestMapping,然后创建该新类型的bean。

你也可以定义一个@ControllerAdvice去自定义某个特殊controller或exception类型的JSON文档:

@ControllerAdvice(basePackageClasses = FooController.class)  

public class FooControllerAdvice extends ResponseEntityExceptionHandler {  

    @ExceptionHandler(YourException.class)  

    @ResponseBody  

ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {  

HttpStatusstatus = getStatus(request);  

return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);  

    }  

    private HttpStatus getStatus(HttpServletRequest request) {  

IntegerstatusCode = (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展示将代替ErrorAttributes展示。

自定义错误页面

如果想为某个给定的状态码展示一个自定义的HTML错误页面,你需要将文件添加到/error文件夹下。错误页面既可以是静态HTML(比如,任何静态资源文件夹下添加的),也可以是使用模板构建的,文件名必须是明确的状态码或一系列标签。

例如,映射404到一个静态HTML文件,你的目录结构可能如下:

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> 

对于更复杂的映射,你可以添加实现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将处理所有未处理的异常。

映射Spring MVC以外的错误页面

对于不使用Spring MVC的应用,你可以通过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处理(在一些非Spring web框架中很常见,比如Jersey,Wicket),那么该Filter需要明确注册为一个ERROR分发器(dispatcher),例如:

@Bean  

public FilterRegistrationBean myFilter() {  

FilterRegistrationBeanregistration = new FilterRegistrationBean();  

    registration.setFilter(new MyFilter());  

    ...  

    registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));  

    return registration;  

(默认的FilterRegistrationBean不包含ERROR dispatcher类型)。

WebSphere应用服务器的错误处理

当部署到一个servlet容器时,Spring Boot通过它的错误页面过滤器将带有错误状态的请求转发到恰当的错误页面。request只有在response还没提交时才能转发(forwarded)到正确的错误页面,而WebSphere应用服务器8.0及后续版本默认情况会在servlet方法成功执行后提交response,你需要设置com.ibm.ws.webcontainer.invokeFlushAfterService属性为false来关闭该行为。

Spring HATEOAS

如果正在开发基于超媒体的RESTful API,你可能需要Spring HATEOAS,而Spring Boot会为其提供自动配置,这在大多数应用中都运作良好。 自动配置取代了@EnableHypermediaSupport,只需注册一定数量的beans就能轻松构建基于超媒体的应用,这些beans包括LinkDiscoverers(客户端支持),ObjectMapper(用于将响应编排为想要的形式)。ObjectMapper可以根据spring.jackson.*属性或Jackson2ObjectMapperBuilder bean进行自定义。

通过注解@EnableHypermediaSupport,你可以控制Spring HATEOAS的配置,但这会禁用上述ObjectMapper的自定义功能。

CORS支持

跨域资源共享(CORS)是一个大多数浏览器都实现了的W3C标准,它允许你以灵活的方式指定跨域请求如何被授权,而不是采用那些不安全,性能低的方式,比如IFRAME或JSONP。

从4.2版本开始,Spring MVC对CORS提供开箱即用的支持。不用添加任何特殊配置,只需要在Spring Boot应用的controller方法上注解@CrossOrigin,并添加CORS配置。通过注册一个自定义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/**");  

            }  

        };  

    }  

推荐一个交流学习群:697-57-9751 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多:

 

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
9 收藏
0
分享
返回顶部
顶部