• springboot下MVC的MessageConverters和静态资源位置的配置


    1. WebMvcConfigurationSupport、WebMvcConfigurer 区别

           springboot中我们通过继承WebMvcConfigurerAdapter进行springmvc相关的配置,如拦截器、消息转换、视图解析器等。在springboot2.0后,该接口被废弃,官方推荐直接implements WebMvcConfigurer ,或者extends WebMvcConfigurationSupport

    查看源码发现@EnableWebMvc实际上引入了一个继承WebMvcConfigurationSupport的DelegatingWebMvcConfiguration。

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import(DelegatingWebMvcConfiguration.class)
    public @interface EnableWebMvc {
    }
    
    @Configuration
    public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport
    

    然后查看WebMvcAutoConfiguration的源码发现:

    @Configuration
    @ConditionalOnWebApplication
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class,WebMvcConfigurerAdapter.class })
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration {
    ...
    }
    

           @ConditionalOnMissingBean({WebMvcConfigurationSupport.class})意思是如果存在它修饰的类的bean,则不需要再创建这个bean。由此可得出结论:如果有bean继承了DelegatingWebMvcConfiguration,WebMvcConfigurationSupport,或者开启了@EnableWebMvc,那么 @EnableAutoConfiguration 中的WebMvcAutoConfiguration 将不会被自动配置,而是使用自定义的WebMvcConfigurationSupport的配置。

    总结
    推荐前两种方式

    • implements WebMvcConfigurer : 不会覆盖@EnableAutoConfiguration关于WebMvcAutoConfiguration的配置。

    • extends WebMvcConfigurationSupport :会覆盖@EnableAutoConfiguration关于WebMvcAutoConfiguration的配置。注意:使用WebMvcConfigurationSupport类配置拦截器时一定要重写addResourceHandlers来实现静态资源的映射

    • @EnableWebMvc : 等于扩展了WebMvcConfigurationSupport但是没有重写任何方法。

    • extends WebMvcConfigurerAdapter spring2.0不再建议使用,被废弃。 官方推荐直接实现WebMvcConfigurer 。

    2. 自定义消息转换器MessageConverters

           以下配置代码用于使用LocalDate,LocalTime ,LocalDateTime属性接收前端传来的标准时间字符串,并返回指定的时间格式,LocalDate对应 yyyy-MM-dd,LocalDateTime对应yyyy-MM-dd HH:mm:dd,LocalTime对应 HH:mm:dd,不论参数的形式传递如body、pathVariable、requestParam里,只要时间字符串满足格式要求都可以转为对应的时间日期属性。

    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.SerializationFeature;
    import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
    import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
    import com.jun.cloud.common.util.DateUtil;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.convert.converter.Converter;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.StringHttpMessageConverter;
    import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
    import org.springframework.util.ObjectUtils;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import java.nio.charset.Charset;
    import java.time.LocalDate;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    import java.time.format.DateTimeFormatter;
    import java.util.ArrayList;
    import java.util.List;
    
    
    /**
     * WebMvc配置:
     * 1.消息转换器的配置,提供Jackson的支持
     */
    @Configuration
    public class WebMvcConfig implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //对外提供的api接口验证及追踪日志
           //registry.addInterceptor(new RestApiInterceptor()).addPathPatterns("/api/**");
        }
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            //添加对swagger-ui的处理
            registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
            //registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/static/");
            registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
    
        /**
         * 注意这里使用的是extendMessageConverters,这个方法不会覆盖springmvc已默认添加的HttpMessageConverter
         */
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add(customerMappingJackson2HttpMessageConverter());
            converters.add(stringHttpMessageConverter());
        }
    
        @Bean
        public MappingJackson2HttpMessageConverter customerMappingJackson2HttpMessageConverter(){
            MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
            messageConverter.setObjectMapper(customDateObjectMapper());
            return messageConverter;
        }
    
        @Bean
        public StringHttpMessageConverter stringHttpMessageConverter(){
            StringHttpMessageConverter stringConvert = new StringHttpMessageConverter();
            List<MediaType> stringMediaTypes = new ArrayList<MediaType>(){{
                add(new MediaType("text","plain",Charset.forName("UTF-8")));
            }};
            stringConvert.setSupportedMediaTypes(stringMediaTypes);
            return stringConvert;
        }
    
        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addConverter(localDateTimeConverter());
            registry.addConverter(localDateConverter());
            registry.addConverter(localTimeConverter());
        }
    
        /**
         * Json序列化和反序列化转换器,用于转换Post请求体中的json以及将我们的对象序列化为返回响应的json
         */
        @Bean
        public ObjectMapper customDateObjectMapper(){
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
            objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    
            //LocalDateTime系列序列化和反序列化模块,继承自jsr310,这里修改了日期格式
            JavaTimeModule javaTimeModule = new JavaTimeModule();
            javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_yyyy_MM_dd_HH_mm_ss)));
            javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_yyyy_MM_dd)));
            javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_HH_mm_ss)));
            javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_yyyy_MM_dd_HH_mm_ss)));
            javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_yyyy_MM_dd)));
            javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_HH_mm_ss)));
    
            objectMapper.registerModule(javaTimeModule);
            return objectMapper;
        }
    
    
        /**
         * LocalDate转换器,用于转换RequestParam和PathVariable参数
         */
        private Converter<String, LocalDate> localDateConverter() {
            return new Converter<String, LocalDate>() {
                @Override
                public LocalDate convert(String source) {
                    if(!ObjectUtils.isEmpty(source)){
                        return LocalDate.parse(source, DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_yyyy_MM_dd));
                    }
                    return null ;
    
                }
            };
        }
    
        /**
         * LocalDateTime转换器,用于转换RequestParam和PathVariable参数
         */
        private Converter<String, LocalDateTime> localDateTimeConverter() {
            return new Converter<String, LocalDateTime>() {
                @Override
                public LocalDateTime convert(String source) {
                    if(!ObjectUtils.isEmpty(source)) {
                        return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_yyyy_MM_dd_HH_mm_ss));
                    }
                    return null ;
                }
            };
        }
    
        /**
         * LocalTime转换器,用于转换RequestParam和PathVariable参数
         */
        private Converter<String, LocalTime> localTimeConverter() {
            return new Converter<String, LocalTime>() {
                @Override
                public LocalTime convert(String source) {
                    if(!ObjectUtils.isEmpty(source)) {
                        return LocalTime.parse(source, DateTimeFormatter.ofPattern(DateUtil.DATE_FORMAT_HH_mm_ss));
                    }
                    return null;
                }
            };
        }
    
    }
    

    注意implements WebMvcConfigurer后,要使用extendMessageConverters方法,这里通过@Bean的方式注入,也可以直接new一个如:

    	@Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
            messageConverter.setObjectMapper(customDateObjectMapper());
            converters.add(messageConverter);
            converters.add(stringHttpMessageConverter());
        }
    

    3. 静态资源

    3.1 静态资源位置

           默认情况下,Spring Boot从classpath下的/static(/public,/resources或/META-INF/resources)文件夹,或从ServletContext根目录提供静态内容。这是通过Spring MVC的ResourceHttpRequestHandler实现的,你可以自定义WebMvcConfigurerAdapter并覆写addResourceHandlers方法来改变该行为(加载静态文件),即上面的

     	@Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
          
            registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
            //添加对swagger-ui的处理
            registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
            registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    
            super.addResourceHandlers(registry);
        }
    

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

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

    3.2 错误页面

           如果想为某个给定的状态码展示一个自定义的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将处理所有未处理的异常。

  • 相关阅读:
    洛谷 P2766 最长不下降子序列问【dp+最大流】
    洛谷 P3254 圆桌问题【最大流】
    洛谷 P2764 最小路径覆盖问题【匈牙利算法】
    洛谷 P2763 试题库问题【最大流】
    洛谷 P2762 太空飞行计划问题 【最大权闭合子图+最小割】
    洛谷 P2761 软件补丁问题 【spfa】
    洛谷 P2754 星际转移问题【最大流】
    洛谷 P1251 餐巾计划问题【最小费用最大流】
    spoj 371 Boxes【最小费用最大流】
    poj 3680 Intervals【最小费用最大流】
  • 原文地址:https://www.cnblogs.com/seasail/p/12179367.html
Copyright © 2020-2023  润新知