51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 500|回复: 0
打印 上一主题 下一主题

[原创] 浅谈SpringBoot对SpringMVC的自动配置

[复制链接]
  • TA的每日心情
    无聊
    昨天 09:05
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2023-3-27 13:18:32 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    Spring MVC自动配置
      Spring Boot为Spring MVC提供了自动配置,可以在大多数应用程序中很好地工作。
      自动配置在Spring默认的基础上添加了以下特性:
      ·包含ContentNegotiatingViewResolver和BeanNameViewResolver bean。
      · 支持提供静态资源,包括对WebJars的支持。
      · 自动注册Converter、GenericConverter和Formatter
      · 对HttpMessageConverters的支持。
      · MessageCodesResolver的自动注册。
      · 静态index.html支持。
      · 自动使用ConfigurableWebBindingInitializer bean。
      如果你想保留这些Spring Boot MVC自定义并进行更多的MVC自定义(拦截器、格式化器、视图控制器和其他特性),你可以自定义配置类实现WebMvcConfigurer类型的@Configuration类,但不需要@EnableWebMvc。
      如果你想提供RequestMappingHandlerMapping、RequestMappingHandlerAdapter或ExceptionHandlerExceptionResolver的自定义实例,并且仍然保持Spring Boot MVC自定义,你可以声明一个WebMvcRegistrations类型的bean,并使用它来提供这些组件的自定义实例,源码如下:
      public interface WebMvcRegistrations {
        default RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
          return null;
        }
        default RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
          return null;
        }
        default ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() {
          return null;
        }
      }


      EnableWebMvcConfiguration
      public static class EnableWebMvcConfiguration {
        private final WebMvcRegistrations mvcRegistrations;
        public EnableWebMvcConfiguration(ObjectProvider<WebMvcRegistrations> mvcRegistrationsProvider) {
          this.mvcRegistrations = mvcRegistrationsProvider.getIfUnique();
        }
        @Override
        protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {
          // 如果自定义了WebMvcRegistrations,则使用自定义的,其他的默认配置行为不变
          if (this.mvcRegistrations != null) {
            RequestMappingHandlerAdapter adapter = this.mvcRegistrations.getRequestMappingHandlerAdapter();
            if (adapter != null) {
              return adapter;
            }
          }
          return super.createRequestMappingHandlerAdapter();
        }
      }


      如果你想要完全控制Spring MVC,你可以添加你自己的@Configuration注解@EnableWebMvc,或者自定义配置类该类继承DelegatingWebMvcConfiguration并使用@Configuration注解。
      消息转换HttpMessageConverters
      SpringMVC使用HttpMessageConverter接口来转换HTTP请求和响应。明智的默认值是开箱即用的。例如,对象可以自动转换为JSON(通过使用Jackson库)或XML(通过使用JacksonXML扩展(如果可用),或者通过使用JAXB(如果Jackson XML扩展不可用))。默认情况下,字符串以UTF-8编码。
      如果需要添加或自定义转换器,可以使用Spring Boot的HttpMessageConverters类,如以下列表所示:
      @Configuration(proxyBeanMethods = false)
      public class CustomHttpMessageConvertersConfiguration {
        @Bean
        public HttpMessageConverters customConverters() {
          HttpMessageConverter<?> additional = new AdditionalHttpMessageConverter();
          HttpMessageConverter<?> another = new AnotherHttpMessageConverter();
          return new HttpMessageConverters(additional, another);
        }
      }


      系统默认也提供了HttpMessageConverters Bean,其实我们可以直接将自定义的HttpMessageConverter注册为Bean即可:
      public class HttpMessageConvertersAutoConfiguration {
        @Bean
        @ConditionalOnMissingBean
        // 这里的参数就能够收集所有HttpMessageConverter类型的Bean
        public HttpMessageConverters messageConverters(ObjectProvider<HttpMessageConverter<?>> converters) {
          return new HttpMessageConverters(converters.orderedStream().collect(Collectors.toList()));
        }  
      }


      自定义JSON序列化和反序列化
      如果你使用Jackson来序列化和反序列化JSON数据,你可能需要编写自己的JsonSerializer和JsonDeserializer类。自定义序列化程序通常通过一个模块向Jackson注册,但Spring Boot提供了一个替代的@JsonComponent注释,可以更容易地直接注册Spring Beans。
      你可以在JsonSerializer、JsonDeserializer或KeyDeserialize实现上直接使用@JsonComponent注释。您也可以在包含序列化程序/反序列化程序作为内部类的类上使用它,如以下示例所示:
      @JsonComponent
      public class MyJsonComponent {
        public static class Serializer extends JsonSerializer<MyObject> {
          @Override
          public void serialize(MyObject value, JsonGenerator jgen, SerializerProvider serializers) throws IOException {
            jgen.writeStartObject();
            jgen.writeStringField("name", value.getName());
            jgen.writeNumberField("age", value.getAge());
            jgen.writeEndObject();
          }
        }
        public static class Deserializer extends JsonDeserializer<MyObject> {
          @Override
          public MyObject deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            ObjectCodec codec = jsonParser.getCodec();
            JsonNode tree = codec.readTree(jsonParser);
            String name = tree.get("name").textValue();
            int age = tree.get("age").intValue();
            return new MyObject(name, age);
          }
        }
      }


      ApplicationContext中的所有@JsonComponent bean都会自动注册到Jackson中。因为@JsonComponent是用@Component进行元注释的,所以应用了通常的组件扫描规则。
      MessageCodesResolver
      Spring MVC有一个生成错误代码的策略,用于从绑定错误中呈现错误消息:MessageCodesResolver。如果你设置
      spring.mvc.message-codes-resolver-format属性PREFIX_ERROR_CODE或POSTFIX_ERROR_CODE,则spring Boot会为你创建一个属性。
      静态内容
      默认情况下,Spring Boot从类路径中名为/static(或/public或/resources或/META-INF/resources)的目录或ServletContext的根目录提供静态内容。它使用Spring MVC中的
      ResourceHttpRequestHandler,因此您可以通过添加自己的WebMvcConfigurer并重写addResourceHandlers方法来修改该行为。
      在独立的[url=]web[/url]应用程序中,不会启用容器中的默认servlet。可以使用
      [url=]server[/url].servlet.register-default-servlet属性启用它。
      默认的servlet充当后备,如果Spring决定不处理ServletContext的根,则从ServletContext提供内容。大多数情况下,这不会发生(除非您修改默认的MVC配置),因为Spring总是可以通过DispatcherServlet处理请求。
      默认情况下,资源映射在/**上,但您可以使用
      spring.mvc.static-path-pattern属性对其进行调优。例如,将所有资源重新定位到/resources/**可以通过以下方式实现:
      spring:
        mvc:
          static-path-pattern: "/resources/**"


      你还可以使用spring.web.resources自定义静态资源位置。static-locations属性(用目录位置列表替换默认值)。根servlet上下文路径“/”也会自动添加为位置。
      除了前面提到的“标准”静态资源位置之外,还有一个针对Webjars内容的特殊情况。任何路径在/webjars/**中的资源,如果它们被打包成webjars格式,就会从jar文件中提供。
      Spring Boot还支持Spring MVC提供的高级资源处理功能,允许使用缓存破坏静态资源或为Webjars使用版本不可知的URL等用例。
      要为Webjars使用与版本无关的URL,请添加Webjars定位器核心依赖项。然后声明您的Webjar。以jQuery为例,添加“
      /webjars/jQuery/jQuery.min.js”会导致“/Webjar/jQuery/x.y.z/jQuery.min.js”,其中x.y.z是Webjar版本。
      要使用缓存破坏,以下配置为所有静态资源配置缓存破坏解决方案,从而有效地在URL中添加内容哈希,如<link href=“
      /css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css”/>:?
      spring:
        web:
          resources:
            chain:
              strategy:
                content:
                  enabled: true
                  paths: "/**"


      路径匹配与内容协商
      Spring MVC可以通过查看请求路径并将其与应用程序中定义的映射(例如,Controller方法上的@GetMapping注释)进行匹配,将传入的HTTP请求映射到处理程序。
      Spring Boot默认选择禁用后缀模式匹配,这意味着像"GET/projects/Spring Boot.json"这样的请求将不会与@GetMapping("/projects/Spring Boot")映射匹配。这被认为是Spring MVC应用程序的最佳实践。这个功能在过去主要适用于没有发送正确的“Accept”请求头的HTTP客户端;我们需要确保向客户端发送正确的内容类型。如今,内容协商更加可靠。
      还有其他方法可以处理那些不一致地发送正确的"Accept"请求头的HTTP客户端。我们可以使用查询参数来确保像"GET/projects/spring-boot?format=json"这样的请求将被映射到@GetMapping("/projects/spring-boot"),而不是使用后缀匹配:
      spring:
        mvc:
          contentnegotiation:
            favor-parameter: true


      或者自定义参数名称:
      spring:
        mvc:
          contentnegotiation:
            favor-parameter: true
            parameter-name: "myparam"


      大多数标准media type都支持开箱即用,但你也可以定义新的:
      spring:
        mvc:
          contentnegotiation:
            media-types:
              markdown: "text/markdown"


      后缀模式匹配已弃用,并将在将来的版本中删除。如果你理解注意事项,并且仍然希望你的应用程序使用后缀模式匹配,则需要以下配置:
      spring:
        mvc:
          contentnegotiation:
            favor-path-extension: true
          pathmatch:
            use-suffix-pattern: true


      ConfigurableWebBindingInitializer
      Spring MVC使用WebBindingInitializer为特定请求初始化WebDataBinder。如果你创建自己的ConfigurationWebBindingInitializer@Bean,则Spring Boot会自动配置Spring MVC以使用它。
      错误处理
      默认情况下,Spring Boot提供了一个/error映射,以合理的方式处理所有错误,并且它在servlet容器中注册为“全局”错误页面。对于机器客户端,它会生成一个JSON响应,其中包含错误、HTTP状态和异常消息的详细信息。对于浏览器客户端,有一个“白标签”错误视图,它以HTML格式呈现相同的数据(要自定义它,请添加一个解决错误的视图)。
      服务器有很多。如果要自定义默认错误处理行为,可以设置的错误属性。
      要完全替换默认行为,可以实现ErrorController并注册该类型的bean定义,或者添加ErrorAttributes类型的bean以使用现有机制,但替换内容。
      你也可以定义一个带有@ControllerAdvice注解的类来定制JSON文档,以返回特定的控制器和/或异常类型,如下例所示:
      @ControllerAdvice(basePackageClasses = SomeController.class)
      public class MyControllerAdvice extends ResponseEntityExceptionHandler {
        @ResponseBody
        @ExceptionHandler(MyException.class)
        public ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
          HttpStatus status = getStatus(request);
          return new ResponseEntity<>(new MyErrorBody(status.value(), ex.getMessage()), status);
        }
        private HttpStatus getStatus(HttpServletRequest request) {
          Integer code = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
          HttpStatus status = HttpStatus.resolve(code);
          return (status != null) ? status : HttpStatus.INTERNAL_SERVER_ERROR;
        }
      }


      完毕!!!

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-22 14:49 , Processed in 0.077098 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表