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 {@Beanpublic 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;/*** 映射文件路径配置*/@Overridepublic 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/图片名称.jpgregistry// 图片回显路径.addResourceHandler("file/ewm/Img/**")// 图片存放路径.addResourceLocations("file:D:/file/ewm/Img/");}}/*** 跨域配置添加*/@Overridepublic void addCorsMappings(CorsRegistry registry) {// 设置允许跨域的路径registry.addMapping("/**")// 设置允许跨域请求的域名// .allowedOrigins("*").allowedOriginPatterns("*")// 是否允许证书 不再默认开启.allowCredentials(true)// 设置允许的方法.allowedMethods("*")// 跨域允许时间.maxAge(3600);}//解决中文乱码问题@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {//解决中文乱码converters.add(responseBodyConverter());//解决 添加解决中文乱码后 上述配置之后,返回json数据直接报错 500:no convertter for return value of typeconverters.add(messageConverter());}@Beanpublic HttpMessageConverter<String> responseBodyConverter(){StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));return converter;}@Beanpublic MappingJackson2HttpMessageConverter messageConverter() {MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();converter.setObjectMapper(getObjectMapper());return converter;}@Beanpublic ObjectMapper getObjectMapper() {return new ObjectMapper();}/*** 格式化返回的内容* https://my.oschina.net/u/3681868/blog/3075150* */@Overridepublic 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项目的拦截器*/@Overridepublic 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);}/*解析器 使用方法在文章末尾*/@Overridepublic 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 {/*** 访问控制器方法前执行*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {// ==========!!!!!!注意注意注意 注意注意 注意注意 ========================/** 前端在请求的时候会发送一个OPTION请求来验证本次请求是否安全,* 但是springboot的拦截器会拦截所有请求。因为第一次是OPTION没有携带JWT,所以验证失败* */if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {return true;}// 获取tokenString token = request.getHeader("token");// 校验tokenif (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; // 拦截}}/*** 访问控制器方法后执行*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {}/*** postHandle方法执行完成后执行,一般用于释放资源*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}
}/*** Web配置类*/
@Configuration
public class WebConfig implements WebMvcConfigurer {/*** 添加Web项目的拦截器*/@Overridepublic 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{// 自定义一个拦截器@AutowiredUserArgumentResolver userArgumentResolver;/*** 映射文件路径配置*/@Overrideprotected 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);}/*** 跨域配置添加*/@Overridepublic void addCorsMappings(CorsRegistry registry) {// 设置允许跨域的路径registry.addMapping("/**")// 设置允许跨域请求的域名.allowedOrigins("*")// 是否允许证书 不再默认开启.allowCredentials(true)// 设置允许的方法 springboot较高版本不能使用*号// .allowedMethods("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH").allowedMethods("*")// 跨域允许时间.maxAge(3600);}//解决中文乱码问题@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {super.configureMessageConverters(converters);//解决中文乱码converters.add(responseBodyConverter());//解决 添加解决中文乱码后 上述配置之后,返回json数据直接报错 500:no convertter for return value of typeconverters.add(messageConverter());}@Beanpublic HttpMessageConverter<String> responseBodyConverter(){StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));return converter;}@Beanpublic MappingJackson2HttpMessageConverter messageConverter() {MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();converter.setObjectMapper(getObjectMapper());return converter;}@Beanpublic ObjectMapper getObjectMapper() {return new ObjectMapper();}/*** 格式化返回的内容(格式转换器)* https://my.oschina.net/u/3681868/blog/3075150* */@Overridepublic 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方法的参数传入之前,有返回值*/@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {// userArgumentResolver该类对Controller传入的参数做了具体处理resolvers.add(userArgumentResolver);}}
自定义解析器
@Configuration
public class UserArgumentResolver implements HandlerMethodArgumentResolver {@AutowiredUserservice userservice;// 此方法返回true,下面的参数才会执行@Overridepublic boolean supportsParameter(MethodParameter methodParameter) {// 获取传入参数的类型Class<?> type = methodParameter.getParameterType();// 如果参数类型有为User类的则符合,进入resolveArgument方法if (UserPojo.class == type) {return true;}return false;}// 该方法为拦截方法,将结果返回给controller@Overridepublic 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();
}
相关文章:

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

用 javascript 来回答宇宙外面是什么
宇宙外面是什么呢? 估计这个问题要困扰很多人, 让我们用一段 javascript 代码来回答一下. 一, 从一段代码说起 var 地球 {名字 : "地球",女友 : "月亮",外面 : {名字 : "太阳系",老大 : "太阳",老二 : {名字 : "木星",二…...

我的性能优化经验
专业方向:App cpu/memory/gpu/流畅度/响应时间的优化,Anr,Framework CarPowerManagementService模块的(STR),从0~1完成性能监控体系搭建,完成3大版本迭代高质量性能交付 响应时间: …...

XSLT 编辑 XML
XSLT 编辑 XML 介绍 XSLT(可扩展样式表语言转换)是一种用于转换XML文档的语言。它允许开发人员将XML数据转换为其他格式,如HTML、PDF或纯文本。XSLT通过使用XPath查询来定位XML文档中的元素,并对这些元素应用转换规则。在本教程…...

数智读书笔记系列010 生命3.0:人工智能时代 人类的进化与重生
书名:生命3.0 生命3.0:人工智能时代,人类的进化与重生 著者:[美]迈克斯•泰格马克 迈克斯・泰格马克 教育背景与职业 教育背景:迈克斯・泰格马克毕业于麻省理工学院,获物理学博士学位。职业经…...

Transfomer的各层矩阵
一、输入 输入一句话:Hello CYZLAB the inspired world 每个单词为一个token 二、Embedding 这里的词向量维度为6,矩阵的行数为token数,列数是词向量的维度 这列是注释不算hello122694CYZLAB222372the222596inspired132440world431273 …...

代码随想录第51天
99.岛屿数量 深搜 import java.util.*;class Main{static int[][] directions {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};static boolean[][] visited;public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int m sc.nextInt();int…...

基础库httpx的使用
urllib 库和 requests 库的使用,已经可以爬取绝大多数网站的数据,但对于某些网站依然无能为力。什么情况?这些网站强制使用HTTP/2.0协议访问,这时 urllib 和requests 是无法爬取数据的,因为它们只支持 HTTP/1.1,不支持…...

c++中如何保持结构体的线程安全?3D坐标的线程安全:从理论到最优解
使用mutex的困扰 struct Point3d {std::mutex mtx;double x, y, z;void set(double nx, double ny, double nz) {std::lock_guard<std::mutex> lock(mtx);x nx; y ny; z nz;} };这种方案虽然安全,但性能开销确实不小。每次访问都需要加锁解锁,…...

Zabbix6.0升级为6.4
为了体验一些新的功能,比如 Webhook 和问题抑制等,升级个小版本。 一、环境信息 1. 版本要求 一定要事先查看官方文档,确认组件要求的版本,否则版本过高或者过低都会出现问题。 2. 升级前后信息 环境升级前升级后操作系统CentOS…...

答题考试系统v1.6.1高级版源码分享+uniapp+搭建测试环境
一.系统介绍 一款基于FastAdminThinkPHPUniapp开发的小程序答题考试系统,支持多种试题类型、多种试题难度、练题、考试、补考模式,提供全部前后台无加密源代码,支持私有化部署 二.测试环境 系统环境:CentOS、 运行环境&#x…...

【Lua热更新】下篇 -- 更新中
上篇链接:【Lua热更新】上篇 文章目录 三、xLua热更新📖1.概述📚︎2.导入xLua框架🔖3. C#调用Lua3.1Lua解析器3.2Lua文件夹的重定向3.3Lua解析器管理器3.4全局变量获取3.5全局函数获取3.6映射到List和Dictionary3.7映射到类3.8映…...

射频测试入门学习(三)——程控仪器是怎样和电脑连接通信的
目录 一、程控仪器需要哪些条件 二、可程控仪器 三、专业的仪器通信软件、驱动 四、编程语言 五、电脑控制仪器条件汇总 六、仪器指令查询 七、结语 一、程控仪器需要哪些条件 1、需要具备硬件条件(可程控的仪器、个人计算机(PC)) 2、专业的仪器通信软件、驱动 3、…...

并发控制之Semaphore
Semaphore 作用 信号量,用于控制同时访问特定资源的线程数量,通过协调各个线程,以确保对共享资源的访问不会导致冲突或数据不一致等问题,有点类似令牌桶,内部维护一组许可证, acquire获取许可证…...

第R3周:RNN-心脏病预测
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 🍺要求: 1 本地读取并加载数据。 2 了解循环神经网络(RNN)的构建过程 3 测试集accuracy到达87% 🍻拔…...

【数值特性库】入口文件
数值特性库入口文件为lib.rs。该文件定义一系列数字特性的trait(特征),这些特性可以被不同的数字类型实现,从而提供一套通用的数值操作方法。下面是对代码中关键部分的解释: 一、基础设置 #
RestTemplate实时接收Chunked编码传输的HTTP Response
学习调用AI接口的时候,流式响应都是使用的 Transfer-Encoding: chunked,图方便想用RestTemplate,但是平时用到的都是直接返回响应对象的类型。使用bing搜索到一种方式,使用下面的代码来读取,于是掉这个坑里了ÿ…...

GIT区域介绍及码云+GIt配置仓库
GIT区域介绍 创建文件夹git init 1、git有3个区域 工作区(working directory):项目的根目录,不包 括.git在内的其他文件暂存区(stage area):是一个看不见的区域,git add 命令就是将文…...

网络安全怎么学习
当我们谈论网络安全时,我们正在讨论的是保护我们的在线空间,这是我们所有人的共享责任。网络安全涉及保护我们的信息,防止被未经授权的人访问、披露、破坏或修改。 一、网络安全的基本概念 网络安全是一种保护:它涉及保护我们的设…...

PugiXML,一个高效且简单的 C++ XML 解析库!
嗨,大家好!我是一行。今天要给大家介绍 PugiXML,这可是 C 里处理 XML 数据的得力助手。它能轻松地读取、修改和写入 XML 文件,就像一个专业的 XML 小管家,不管是解析配置文件,还是处理网页数据,…...

Linux设备树的驱动开发
概述 本文介绍了platform框架下的设备驱动开发流程和方法,主要包括设备树、驱动程序和应用程序的开发。以随机数驱动为例,实现了应用程序调用库函数,通过系统调用陷入内核,最后执行硬件驱动,获取真随机数的过程。 添…...

连锁?下沉?AI?2025年餐饮新活力!
如果要用几个词来形容 2024 年的餐饮业,这些词大概率会是「卷、难、惨」,用著名商业顾问刘润的话来说就是「卷到极致」。虽然餐饮人在社交平台上叫苦连天,但当我们查看餐饮大盘数据时发现,大盘在涨,与个体餐饮人的实感…...

Javascript中如何实现函数缓存?函数缓存有哪些应用场景?
今天要聊的一个很经典的问题——如何在JavaScript中实现函数缓存,以及它有哪些应用场景。 我们先来明确一下,函数缓存是什么。简单来说,函数缓存是将函数的运算结果存储起来,以便下次用到相同的输入时,可以直接返回结…...

子页面访问父页面
子页面访问父页面的方式主要依赖于页面之间的关系,特别是它们是否处于同一域、是否是嵌套在 <iframe> 中、或者通过弹出窗口打开。下面是几种常见的子页面访问父页面的方法: 1. 通过 window.parent 访问父页面(适用于嵌套的 iframe&am…...

芯片级IO (Pad) Ring IP Checklist
SoC top顶层数字后端实现都会涉及到IO Ring (PAD Ring)的设计。这里面包括VDD IO,VDDIO IO, Signal IO, Corner IO,Filler IO,IO power cut cell等等。 数字后端零基础入门系列 | Innovus零基础LAB学习Day2 数字IC后端实现TOP F…...

计算机毕业设计论文指导
计算机毕业设计论文指导 计算机毕业设计辅导一站式!太香了💪 [赞R][赞R][赞R]嗨喽!计算机专业的宝子们! 计算机毕设辅导专业靠谱的他来了!! 是不是还在为选题程序不会做而感到苦难? 论文没思路赶…...

Electron-Vue 开发下 dev/prod/webpack server各种路径设置汇总
背景 在实际开发中,我发现团队对于这几个路径的设置上是纯靠猜的,通过一点点地尝试来找到可行的路径,这是不应该的,我们应该很清晰地了解这几个概念,以下通过截图和代码进行细节讲解。 npm run dev 下的路径如何处理&…...

Vue.js前端框架教程9:Vue插槽slot用法
文章目录 插槽(Slots)无名插槽(默认插槽)具名插槽reference 插槽使用 v-slot 的缩写语法 插槽(Slots) 在 Vue 中,插槽(Slots)是一种组件内容分发的机制,允许…...

初学stm32 --- NVIC中断
目录 STM32 NVIC 中断优先级管理 NVIC_Type: ISER[8]: ICER[8]: ISPR[8]: ICPR[8]: IABR[8]: IP[240]: STM32 的中断分组: 中断优先级分组函数 NVIC_PriorityGroupConfig 中断初始化函…...

Jest 入门指南:从零开始编写 JavaScript 单元测试
前言 在前端开发中,单元测试已经成为确保代码质量和稳定性的关键步骤。Jest 作为由 Facebook 开发和维护的功能强大的 JavaScript 测试框架,以其易于配置、丰富的功能和开箱即用的特性,成为众多开发者的首选工具。本文旨在引导你从零开始&am…...