WebMvcConfigurer和WebMvcConfigurationSupport(MVC配置)

一:基本介绍

WebMvcConfigurer是接口,用于配置全局的SpringMVC的相关属性,采用JAVABean的方式来代替传统的XML配置文件,提供了跨域设置、静态资源处理器、类型转化器、自定义拦截器、页面跳转等能力。

WebMvcConfigurationSupport是webmvc的配置类,如果在springboot项目中,有配置类继承了WebMvcConfigurationSupport,那么webmvc的自动配置类WebMvcAutoConfiguration就会失效。

两者都是Spring MVC中的组件,具体的区别如下:

1.实现方式不同

WebMvcConfigurer:是一个接口,它提供了多个回调方法,可以用于自定义Spring MVC的配置(如消息转换器、拦截器等)。我们在使用时只需要实现该接口,重写其中的方法即可。

WebMvcConfigurationSupport:是一个抽象类,它也提供了多个回调方法,用于自定义Spring MVC的配置,但是需要继承该类并重写其中的方法。

2.作用不同

WebMvcConfigurer:

主要用于添加或修改Spring MVC的配置,如添加拦截器,自定义消息转换器等。

WebMvcConfigurationSupport:

主要用于完全自定义Spring MVC的配置,如果我们需要对Spring MVC的配置进行大量的自定义,可以选择继承该类并重写其中的方法。但是需要注意的是,继承该类会覆盖Spring MVC的部分默认配置。因此,当我们只需要对部分配置进行自定义时,应该使用WebMvcConfigurer。

3.继承关系不同

WebMvcConfigurer:

没有继承关系,我们只需要实现该接口即可使用。

WebMvcConfigurationSupport:

是一个抽象类,需要继承后才能使用。

4.总结

在日常开发中推荐优先使用WebMvcConfigurer的方式(官方推荐),因为简单方便,也没有特别复杂的定制需求;若我们项目中使用的MVC存在着更加复杂的配置需求推荐WebMvcConfigurationSupport,通过继承此类,我们可以说对官方的MVC代码进行重写操作,但是因为其配置量较大,实现比较复杂,因此在日常开发中使用WebMvcConfigurationSupport并不常见。

二:配置摘要

常用自定义配置如下:

1.addInterceptors(拦截器配置)

2.addCorsMappings(全局跨域处理)

3.addViewControllers(注册视图控制器)(可以让地址对应资源文件,如html文件)

4.addResourceHandlers(配置静态资源处理)(可以在客户端直接访问静态资源信息)

三:Spring和SpringBoot中配置步骤区别

1.Spring中配置WebMvcConfigurer方式:

①创建一个Java 类,实现WebMvcConfigurer接口

复制代码
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
    // 自定义配置代码
}

②注入到Bean容器里

复制代码
@Configuration
public class AppConfig {
    @Bean
    public MyWebMvcConfig myWebMvcConfig() {
        return new MyWebMvcConfig();
    }
}
2.SpringBoot中配置WebMvcConfigurer方式:(这种方式简单)

创建一个 Java 类,并实现WebMvcConfigurer接口

复制代码
@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {
    // 自定义配置代码
}

四:拦截器(addInterceptors)

在SpringBoot中,可以使用拦截器来对请求进行统一的预处理或后处理。拦截器用于日志记录、权限检查、性能监控、事务控制等方面。SpringBoot中实现拦截器,首先要创建一个实现HandlerInterceptor接口的拦截器类。该接口定义了三个方法,分别是preHandle、postHandle和afterCompletion,用于在请求处理前、请求处理后和请求完成后进行处理。

1.HandlerInterceptor接口方法详解:

①preHandler

在请求处理之前被调用。该方法在Interceptor类中最先执行,用来进行一些前置初始化操作或是对当前请求做预处理,也可以进行一些判断来决定请求是否要继续进行下去。该方法的返回值是Boolean类型,当它返回false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当它返回为true时会继续调用下一个Interceptor的preHandle方法,如果已经是最后一个Interceptor的时候就会调用当前请求的Controller方法。

②postHandler

在请求处理完成之后调用。也就是Controller方法调用之后执行,但是它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。

③afterCompletion

在整个请求结束后调用。就是对应的Interceptor类的postHandler方法返回true时才执行。就是说该方法将在整个请求结束之后,

也就是在DispatcherServlet渲染了对应的视图之后执行。此方法主要用来进行资源清理。

注:官方其实不建议我们非要把3个方法都重写,我们只要对需要的方法重写接口,就比如大部分项目只需要重写preHandler方法

2.InterceptorRegistry类方法介绍:

①addInterceptor

该方法用于向拦截器链中添加一个拦截器。interceptor参数为待添加的拦截器对象,可以是自定义的拦截器类或Spring提供的预置拦截器。返回值为InterceptorRegistration对象,用于进一步配置该拦截器的属性。

②addWebRequestInterceptor

该方法用于向WebRequest拦截器链中添加一个拦截器。interceptor参数为待添加的拦截器对象,可以是自定义的WebRequestInterceptor类或者Spring提供的预置拦截器。也是返回值为InterceptorRegistration对象,用于进一步配置该拦截器的属性。

③getInterceptors

用于获取当前已经添加到拦截器链中的所有拦截器,返回值为List<HandlerInterceptor>对象,表示拦截器列表。

3.InterceptorRegistration类方法介绍:

①order

该方法用于设置拦截器的执行顺序,即在拦截器链中的位置。order参数为一个整数,值越小表示越先执行。

②addPathPatterns

该方法用于设置需要拦截的请求路径模式,即满足哪些请求路径时才会触发该拦截器。若"/**"则拦截全部;传入的参数是一个字符串数组,包含多个Ant风格的路径模式,例如"/api/**""/user/*"等。

③excludePathPatterns

该方法用于设置不需要拦截的请求路径模式,即满足哪些请求路径时不会触发该拦截器。一般不拦截,如登录或者Swagger等传入的参数是一个字符串数组,包含多个Ant风格的路径模式,例如 "/api/login""/user/login"等。

④pathMatcher

该方法用于设置该拦截器所使用的PathMatcher实例,从而可以自定义路径匹配逻辑。

注:拦截的路径或者放行的路径是以Controller开始的,如我们在application.yml配置的地址前缀则不包含

五:跨域(addCorsMappings)

参考:SpringBoot解决跨域的三种方式

1.CorsRegistry类方法介绍:

①addMapping

该方法用于添加允许跨域访问的路径,String类型,若存在多个路径则需要在CorsRegistry里配置多个

2.CorsRegistration类方法介绍:

CorsRegistration是CorsRegistry的辅助类,使用它可以对单个跨域请求进行更细粒度的配置。

①allowedOrigins(低版本使用,但是现在高版本也支持)

设置允许跨域请求的来源URL。该方法接受多个参数,每个参数为一个允许的来源URL。或者设置"*"

②allowedOriginPatterns(一般使用这种方式)

设置允许跨域请求的来源URL的模式。该方法接受多个参数,每个参数为一个允许的来源URL模式。或者设置"*"

③allowCredentials

设置是否允许跨域请求携带凭证信息。默认情况下,浏览器不会向跨域请求发送Cookie等凭证信息。如果希望携带凭证信息,则需要将allowCredentials方法设置为true。

④allowedMethods

设置允许跨域请求的HTTP方法。该方法接受多个参数,每个参数为一种允许的HTTP请求方式。

⑤allowedHeaders

设置允许请求携带的HTTP头信息。该方法接受多个参数,每个参数为一种允许的HTTP头信息。(放行哪些请求头部信息)

⑥exposedHeaders

设置响应头信息,这些信息允许客户端访问。该方法接受多个参数,每个参数为一种允许的响应头信息。(暴露哪些响应头信息)

⑦combine

将当前CorsRegistration对象与另一个CorsConfiguration对象合并,返回合并后的CorsConfiguration对象。可以使用该方法将多个CorsRegistration对象的配置合并到同一个CorsConfiguration对象中。

⑧maxAge

设置响应的缓存时间,单位为秒,默认30分钟。

例如,当设置maxAge为3600时,如果浏览器在一小时内再次向同一个目标URL发送跨域请求,就可以直接使用以前的预检请求结果,而不需要再次进行预检请求。maxAge属性只影响预检请求的缓存时间,而不会影响正常的跨域请求,因此不会对实际的业务逻辑产生影响。此外,maxAge属性的具体值需要根据实际情况进行调整,过小的缓存时间可能会导致频繁的预检请求,过大的缓存时间可能会使跨域请求的控制权得不到及时更新,从而增加安全风险。

六:注册页面跳转(addViewControllers)

addViewControllers方法是SpringMVC中WebMvcConfigurer接口定义的一个方法,用于注册一个简单的视图控制器,以便将请求路径映射到一个具体的视图页面。在一些简单的场景下,我们可能只需要将某个请求路径直接映射到一个固定的视图页面,而不需要进行额外的逻辑处理。此时,可以使用addViewControllers方法来快速注册一个视图控制器,并指定对应的请求路径和视图名称即可。但是我们需要额外注意的是,我们在SpringBoot中处理视图跳转时最好集成Thymeleaf,因为它是SpringBoot指定认可的并且,Thymeleaf与SpringMVC协同工作,可以方便地实现快速开发Web应用程序。

1.配置

在SpringBoot配置Thymeleaf的一些信息:

导入Thymeleaf坐标:

复制代码
<!--导入SpringBoot集成Thymeleaf启动器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

在application.yml配置Thymeleaf配置信息:

复制代码
    spring:
       # 配置Thymeleaf模板(默认启动会请求/templates/index.html)
      thymeleaf:
        cache: false                      # 是否有模板缓存
        prefix: classpath:/templates/     # 模板放置的位置
        suffix: .html                     # 模板后缀
        mode: HTML                        # 模板类型
        encoding: UTF-8                   # 模板编码
2.ViewControllerRegistry类说明:

①addViewController(String urlPath)

通过urlPath参数指定的请求URL路径(例如"/home")注册一个简单的视图控制器,该方法返回一个ViewControllerRegistration对象,通过该对象可以设置相关属性,如视图名称、请求方式等。如:registry.addViewController("/login");

②setOrder(int order)

设置当前视图控制器的执行顺序,当有多个视图控制器针对同一请求路径时,可以使用该方法进行优先级排序。默认情况下,不同的视图控制器按照它们被注册的顺序执行。

③addRedirectViewController(String urlPath, String redirectUrl)

注册一个重定向视图控制器,将urlPath请求路径重定向到指定的重定向地址redirectUrl。

如:registry.addRedirectViewController("/toBaidu","https://www.baidu.com");

3.ViewControllerRegistration类说明:

①setViewName(String viewName)

资源路径的前缀

②setStatusCode(HttpStatus statusCode)

配置访问不存在资源的响应码,如下常见的:

HttpStatus.BAD_REQUEST:请求参数错误或格式不正确,例如缺少必需参数、参数类型错误等。

HttpStatus.UNAUTHORIZED:未经授权访问,需要用户登录或提供凭证。

HttpStatus.FORBIDDEN:已经授权但访问被禁止,通常意味着权限不足或需要进行进一步身份验证。

HttpStatus.NOT_FOUND:请求的资源不存在,通常使用自定义的404错误页面进行提示。

HttpStatus.METHOD_NOT_ALLOWED:请求方式不支持,例如GET请求访问只支持POST的接口时会返回405错误。

HttpStatus.INTERNAL_SERVER_ERROR:服务器内部错误,需要在后台进行排查和修复。

示例:registry.addViewController("/**").setStatusCode(HttpStatus.NOT_FOUND);

说明:访问不存在的页面我一律按照404处理,但是我们templates/error/404.html页面需要存在

注:按照之前配置的thymeleaf配置来说,默认根路径为resources/templates,跳转的资源文件都是.html文件

七:静态资源处理(addResourceHandlers)

addResourceHandlers方法是SpringMVC框架提供的一种配置静态资源的方式,它可以将指定的资源路径映射到一个或多个URL路径上,并指定资源的缓存策略、版本号以及是否允许目录列表等选项。具体来说,addResourceHandlers方法需要传入一个ResourceHandlerRegistry对象作为参数,然后在这个对象上调用addResourceHandler方法,来添加一个或多个处理器。

1.ResourceHandlerRegistry类方法介绍:

①addResourceHandler

该方法用于指定静态资源的URL路径,支持Ant风格的通配符,如"/resources/**"表示匹配所有以"/resources/"开头的请求。

2.ResourceHandlerRegistration类方法介绍:

①addResourceLocations

该方法为静态资源所在的物理路径或URL。可以使用多个addResourceLocations方法指定多个路径,如下例:

复制代码
registry.addResourceHandler("/resources/**")
	.addResourceLocations("classpath:/static/", "file:/opt/files/")

说明:

classpath:/static/表示在项目的Classpath下(即src/main/resources文件夹下)查找static文件夹,file:/opt/files/表示在系统中的/opt/files/目录下查找文件。

②setCacheControl

此方法用于设置缓存控制头(cache-control header),CacheControl是一个封装了缓存策略的类。例如:

复制代码
CacheControl cc = CacheControl.maxAge(30, TimeUnit.DAYS).cachePublic();
registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/static/")
	.setCacheControl(cc);

说明:这将指示浏览器缓存静态资源30天,并且它们是public缓存,意味着中间代理服务器也可以缓存资源。

③setCachePeriod

该方法用于设置静态资源缓存时间,参数类型为Duration类型。如:

复制代码
registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/static/")
	.setCachePeriod(Duration.ofMinutes(5));

说明:静态资源缓存的过期时间为5分钟

④setOptimizeLocations

此方法用于启用或禁用位置优化。如果启用位置优化,则将优化静态资源的位置,以便并发访问静态资源时可以获得更好的性能。默认情况下,位置优化是禁用的。

⑤setUseLastModified

此方法用于启用或禁用上次修改时间检查(last-modified check)。如果启用上次修改时间检查,则在每个请求中发送一个if-modified-since头,以检查是否需要返回新内容。默认情况下,上次修改时间检查是启用的。

⑥resourceChain

用于开启或关闭ResourceChain模式。当开启ResourceChain模式时,每个资源文件都会自动添加版本号,避免浏览器缓存问题。

例如:

复制代码
registry.addResourceHandler("/resources/**").addResourceLocations("classpath:/static/")
	.resourceChain(true);

注意,要使ResourceChain生效,还需要设置addResolver如:

复制代码
.resourceChain(false)
	// 添加VersionResourceResolver,且指定版本号
	.addResolver(new VersionResourceResolver()
	.addFixedVersionStrategy("1.0.0", "/**")); // 下次访问地址:http://localhost:8881/resources/1.0.0/xxx.css

八:代码示例(WebMvcConfigurer)

复制代码
package com.syh.pdd.config.web;
 
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.*;
 
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
 
@Configuration
public class WebConfig implements WebMvcConfigurer {
 
    // 用户头像
    @Value("${file.userImage.writePath}")
    private String userImageWritePath;
    @Value("${file.userImage.readPath}")
    private String userImageReadPath;
 
    /**
     * 映射文件路径配置
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
 
 
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
 
        registry.addResourceHandler("BJTPReadpath")
                .addResourceLocations("file:"+ "BJTPSavepath");
 
        // 浏览器测试图片回显路径: http://IP:8080/file/ewm/Img/2023/10/12/图片名称.jpg
  	    registry
                // 图片回显路径
                .addResourceHandler("file/ewm/Img/**")
                // 图片存放路径
                .addResourceLocations("file:D:/file/ewm/Img/");
        }
    }
 
    /**
     * 跨域配置添加
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路径
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
              //  .allowedOrigins("*")
                .allowedOriginPatterns("*")
                // 是否允许证书 不再默认开启
                .allowCredentials(true)
                // 设置允许的方法
                .allowedMethods("*")
                // 跨域允许时间
                .maxAge(3600);
    }
 
    //解决中文乱码问题
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        //解决中文乱码
        converters.add(responseBodyConverter());
        //解决 添加解决中文乱码后 上述配置之后,返回json数据直接报错 500:no convertter for return value of type
        converters.add(messageConverter());
    }
    @Bean
    public HttpMessageConverter<String> responseBodyConverter(){
        StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
        return converter;
    }
    @Bean
    public MappingJackson2HttpMessageConverter messageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(getObjectMapper());
        return converter;
    }
    @Bean
    public ObjectMapper getObjectMapper() {
        return new ObjectMapper();
    }
 
    /**
     *  格式化返回的内容
     *  https://my.oschina.net/u/3681868/blog/3075150
     * */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = converter.getObjectMapper();
        // 时间格式化
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 设置格式化内容
        converter.setObjectMapper(objectMapper);
        converters.add(0, converter);
    }
 
    /**
     * 添加Web项目的拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //放行路径
        List<String> jwtExcludePatterns = new ArrayList();
        // 登录接口放行
        jwtExcludePatterns.add("/api/user/login");
        // 验证码放行
        jwtExcludePatterns.add("/api/user/getVerify/**");
        // 前端更换头像请求,没有走拦截器,此处放行
        jwtExcludePatterns.add("/api/user/updatePicture");
        // 对所有api开头的访问路径,都通过MyInterceptor拦截器进行拦截,MyInterceptor代码在下面
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/api/**")
                .excludePathPatterns(jwtExcludePatterns);
    }
 
    /*解析器  使用方法在文章末尾*/
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        WebMvcConfigurer.super.addArgumentResolvers(resolvers);
    }
}

九:代码示例(拦截器)

复制代码
<dependency>
	<groupId>net.minidev</groupId>
	<artifactId>json-smart</artifactId>
</dependency>
<dependency>
	<groupId>com.vaadin.external.google</groupId>
	<artifactId>android-json</artifactId>
	<version>0.0.20131108.vaadin1</version>
	<scope>compile</scope>
</dependency>


package com.syh.pdd.config.web;
 
import com.syh.pdd.Utils.token.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import net.minidev.json.JSONObject;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
 
/**
 * 自定义拦截器类token认证
 * 注意:可以实现多个拦截器,只需要继续实现HandlerInterceptor即可            
 */
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
    /**
     * 访问控制器方法前执行
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        // ==========!!!!!!注意注意注意   注意注意   注意注意   ========================
        /*
         * 前端在请求的时候会发送一个OPTION请求来验证本次请求是否安全,
         * 但是springboot的拦截器会拦截所有请求。因为第一次是OPTION没有携带JWT,所以验证失败
         * */
        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            return true;
        }
        // 获取token
        String token = request.getHeader("token");
        // 校验token
        if (JwtUtil.checkToken(token)) {
            log.info(request.getRequestURL() + ">>>" +
                    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "通过token验证");
            return true; // 放行
        } else{
            //设置response状态
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            //设置失败响应数据
            JSONObject res = new JSONObject();
            res.put("status","101010");
            res.put("msg","登录过期,请重新登录");
            PrintWriter out = null ;
            out = response.getWriter();
            out.write(res.toString());
            out.flush();
            out.close();
            return false; // 拦截
        }
    }
 
    /**
     * 访问控制器方法后执行
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    }
 
    /**
     * postHandle方法执行完成后执行,一般用于释放资源
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    }
}


/**
 * Web配置类
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {
    /**
     * 添加Web项目的拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //放行路径
        List<String> jwtExcludePatterns = new ArrayList();
        // 登录接口放行
        jwtExcludePatterns.add("/system/user/login");
        // 验证码放行
        jwtExcludePatterns.add("/system/user/getVerify/**");
        // 对所有图片资源放行
        jwtExcludePatterns.add("/Project/saveFile/**");
 
        // 拦截路径:对所有访问路径,都通过MyInterceptor类型的拦截器进行拦截
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**")
                .excludePathPatterns(jwtExcludePatterns);
 
    }
}

十:代码示例(WebMvcConfigurationSupport)

复制代码
package com.hssmart.config.web;
 
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
 
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.List;
 
@Configuration
public class WebConfig extends WebMvcConfigurationSupport{
 
    // 自定义一个拦截器
    @Autowired
    UserArgumentResolver userArgumentResolver;
 
 
    /**
     * 映射文件路径配置
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
 
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
 
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
 
 
        /**
         * 说明:增加虚拟路径(经过本人测试:在此处配置的虚拟路径,用springboot内置的tomcat时有效,
         用外部的tomcat也有效;所以用到外部的tomcat时不需在tomcat/config下的相应文件配置虚拟路径了,阿里云linux也没问题)
         */
        //registry.addResourceHandler("/pic/**").addResourceLocations("file:E:/pic/");
 
 
 
        registry.addResourceHandler("BJTPReadpath")
                .addResourceLocations("file:"+ "BJTPSavepath");
 
 
        //阿里云(映射路径去除盘符)
        //registry.addResourceHandler("/ueditor/image/**").addResourceLocations("/upload/image/");
        //registry.addResourceHandler("/ueditor/video/**").addResourceLocations("/upload/video/");
 
        //用户图片路径
        registry.addResourceHandler("/Project/saveFile/user/userImg/**"(网络路径,其实可以任意定义))
                .addResourceLocations("file:D:/JAVA/Project/saveFile/user/userImg/"(储存路径));
 
        super.addResourceHandlers(registry);
    }
 
 
    /**
     * 跨域配置添加
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路径
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
                .allowedOrigins("*")
                // 是否允许证书 不再默认开启
                .allowCredentials(true)
                // 设置允许的方法 springboot较高版本不能使用*号
                // .allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH")
                .allowedMethods("*")
                // 跨域允许时间
                .maxAge(3600);
    }
 
 
    //解决中文乱码问题
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        super.configureMessageConverters(converters);
        //解决中文乱码
        converters.add(responseBodyConverter());
        //解决 添加解决中文乱码后 上述配置之后,返回json数据直接报错 500:no convertter for return value of type
        converters.add(messageConverter());
    }
 
 
    @Bean
    public HttpMessageConverter<String> responseBodyConverter(){
        StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
        return converter;
    }
    @Bean
    public MappingJackson2HttpMessageConverter messageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(getObjectMapper());
        return converter;
    }
    @Bean
    public ObjectMapper getObjectMapper() {
        return new ObjectMapper();
    }
 
 
    /**
     *  格式化返回的内容(格式转换器)
     *  https://my.oschina.net/u/3681868/blog/3075150
     * */
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = converter.getObjectMapper();
        // 生成JSON时,将所有Long转换成String
        //SimpleModule simpleModule = new SimpleModule();
        //simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        //simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        //objectMapper.registerModule(simpleModule);
        // 时间格式化
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 设置格式化内容
        converter.setObjectMapper(objectMapper);
        converters.add(0, converter);
    }
 
    /**
     * 解析器,该方法可实现可不实现,需要自定义。作用在调用Controller方法的参数传入之前,有返回值
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        // userArgumentResolver该类对Controller传入的参数做了具体处理
        resolvers.add(userArgumentResolver);
    }
 
 
}

自定义解析器

复制代码
@Configuration
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
 
    @Autowired
    Userservice userservice;
 
    // 此方法返回true,下面的参数才会执行
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        // 获取传入参数的类型
        Class<?> type = methodParameter.getParameterType();
        // 如果参数类型有为User类的则符合,进入resolveArgument方法
        if (UserPojo.class == type) {
            return true;
        }
        return false;
    }
 
    // 该方法为拦截方法,将结果返回给controller
    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest,
                                  WebDataBinderFactory webDataBinderFactory) throws Exception {
 
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
        
        String userTick = CookieUtil.getCookieValue(request, "userTicket");
        if (StringUtils.isBlank(userTick)) {
            return null;
        }
        UserPojo userPojo = userservice.getUserByCookie(userTick, request, response);
        if (userPojo == null) {
            return null;
        }
        return userPojo;
    }
}


@GetMapping("goods")
public Result showGoods(UserPojo user){
    // 注意:这里的User参数不是由前端传入的,而是由addArgumentResolvers方法处理之后传进来的
    log.info(user.toString());
    // 根据处理之后传入的参数判断是否登录
    if (user == null)
        return Result.error();
    return Result.ok();
}
相关推荐
xiaodaidai丶5 小时前
解决Sa-Token在 Spring MVC + WebFlux 混合架构中流式接口报错SaTokenContext 上下文尚未初始化的问题
spring·架构·mvc
xiaodaidai丶12 小时前
Spring Web MVC的异步请求解读
spring boot·spring·mvc
Thomas.Sir13 小时前
SpringMVC 工作原理深入解析
spring·设计模式·mvc·spring mvc
毅炼18 小时前
JVM常见问题总结(2)
java·jvm·mvc
Lyyaoo.1 天前
Spring MVC中用于处理HTTP请求的常用注解
spring·http·mvc
刀法如飞13 天前
一款Go语言Gin框架MVC脚手架,满足大部分场景
go·mvc·gin
莫寒清14 天前
Spring MVC:@PathVariable 注解详解
java·spring·mvc
莫寒清17 天前
Spring MVC:@RequestParam 注解详解
java·spring·mvc
莫寒清18 天前
Spring MVC:MultipartFile 详解
java·spring·mvc