《学会 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/47276.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

元宇宙深入解析

元宇宙&#xff08;Metaverse&#xff09;是一个新兴的概念&#xff0c;它激发了技术专家、艺术家和商业领袖的无限想象。它代表着数字互动的新前沿&#xff0c;提供了一个平行的数字宇宙&#xff0c;用户可以在其中实时互动&#xff0c;超越物理世界的限制。 元宇宙是什么&am…

一五六、Node+Vue 使用七牛上传图片,并配置个人域名

1. 七牛云ak/sk获取 点击注册&#x1f517;开通七牛开发者帐号如果已有账号&#xff0c;直接登录七牛开发者后台&#xff0c;点击这里&#x1f517;查看 Access Key 和 Secret Key 2. Node.js获取七牛token 安装qiniu npm install qiniu创建空间 Node获取token const qi…

美国INSTRUMENTS的L2/L6/L10/L20/L50线性放大器与ATA-L水声放大器

一、企业背景&#xff1a; Aigtek是一家来自中国的专业从事测量仪器研发、生产和销售的高科技企业。公司主要研发和生产功率放大器、功率放大器模块、功率信号源、计量校准源等产品。核心团队主要是来自西安交通大学及西北工业大学的专家教授等联合组成研发团队&#xff0c;目前…

QT开发笔记:信号和槽

乱码问题&#xff1a; 出现乱码问题原因只有一个&#xff1a;就是编码方式不匹配&#xff01;&#xff01;&#xff01; 中文常见汉字4K,算上各种生僻字差不多六万字 仍然使用一个大表格&#xff0c;给每个汉字&#xff0c;分配一个整数即可。 字符集~~表示汉字的字符集&#…

FPGA实训报告DAY 1(Verilog HDL)

实习日志与总结 日期&#xff1a;2024 年 7 月 10 日 星期三 姓名&#xff1a;XXX 一、实习日志 上午 9:00 - 9:30 按时到达工位&#xff0c;参加部门早会&#xff0c;了解了今天的实习任务和目标&#xff0c;即初步学习 FPGA 简介和 Verilog 基础语法知识。 9:30 - 10:30…

LeetCode-随机链表的复制

. - 力扣&#xff08;LeetCode&#xff09; 本题思路&#xff1a; 首先注意到随机链表含有random的指针&#xff0c;这个random指针指向是随机的&#xff1b;先一个一个节点的拷贝&#xff0c;并且把拷贝的节点放在拷贝对象的后面&#xff0c;再让拷贝节点的next指向原链表拷贝…

免分助手神器-交管12123学法减分模拟考试题目及答案 #经验分享#其他

“驾照学法减分”为驾驶人提供了一种积极参与交通安全事业、减缓驾驶证扣分的方式&#xff0c;有益于促进驾驶人形成遵守交通规则、规范驾驶行为的良好习惯&#xff0c;提高道路交通安全意识和素养。然而&#xff0c;持有人仍需严格遵守道路交通安全法律法规&#xff0c;减少交…

1.MQ介绍

MQ 消息队列&#xff0c;本质是一个队列&#xff0c;先进先出&#xff0c;只不过队列中存放的内容是message而已。 为啥学习MQ 1.流量消峰 如果一个订单系统最多每秒能处理一万次订单&#xff0c;正常情况下我们下单1秒后就能返回结果。但是在高峰期&#xff0c;如果有两万…

VsCode远程ssh连接失败:Could not establish connection to XXX

一、问题描述 在VsCode中按下"F1"&#xff0c;选择Remote-SSH:Connect to Host 选择一个已经配置好的SSH主机&#xff0c;比如我选择的是192.168.0.104&#xff1a; 结果提示&#xff1a;Could not establish connection to XXX 二、解决方法 观察VsCode的输出信息…

splunk编写自定义命令

1. 自定义命令简介 splunk有丰富的内置搜索命令&#xff0c;但也提供了编写自定义命令来实现个性化的搜索需求&#xff0c;以此方式扩展splunk搜索处理语言&#xff08;SPL&#xff09;。 自定义搜索命令是一个可执行文件&#xff0c;可读入和写出数据&#xff0c;它可以是一…

办公效率翻倍!2024最适合办公电脑安装的系统推荐!

当前&#xff0c;电脑已成为我们日常办公最常用的工具。为了确保高效顺畅的办公体验&#xff0c;选择一款合适的操作系统至关重要。那么&#xff0c;如何找到并下载最适合办公电脑的操作系统呢&#xff1f;接下来系统之家小编为您推荐2024年最适合办公电脑安装的系统&#xff0…

Qt窗口程序整理汇总

到今日为止&#xff0c;通过一个个案例的实验&#xff0c;逐步熟悉了 Qt6下 窗体界面开发的&#xff0c;将走过的路&#xff0c;再次汇总整理。 Qt Splash样式的登录窗https://blog.csdn.net/castlooo/article/details/140462768 Qt实现MDI应用程序https://blog.csdn.net/cast…

基于单片机STC89C52和GSM实现的远程拨号开锁设计(含文档、源码与proteus仿真,以及系统详细介绍)

本篇文章论述的是基于单片机STC89C52和GSM实现的远程拨号开锁设计的详情介绍&#xff0c;如果对您有帮助的话&#xff0c;还请关注一下哦&#xff0c;如果有资源方面的需要可以联系我。 目录 摘要 仿真图 单片机系统流程图 实物图 代码 系统论文 资源下载 摘要 本文介…

python数据可视化(9)——绘制小提琴图

课程学习来源&#xff1a;b站up&#xff1a;【蚂蚁学python】 【课程链接&#xff1a;【【数据可视化】Python数据图表可视化入门到实战】】 【课程资料链接&#xff1a;【链接】】 python&#xff1a;3.12.3 所有库都使用最新版。 Python绘制小提琴图 小提琴图(violin plot…

【经验总结】将markdown文档转换为word(swagger导出word)

工具准备&#xff1a; 任意markdown编辑器&#xff0c;以typora为例pandoc&#xff0c;官方下载地址 思路整理&#xff1a; 从swagger提取离线md文档将md文档转换为word格式 操作步骤&#xff1a; 一、安装pandoc &#xff08;markdown编辑器安装略&#xff09; 前往官网…

Yak与nuclei的深度融合:打造高效漏扫生态,解锁PoC管理新姿势

在Yakit中使用nuclei很简单&#xff0c;只需要几行代码。在Yak Runner中&#xff0c;使用下面代码&#xff0c;指定扫描的目标与选项&#xff0c;便能调用nuclei的漏扫能力&#xff1a; results:nuclei.Scan(target,opts...)~for result in results { dump(result)} 比如以…

UNiapp微信小程序Ucharts

效果图如下 以上为加载接口所得数据的玫瑰图与折线图 具体步骤如下 1&#xff0c;将插件导入Hbuiler 所需要的项目中&#xff08;插件地址&#xff1a;秋云 ucharts echarts 高性能跨全端图表组件 - DCloud 插件市场&#xff09; 2&#xff0c;导入成功是这样的 3&#xff0c…

相对定位语法:css+xpath基础语法使用-定位页面元素

文章目录 CSS相对定位获取元素关系定位顺序关系 XPath相对定位基础语法顺序关系-通过索引获取元素选取元素 总结 ✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来&#xff01; 编程真是一件很奇妙的东西。你只是浅尝辄止&#xff0c;那么只会觉得枯燥乏味&#xff0c…

uniapp小程序上传pdf文件

<template><view class="mainInnBox"><view class="formBox"><!-- 注意,如果需要兼容微信小程序,最好通过setRules方法设置rules规则 --><u-form :model="form" ref="uForm" :rules="rules"&g…

韦东山嵌入式linux系列-具体单板的 LED 驱动程序

笔者使用的是STM32MP157的板子 1 怎么写 LED 驱动程序&#xff1f; 详细步骤如下&#xff1a; ① 看原理图确定引脚&#xff0c;确定引脚输出什么电平才能点亮/熄灭 LED ② 看主芯片手册&#xff0c;确定寄存器操作方法&#xff1a;哪些寄存器&#xff1f;哪些位&#xff1f;…