《学会 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 {
    @Override
    protected 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 {

    @Override
    public 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.xml
    registry.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-TOKEN
    super.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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【持续集成_06课_Jenkins高级pipeline应用】

一、创建项目选择pipeline的风格 它主要是以脚本&#xff08;它自己的语言&#xff09;的方式进行运行&#xff0c;一般由运维去做的事情&#xff0c;作为测试而言。了解即可。 --- 体现形式全部通过脚本去实现&#xff1a;执行之前&#xff08;拉取代码&#xff09;执行&…

【JavaEE精炼宝库】 初识网络原理——网络通信基础 | 协议

文章目录 一、网络发展史1.1 独立模式&#xff1a;1.2 网络互连&#xff1a;1.3 局域网&#xff08;LAN&#xff09;&#xff1a;1.4 广域网&#xff08;WAN&#xff09;&#xff1a; 二、网络通信基础2.1 IP地址&#xff1a;2.2 端口号&#xff1a; 三、协议3.1 协议的概念&am…

[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-18 I2C MASTER控制器驱动设计

软件版本&#xff1a;Anlogic -TD5.9.1-DR1_ES1.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用安路(Anlogic)FPGA 实验平台&#xff1a;米联客-MLK-L1-CZ06-DR1M90G开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 ht…

【5G Sub-6GHz模块】专为IoT/eMBB应用而设计的RG520NNA、RG520FEB、RG530FNA、RG500LEU 5G模组

推出全新的5G系列模组&#xff1a; RG520NNADB-M28-SGASA RG520NNADA-M20-SGASA RG520FEBDE-M28-TA0AA RG530FNAEA-M28-SGASA RG530FNAEA-M28-TA0AA RG500LEUAA-M28-TA0AA ——明佳达 1、5G RG520N 系列——专为IoT/eMBB应用而设计的LGA封装模块 RG520N 系列是一款专为 IoT…

Ghost Browser指纹浏览器集成IPXProxy代理IP:解锁Twitch直播新体验

​Twitch 是一个实时视频流平台&#xff0c;允许人们实时播放各自的内容&#xff0c;无论是游戏、娱乐、体育、音乐还是其他内容。不少人的人都想要在Twitch直播来吸引更多的粉丝&#xff0c;然而有时候会面临无法成功使用Twitch的问题。本文将带来Ghost Browser指纹浏览器集成…

网络概念: 互联网和局域网、 OSI七层网络互联模型、数据封装、应用端口、地址解析、网络设备、网络配置

文章目录 引言I 网络概念1.1 互联网和局域网1.2 OSI七层网络互联模型1.3 数据封装1.4 TCP/IP协议1.5 应用端口II 地址解析III 网络设备3.1 集线器 HUB3.2 交换机 swich3.3 路由器 router3.4 防火墙 firewallIV 网络配置4.1 网络安全域(你住哪里?)4.2 地址转换(NAT,你名字叫…

Go 1.19.4 函数-Day 08

1. 函数概念和调用原理 1.1 基本介绍 函数是基本的代码块&#xff0c;用于执行一个任务。 Go 语言最少有个 main() 函数。 你可以通过函数来划分不同功能&#xff0c;逻辑上每个函数执行的是指定的任务。 函数声明告诉了编译器函数的名称&#xff0c;返回类型&#xff0c;和参…

Windows与Ubuntu安装ffmpeg

文章目录 前言ffmpeg的简介安装ffmpegWindows下载设置环境变量 Ubuntu 总结 前言 FFmpeg是一款非常强大的开源音视频处理工具&#xff0c;它包含了众多的音视频编解码库&#xff0c;可以用于音视频的采集、编解码、转码、流化、过滤和播放等复杂的处理。在Windows系统上安装FF…

一五六、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…

Redis中数据分片与分片策略

概述 数据分片是一种将数据分割并存储在多个节点上的技术&#xff0c;可以有效提高系统的扩展性和性能。在Redis中&#xff0c;数据分片主要用于解决单个实例存储容量和性能瓶颈的问题。通过将数据分散存储到多个Redis节点中&#xff0c;可以将负载均衡到不同的服务器上&#…

如何使用在线工具将手机相册中的图片转换为JPG格式

我们经常在手机相册中保存大量的图片&#xff0c;无论是家庭聚会的照片还是旅行的瞬间&#xff0c;每一幅图像都承载着珍贵的记忆。然而&#xff0c;有时候我们会遇到图片格式不兼容的问题&#xff0c;尤其是在需要将图片分享到特定平台或编辑时。 例如&#xff0c;某些社交平台…

手机操作系统的沉浮往事

手机操作系统的沉浮往事&#xff08;上&#xff09; 移动终端操作系统&#xff0c;也就是指手机、平板电脑等设备所使用的操作系统。 在移动互联网高度发达的今天&#xff0c;我们使用移动终端操作系统的时长&#xff0c;可能已经远远超过了 Windows 等桌面操作系统。 那么&…

Windows 11预览补丁KB5040527影响火绒驱动加载的解决办法

7 月 11 日&#xff0c;微软更新Windows 11 预览版本补丁 KB5040527&#xff0c;补丁安装后会影响火绒驱动加载导致火绒安全软件服务异常&#xff0c;补丁相关信息如下&#xff1a; https://blogs.windows.com/windows-insider/2024/07/11/releasing-windows-11-builds-22621-…

Apache trino的ldap认证开启

作者&#xff1a;櫰木 1、背景 由于trino 默认没有开启用户认证体系&#xff0c;需要ldap用户进行认证。开启tls和ldap用户认证&#xff0c;提高安全性。 2、配置 前置条件。 trino 集群已经部署完成 ldap 服务 openjdk 版本大于11.0.17 生成证书 keytool -genkeypair…

Python(集合)

集合中只能存储不可变类型&#xff0c;使用{}定义&#xff0c;元素之间用逗号分隔 #集合的使用 #方法1 s{10,20,30,40} #整数是不可变数据类型,不能存储列表 print(s) #方法2 sset()#创建了一个空集合 print(s) s{}#创建了一个字典 print(s,type(s))sset(helloworld) print(s)…

UNiapp微信小程序Ucharts

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

镜舟科技荣获优秀数字化服务商奖,助力企业用数智技术重塑新消费

7 月 13 日&#xff0c;由 ITShare智享会和 BT商业科技观察主办的2024 第八届 FMCG 零售消费品数字化峰会于上海落幕。在现场&#xff0c;镜舟科技凭借在多家零售企业构建与实施智能数据中台解决方案的成功经验&#xff0c;荣获优秀数字化服务商奖项。 在会上&#xff0c;麦当劳…

软件测试——web单功能测试

工作职责&#xff1a; 1.负责产品系统测试&#xff0c;包括功能测试、性能测试、稳定性测试、用户场景测试、可靠性测试等。 2.负责测试相关文档的编写&#xff0c;包括测试计划、测试用例、测试报告等。 3.负责自动化测试框架、用例的维护。 岗位要求&#xff1a; 1.熟练…

无编码器多模态大模型EVE:原生多模态新方案

近期&#xff0c;关于多模态大模型的研究如火如荼&#xff0c;工业界的投入也愈发高涨。国外相继推出了炙手可热的模型&#xff0c;例如 GPT-4o &#xff08;OpenAI&#xff09;、Gemini&#xff08;Google&#xff09;、Phi-3V &#xff08;Microsoft&#xff09;、Claude-3V&…

SpringMVC框架--个人笔记步骤总结

一、步骤 1.创建工程 2.加入springmvc依赖--pom.xml <!--springmvc依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.10.RELEASE</version> </depend…