zoukankan      html  css  js  c++  java
  • 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将处理所有未处理的异常。

  • 相关阅读:
    Educational Codeforces Round 85 D. Minimum Euler Cycle(模拟/数学/图)
    Educational Codeforces Round 85 C. Circle of Monsters(贪心)
    NOIP 2017 提高组 DAY1 T1小凯的疑惑(二元一次不定方程)
    Educational Codeforces Round 85 B. Middle Class(排序/贪心/水题)
    Educational Codeforces Round 85 A. Level Statistics(水题)
    IOS中的三大事件
    用Quartz 2D画小黄人
    strong、weak、copy、assign 在命名属性时候怎么用
    用代码生成UINavigationController 与UITabBarController相结合的简单QQ框架(部分)
    Attempting to badge the application icon but haven't received permission from the user to badge the application错误解决办法
  • 原文地址:https://www.cnblogs.com/seasail/p/12179367.html
Copyright © 2011-2022 走看看