• SpringBoot对Spring MVC都做了哪些事?


    环境:Springboot2.4.12

    Spring MVC自动配置

    Spring Boot为Spring MVC提供了自动配置,可以很好地与大多数应用程序配合使用。

    自动配置在Spring默认设置的基础上添加了以下功能:

    • 包含 ContentNegotiatingViewResolver 和 BeanNameViewResolver bean 。
    • 支持提供静态资源,包括对WebJars的支持(本文档后面会讲到)。
    • Converter 、 GenericConverter 和 Formatter bean的自动注册。
    • 对 HttpMessageConverters 的支持(本文档后面会讲到)。
    • 自动注册 MessageCodesResolver (本文档后面将介绍)。
    • 静态 index.html 的支持。
    • 自动使用 ConfigurableWebBindingInitializer bean(本文档后面将介绍)。

    如果你想保留那些Spring Boot MVC自定义,并做更多的MVC自定义(拦截器、格式化器、视图控制器和其他特性),你可以添加你自己的WebMvcConfigurer类型的@Configuration类,但不需要@EnableWebMvc。

    如果你想提供RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例,并且仍然保持Spring Boot MVC自定义,你可以声明一个WebMvcRegistrations类型的bean,并使用它来提供这些组件的自定义实例。

    上面这段什么意思?就是我们可以自定义一个Class实现WebMvcRegistrations接口实现自定义的上面的RequestMappingHandlerMapping等相关的类。

    WebMvcRegistrations 接口

    1. public interface WebMvcRegistrations {
    2. default RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
    3. return null;
    4. }
    5. default RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
    6. return null;
    7. }
    8. default ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() {
    9. return null;
    10. }
    11. }

    自动配置中又是如何使用(知道)我们自定义的这个WebMvcRegistrations 类呢?

    1. @Configuration(proxyBeanMethods = false)
    2. @EnableConfigurationProperties(WebProperties.class)
    3. public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    4. // 注入自定义的WebMvcRegistrations
    5. private final WebMvcRegistrations mvcRegistrations;
    6. public EnableWebMvcConfiguration(ObjectProvider<WebMvcRegistrations> mvcRegistrations) {
    7. // ...
    8. this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
    9. }
    10. }

    这里RequestMappingHandlerMapping 为例说明自动配置是如何使用自定义的。接着上面的类中,有如下方法定义。

    1. @Configuration(proxyBeanMethods = false)
    2. @EnableConfigurationProperties(WebProperties.class)
    3. public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
    4. @Bean
    5. @Override
    6. public RequestMappingHandlerAdapter requestMappingHandlerAdapter(...) {
    7. // 调用父类的方法,这里就不进入父类方法了,父类方法中会调用createRequestMappingHandlerAdapter
    8. // 方法,而此方法正好被当前类重写了
    9. RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager, conversionService, validator);
    10. // ...
    11. return adapter;
    12. }
    13. @Override
    14. protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
    15. // 判断是否存在自定义的WebMvcRegistrations接口
    16. if (this.mvcRegistrations != null) {
    17. RequestMappingHandlerAdapter adapter = this.mvcRegistrations.getRequestMappingHandlerAdapter();
    18. if (adapter != null) {
    19. return adapter;
    20. }
    21. }
    22. return super.createRequestMappingHandlerAdapter();
    23. }
    24. }

    以上就是自动配置实现自定义RequestMappingHandlerMapping 等相关WebMVC核心组件的方式。

    如何完全的自己控制WebMVC的配置呢?

    你可以添加自己的@Configuration注释@EnableWebMvc,或者自定义配置类 @Configuration注释且此类是DelegatingWebMvcConfiguration的子类

    如果你想定制Spring MVC使用的ConversionService,你可以提供一个带有addFormatters方法的WebMvcConfigurer bean。通过这个方法,你可以注册任何你喜欢的转换器,或者你可以委托给ApplicationConversionService上可用的静态方法。

    HttpMessageConverters

    Spring MVC使用HttpMessageConverter接口来转换HTTP请求和响应。合理的默认值是开箱即用的。例如,可以将对象自动转换为JSON(通过使用Jackson库)或XML(通过使用Jackson XML扩展(如果可用),或通过使用JAXB(如果Jackson XML扩展不可用)。缺省情况下,字符串是用UTF-8编码的。

    如果你需要添加或自定义转换器,你可以使用Spring Boot的HttpMessageConverters类,如下所示:

    @Configuration(proxyBeanMethods = false)
    public class MyConfiguration {
      @Bean
      public HttpMessageConverters customConverters() {
        HttpMessageConverter additional = ...
        HttpMessageConverter another = ...
        return new HttpMessageConverters(additional, another);
      }
    }

    自动配置又是如何使用咱们自定义的配置?

    • HandlerAdapter设置HttpMessageConverter
    1. public class WebMvcConfigurationSupport {
    2. @Nullable
    3. private List> messageConverters;
    4. public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    5. RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
    6. // HandlerAdapter设置消息转换器
    7. adapter.setMessageConverters(getMessageConverters());
    8. }
    9. protected final List> getMessageConverters() {
    10. // 默认为null
    11. if (this.messageConverters == null) {
    12. this.messageConverters = new ArrayList<>();
    13. // 该方法在子类中重写了,调用子类DelegatingWebMvcConfiguration#configureMessageConverters方法
    14. configureMessageConverters(this.messageConverters);
    15. if (this.messageConverters.isEmpty()) {
    16. // 添加系统默认的消息转换器
    17. addDefaultHttpMessageConverters(this.messageConverters);
    18. }
    19. extendMessageConverters(this.messageConverters);
    20. }
    21. return this.messageConverters;
    22. }
    23. }
    24. @Configuration(proxyBeanMethods = false)
    25. public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
    26. private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
    27. // 获取容器中所有的自定义的WebMvcConfigurer
    28. // 这里会有一个系统提供的配置类WebMvcAutoConfigurationAdapter
    29. @Autowired(required = false)
    30. public void setConfigurers(List configurers) {
    31. if (!CollectionUtils.isEmpty(configurers)) {
    32. this.configurers.addWebMvcConfigurers(configurers);
    33. }
    34. }
    35. protected void configureMessageConverters(List> converters) {
    36. // 分别调用(内部for)WebMvcConfigurer#configureMessageConverters方法
    37. this.configurers.configureMessageConverters(converters);
    38. }
    39. }

    系统提供的WebMvcConfigurer 实现类WebMvcAutoConfigurationAdapter。

    1. public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    2. private final ObjectProvider messageConvertersProvider;
    3. public WebMvcAutoConfigurationAdapter(ObjectProvider messageConvertersProvider) {
    4. // ...
    5. this.messageConvertersProvider = messageConvertersProvider;
    6. // ...
    7. }
    8. public void configureMessageConverters(List> converters) {
    9. // 在上面的WebMvcConfigurerComposite#configureMessageConverters方法中会执行该方法。
    10. // 该方法先获取有效的HttpMessageConverters
    11. // ifAvailable方法接受一个Consumer函数式接口,将HttpMessageConverters#getConverters中的获取到的
    12. // HttpMessageConverter添加到当前的List集合中
    13. this.messageConvertersProvider.ifAvailable((customConverters) -> converters.addAll(customConverters.getConverters()));
    14. }
    15. }

    以上就将自定义的HttpMessageConverter 添加到了容器中。

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

    如果你使用Jackson来序列化和反序列化JSON数据,你可能需要编写自己的JsonSerializer和JsonDeserializer类。自定义序列化器通常通过模块注册到Jackson,但Spring Boot提供了一个替代的@JsonComponent注释,可以更容易地直接注册Spring bean。

    你可以在JsonSerializer、JsonDeserializer或KeyDeserializer实现中直接使用@JsonComponent注释。你也可以在包含序列化器/反序列化器作为内部类的类上使用它,如下所示:

    @JsonComponent
    public class Example {
      public static class Serializer extends JsonSerializer {
        // ...
      }
      public static class Deserializer extends JsonDeserializer {
        // ...
      }
    
    }

    ​ApplicationContext中的所有@JsonComponent bean都会自动向Jackson注册。因为@JsonComponent是用@Component进行元注释的,所以通常的组件扫描规则也适用。

    Spring Boot还提供了JsonObjectSerializer和JsonObjectDeserializer基类,它们在序列化对象时提供了标准Jackson版本的有用替代方案。

     

    静态内容

    默认情况下,Spring Boot从类路径中名为/static(或/public或/resources或/META-INF/resources)的目录或ServletContext的根目录提供静态内容。它使用Spring MVC中的ResourceHttpRequestHandler,因此你可以通过添加自己的WebMVCConfiguer并重写addResourceHandlers方法来修改该行为。

    在一个独立的web应用程序中,容器中的默认servlet也被启用,并作为备用,如果Spring决定不处理它,则从ServletContext的根提供内容。大多数情况下,这不会发生(除非你修改默认的MVC配置),因为Spring始终可以通过DispatcherServlet处理请求。

    默认情况下,资源映射到/**,但你可以通过spring.mvc.static-path-pattern属性进行调整。例如,可以通过以下方式将所有资源重新定位到/resources/**中:

    spring:
      mvc:
        static-path-pattern: "/resources/**"

    你也可以通过spring.web.resources.static-locations属性执行设置静态资源的位置。Servlet的根上下文路径“/”也会自动添加为一个位置。

    除了前面提到的“标准”静态资源位置之外,Webjars内容还有一种特殊情况。任何路径在/webjars/**的资源都是从jar文件中提供的,如果它们被打包成webjars格式的话。

    Spring Boot还支持Spring MVC提供的高级资源处理特性,允许使用缓存破坏静态资源或为webjar使用版本无关url等用例。

    要对webjar使用版本不确定的url,需要添加Webjars-locator-core依赖。然后声明你的Webjar。以jQuery为例,添加“/Webjar/jQuery/jQuery.min.js”,得到“/Webjar/jQuery/x.y.z/jQuery .min.js”,其中x.y.z是Webjar版本。

    欢迎页

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

    路径匹配和内容协商

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

    默认情况下,Spring Boot选择禁用后缀模式匹配,这意味着像“GET /projects/spring-boot”这样的请求。json"不会匹配@GetMapping("/projects/spring-boot")映射。这被认为是Spring MVC应用程序的最佳实践。这个特性主要是在过去的HTTP客户端没有发送正确的“Accept”请求头的时候有用;我们需要确保向客户端发送正确的内容类型。如今,内容协商更加可靠。

    还有其他方法来处理HTTP客户端不一致地发送正确的“Accept”请求头。不使用后缀匹配,我们可以使用查询参数来确保像“GET /projects/spring-boot?”format=json"将被映射到@GetMapping("/projects/spring-boot"):

    示例:

    @GetMapping("/format")
    public Map format() {
      Map result = new HashMap<>() ;
      result.put("name", "张三") ;
      return result ; 
    }

    如上接口,在正常请求的情况下返回:

    再次测试,这次我们将Accept请求Header做下修改如下:

    请求失败了,你客户端能接收的是text/html,但是服务端生成的application/json类型,所以客户端是无法解析处理的。

    对服务端做如下配置:

    spring:
      mvc:
        contentnegotiation:
          favor-parameter: true

    在请求中这次添加一个format参数:

    成功,如果你将format值改成其它又会错误。

    修改参数名:

    spring:
      mvc:
        contentnegotiation:
          favor-parameter: true
          parameter-name: akf

    通过parameter-name将默认的format修改为akf。

    自定义请求header类型

    spring:
      mvc:
        contentnegotiation:
          media-types:
            cnn: app/cnn

    这时候我们的Accept也是可以接收app/cnn的请求类型的。

    ConfigurableWebBindingInitializer

    Spring MVC使用WebBindingInitializer为特定的请求初始化WebDataBinder。如果你创建了自己的ConfigurableWebBindingInitializer @Bean, Spring Boot会自动配置Spring MVC来使用它。

    模版引擎

    除了REST web服务之外,还可以使用Spring MVC来提供动态HTML内容。Spring MVC支持各种模板技术,包括 thymleaf 、 FreeMarker 和 jsp 。此外,许多其他模板引擎也包含它们自己的Spring MVC集成。

    Spring Boot包括对以下模板引擎的自动配置支持:

    • FreeMarker
    • Groovy
    • Thymeleaf
    • Mustache

    如果可能的话,应该避免使用jsp。当将它们与嵌入式servlet容器一起使用时,有几个已知的限制。

    当你使用这些带有默认配置的模板引擎之一时,你的模板将自动从src/main/resources/templates中获取。

  • 相关阅读:
    Linux之多线程
    Access注入---Cookie注入
    html旅游网站设计与实现——绿色古典旅游景区 HTML+CSS+JavaScript
    react多组件出错其他正常显示
    B树和B+树
    python从视频中取图片
    基于51单片机的智能门禁控制系统
    分享一个有意思的线程相关的程序运行题
    基于springboot+vue的食品安全管理系统(源码+论文)
    [Rust GUI]0.10.0版本iced代码示例 - progress_bar
  • 原文地址:https://blog.csdn.net/JavaShark/article/details/125993442