当前位置: 首页 > news >正文

《学会 SpringBoot · 定制 SpringMVC》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 近期刚转战 CSDN,会严格把控文章质量,绝不滥竽充数,如需交流,欢迎留言评论。👍

文章目录

    • 写在前面的话
    • 定制 MVC 功能(前言)
      • WebMvcConfigurerAdapter 废弃方式
      • WebMvcConfigurationSupport 覆盖方式
      • WebMvcConfigurer 推荐方式
    • 定制 MVC 功能(正篇)
      • 可以做什么?
      • 静态资源配置
      • 拦截器配置
      • 参数解析器
      • 跨域拦截器
      • 消息转换器
      • 格式化器&转换器
      • 路径匹配规则
      • 内容协商策略
      • 异步调用支持
      • 静态资源处理器
    • 总结陈词

CSDN.gif

写在前面的话

使用SpringBoot作为Java后端开发框架,基本是大多数企业的标配,这边把实际企业开发中,一些常用的 SpringBoot 操作做一个整理,将持续更新。


定制 MVC 功能(前言)

Spring Boot 为 Spring MVC 提供了默认的配置主要包括视图解析器、静态资源处理、类型转化器与格式化器、HTTP消息转换器、静态主页支持等,可谓简单易用。但实践中,难免需要进行个性化的配置,因此自定义Web MVC配置在所难免。
Spring Boot 先后提供了 WebMvcConfigurerAdapter、WebMvcConfigurationSupport、WebMvcConfigurer、@EnableWebMvc 等形式来实现Web MVC的自定义配置。

WebMvcConfigurerAdapter 废弃方式

SB1.x,可使用WebMvcConfigurerAdapter来扩展Spring MVC的功能,它是WebMvcConfigurer的一个抽象实现类,该抽象类中所有的方法实现都为空,子类需要哪些功能就实现哪些功能。
SB2.x,基于Java8实现,可将接口的方法定义为default,接口中被定义为default的方法子类可以不进行实现。而接口WebMvcConfigurer便运用了Java8的特性,因此WebMvcConfigurerAdapter存在的意义没有了。

@Deprecated
public abstract class WebMvcConfigurerAdapter implements WebMvcConfigurer {}

查看WebMvcConfigurerAdapter的实现,你会发现它就是把接口的所有方法实现为一个空的方法而已,Java8的default特性完全覆盖掉此功能。

WebMvcConfigurationSupport 覆盖方式

SB2.x,WebMvcConfigurerAdapter 被废弃了,那么我们还可以通过继承 WebMvcConfigurationSupport 来实现Spring MVC的拓展。

public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {...}

这个类很特殊,实现了ApplicationContextAware和ServletContextAware接口, 提供了一些默认实现,同时提供了很多@Bean 方法,但是并没有提供@Configureation注解,因此这些@Bean并不会生效,所以我们需要继承这个类,并在提供的类上提供@Configureation注解才能生效。
WebMvcConfigurationSupport 中不仅定义了Bean,还提供了大量add、config开头的方法。

/**
* Override this method to add Spring MVC interceptors for
* pre- and post-processing of controller invocation.
* @see InterceptorRegistry*/
protected void addInterceptors(InterceptorRegistry registry) {}/*** Override this method to add view controllers.* @see ViewControllerRegistry
*/
protected void addViewControllers(ViewControllerRegistry registry) {}

继承 WebMvcConfigurationSupport之后,可以使用方法来添加自定义的拦截器、视图解析器等功能,如下:

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {@Overrideprotected void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("login");registry.addViewController("/login.html").setViewName("login");}
}

这种方式有一个问题,其他没自定义实现的逻辑,也会无效,你可能会遇到比如静态资源访问不到、返回数据不成功等奇奇怪怪的问题。
那么,为什么继承WebMvcConfigurationSupport会顶替到Spring Boot默认的MVC配置呢?先来看一下Spring Boot中对WEB MVC相关组件自动装配的实现:

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {...}

Spring Boot通过WebMvcAutoConfiguration配置类来对MVC的默认参数(约定)进行设置,但WebMvcAutoConfiguration生效是有限制条件的。@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})指定了,当Spring容器中不存在类型为WebMvcConfigurationSupport的bean的时候,才会进行默认配置。一定自定义了WebMvcConfigurationSupport,那么将导致WebMvcAutoConfiguration无法实例化,进而内部初始化配置将全部无法实例化。
这种情况下,相关的配置都需要自己去实现了,除非对代码有极好的把控能力,或者大量特殊化定制,才会考虑此种形式。否则,一些列的约定便不复存在,可能会出现一些莫名其妙的问题。

WebMvcConfigurer 推荐方式

为了解决上述问题,我们可以直接实现WebMvcConfigurer接口,这种方式不会影响未覆盖的方法逻辑,这也是推荐的稳妥方式。

@Configuration
public class MyMVCConfig implements WebMvcConfigurer {@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/user").setViewName("success");}
}

定制 MVC 功能(正篇)

Spring2.0版本后,推荐使用实现WebMvcConfigurer的方式 来全局定制SpringMVC特性。
也可以继承WebMvcConfigurationSupport实现,但会覆盖默认行为,具体参考前面分析专栏。

Tips:早期1.x版本是使用 extends WebMvcConfigurerAdapter 的方式,暂不需要了解。

@Configuration    
public class WebMvcConfg implements WebMvcConfigurer {}

可以做什么?

/* 拦截器配置 */
void addInterceptors(InterceptorRegistry var1);/* 视图跳转控制器 */
void addViewControllers(ViewControllerRegistry registry);/* 静态资源处理 */
void addResourceHandlers(ResourceHandlerRegistry registry);/* 默认静态资源处理器 */
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);/* 这里配置视图解析器 */
void configureViewResolvers(ViewResolverRegistry registry);/* 配置内容裁决的一些选项*/
void configureContentNegotiation(ContentNegotiationConfigurer configurer);/** 解决跨域问题 **/
public void addCorsMappings(CorsRegistry registry) ;

静态资源配置

重写 addResourceHandlers 来配置路径访问等,SB 中默认使用 ResourceHttpRequestHandler 来映射类路径下的/static、/public、/resources 等路径中的静态文件直接映射为 /****。

/*** 配置静态访问资源* addResoureHandler:指的是对外暴露的访问路径* addResourceLocations:指的是内部文件放置的目录* 范例:http://localhost:8868/my/a1.jpg* 等同:http://localhost:8868/img/a1.jpg*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/my/**").addResourceLocations("classpath:/static/img/");
}@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {	//静态资源路径 css,js,img等registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/");//视图registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");//mapper.xmlregistry.addResourceHandler("/mapper/**").addResourceLocations("classpath:/mapper/");super.addResourceHandlers(registry);		
}  

拦截器配置

重写addInterceptors() 方法来配置拦截器(实现了HandlerInterceptor接口)等。这里实现的addInterceptors方法对应的是xml文件中mvc:interceptors配置。

@Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new CustomInterceptor()).addPathPatterns("/**");
}@Autowired
private MyInteceptor myInteceptor;@Override
public void addInterceptors(InterceptorRegistry registry) {	//注册自定义拦截器,添加拦截路径和排除拦截路径registry.addInterceptor(myInteceptor) //添加拦截器.addPathPatterns("/**") //添加拦截路径.excludePathPatterns("/statics/**/*.*",);//排除拦截路径super.addInterceptors(registry); //这句可不要	
}

参数解析器

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(currentUserMethodArgumentResolver());
}@Bean
CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {return new CurrentUserMethodArgumentResolver();
}

跨域拦截器

重写addCorsMappings方法实现配置cors跨域限制等。

/*** 配置跨域拦截器** @param registry 跨域拦截器*/
@Override
public void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowCredentials(true).allowedHeaders("*").allowedOrigins("*").allowedMethods("*");
}@Override
public void addCorsMappings(CorsRegistry registry) {		registry.addMapping("/**")//配置允许跨域的路径.allowedOrigins("*")//配置允许访问的跨域资源的请求域名.allowedMethods("PUT,POST,GET,DELETE,OPTIONS")//配置允许访问该跨域资源服务器的请求方法,如:POST、GET、PUT、DELETE等.allowedHeaders("*"); //配置允许请求header的访问,如 :X-TOKENsuper.addCorsMappings(registry);
}/*** 此种设置跨域的方式,在自定义拦截器的情况下可能导致跨域失效* 原因:当跨越请求在跨域请求拦截器之前的拦截器处理时就异常返回了,那么响应的response报文头部关于跨域允许的信息就没有被正确设置,导致浏览器认为服务不允许跨域,而造成错误。* 解决:自定义跨域过滤器解决跨域问题(该过滤器最好放在其他过滤器之前)* @param registry*/
@Override
public void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE").allowCredentials(true).maxAge(3600);
}

消息转换器

重写configureMessageConverters方法来对消息进行转换。MessageConverter用于对http请求的返回结果进行转换,以fastjon、编码格式application/json;charset=UTF-8进行转换。

/*** 添加自定义消息转换器* 自定义消息转化器的第二种方法* 默认也会注册utf-8的该转换器*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));converters.add(converter);
}

格式化器&转换器

重写addFormatters方法来添加数据格式化器,比如将字符串转换为日期类型,可通过DateFormatter类来实现自动转换。
formatters和converters用于对日期格式进行转换,默认已注册了Number和Date类型的formatters,支持@NumberFormat和@DateTimeFormat注解,需要自定义formatters和converters可以实现addFormatters方法。

@Override
public void addFormatters(FormatterRegistry registry) {registry.addFormatter(new DateFormatter("yyyy-MM-dd"));
}@Override
public void addFormatters(FormatterRegistry registry) {//注册ConverterFactory(类型转换器工厂)registry.addConverterFactory(new BaseEnumConverterFactory());
}//具体实例参考上方博客
@Override
public void addFormatters(FormatterRegistry registry) {registry.addFormatter(booleanFormatter());// 布尔格式化器registry.addConverter(stringToDateConverter());// 字符串转日期转化器
}@Bean
BooleanFormatter booleanFormatter() {return new BooleanFormatter();
}@Bean
StringToDateConverter stringToDateConverter() {return new StringToDateConverter();
}

路径匹配规则

@Override
public void configurePathMatch(PathMatchConfigurer configurer) {// 设置是否模糊匹配,默认真。例如/user是否匹配/user.*。如果真,也就是说"/user.html"的请求会被"/user"的Controller所拦截。configurer.setUseSuffixPatternMatch(false);// 设置是否自动后缀模式匹配,默认真。如/user是否匹配/user/。如果真,也就是说, "/user"和"/user/"都会匹配到"/user"的Controller。configurer.setUseTrailingSlashMatch(true);
}

内容协商策略

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {// 自定义策略configurer.favorPathExtension(true)// 是否通过请求Url的扩展名来决定mediaType,默认true.ignoreAcceptHeader(true)// 不检查Accept请求头.parameterName("mediaType").defaultContentType(MediaType.TEXT_HTML)// 设置默认的MediaType.mediaType("html", MediaType.TEXT_HTML)// 请求以.html结尾的会被当成MediaType.TEXT_HTML.mediaType("json", MediaType.APPLICATION_JSON)// 请求以.json结尾的会被当成MediaType.APPLICATION_JSON.mediaType("xml", MediaType.APPLICATION_ATOM_XML);// 请求以.xml结尾的会被当成MediaType.APPLICATION_ATOM_XML// 或者下面这种写法Map<String, MediaType> map = new HashMap<>();map.put("html", MediaType.TEXT_HTML);map.put("json", MediaType.APPLICATION_JSON);map.put("xml", MediaType.APPLICATION_ATOM_XML);// 指定基于参数的解析类型ParameterContentNegotiationStrategy negotiationStrategy = new ParameterContentNegotiationStrategy(map);// 指定基于请求头的解析configurer.strategies(Arrays.asList(negotiationStrategy));
}

异步调用支持

@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {// 注册callable拦截器configurer.registerCallableInterceptors(timeoutInterceptor());// 注册deferredResult拦截器configurer.registerDeferredResultInterceptors();// 异步请求超时时间configurer.setDefaultTimeout(1000);// 设定异步请求线程池callable等, spring默认线程不可重用configurer.setTaskExecutor(new ThreadPoolTaskExecutor());
}@Bean
public TimeoutCallableProcessingInterceptor timeoutInterceptor() {return new TimeoutCallableProcessingInterceptor();
}//测试接口
@GetMapping("test1")
public Callable<String> test1() {Callable<String> callable = () -> {Thread.sleep(60000);return "test";};return callable;
}

静态资源处理器

@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();configurer.enable("defaultServletName");
}

此时会注册一个默认的Handler:DefaultServerHttpRequestHandler,这个Handler也会用来处理静态文件的,它会尝试映射/*。当DispatcherServlet映射/时(/ 和/*是有区别的),并且没有找到合适的Handler来处理请求时,就会交给DefaultServletHttpRequestHandler来处理。注意:这里的静态资源是放置在web根目录下,而非WEB_INF下。
举例说明:在webroot目录下有一个图片a.png,我们知道Servelt规范中web根目录webroot下的文件可以直接访问的,但是由于DispatcherServlet配置了映射路径是:/,它几乎把所有的请求都拦截了,从而导致a.png访问不到,这时注册一个DefaultServletHttpRequestHandler就可以解决这个问题,其实可以理解为DispatchServlet破坏了Servler的一个特性(就是根目录下的文件可以直接访问),DefaultServletHttpRequestHandler是帮助回归这个特性的。


总结陈词

此篇文章介绍了SpringBoot 项目中如何常见的SpringMVC定制能力,仅供学习参考。
💗 后续将持续更新,请多多支持!!

CSDN_END.gif

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Java的数组
  • Python与MQTT:构建物联网通信的桥梁
  • PostgreSQL使用(三)
  • Vue和Element UI 路由跳转
  • 【C语言】详解结构体(上)
  • 通过Dockerfile构建镜像
  • 用HTML和CSS实现提示工具(tooltip)及HTML元素的定位
  • zookeeper+kafka消息队列群集部署
  • 分布式 I/O 系统Modbus TCP 耦合器BL200
  • pytorch中一些最基本函数和类
  • 集群架构-web服务器(接入负载均衡+数据库+会话保持redis)--15454核心配置详解
  • 华为USG6000V防火墙安全策略用户认证
  • 01.Verilog基础语法
  • js中scrollIntoView第一次不生效,第二次生效
  • Linux C++ 055-设计模式之状态模式
  • const let
  • echarts花样作死的坑
  • GitUp, 你不可错过的秀外慧中的git工具
  • Laravel Telescope:优雅的应用调试工具
  • LeetCode29.两数相除 JavaScript
  • SpringBoot 实战 (三) | 配置文件详解
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 前端设计模式
  • 深入浏览器事件循环的本质
  • 使用 QuickBI 搭建酷炫可视化分析
  • ​520就是要宠粉,你的心头书我买单
  • ### RabbitMQ五种工作模式:
  • #### go map 底层结构 ####
  • #mysql 8.0 踩坑日记
  • #QT项目实战(天气预报)
  • (1)Jupyter Notebook 下载及安装
  • (145)光线追踪距离场柔和阴影
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (delphi11最新学习资料) Object Pascal 学习笔记---第13章第1节 (全局数据、栈和堆)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (ros//EnvironmentVariables)ros环境变量
  • (补充)IDEA项目结构
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (六)软件测试分工
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (三十)Flask之wtforms库【剖析源码上篇】
  • (一)Neo4j下载安装以及初次使用
  • (一)UDP基本编程步骤
  • **《Linux/Unix系统编程手册》读书笔记24章**
  • .htaccess配置常用技巧
  • .Net 中Partitioner static与dynamic的性能对比
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • .net用HTML开发怎么调试,如何使用ASP.NET MVC在调试中查看控制器生成的html?
  • .NET与 java通用的3DES加密解密方法
  • .sh文件怎么运行_创建优化的Go镜像文件以及踩过的坑
  • .skip() 和 .only() 的使用
  • 。Net下Windows服务程序开发疑惑
  • [ IO.File ] FileSystemWatcher
  • [ 常用工具篇 ] AntSword 蚁剑安装及使用详解
  • [Android Pro] android 混淆文件project.properties和proguard-project.txt