《学会 SpringBoot · 定制 SpringMVC》

news2024/9/20 22:29:17

📢 大家好,我是 【战神刘玉栋】,有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

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

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

相关文章

Pytorch学习笔记day1—— 安装教程

这里写自定义目录标题 Pytorch安装方式 工作需要&#xff0c;最近开始搞一点AI的事情。但是这个国产的AI框架&#xff0c;实话说对初学者不太友好 https://www.mindspore.cn/ 比如说它不支持win下的CUDA&#xff0c;可是我手里只有3070Ti和4060也不太可能自己去买昇腾就有点绷不…

C语言 | Leetcode C语言题解之第239题滑动窗口最大值

题目&#xff1a; 题解&#xff1a; int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {int prefixMax[numsSize], suffixMax[numsSize];for (int i 0; i < numsSize; i) {if (i % k 0) {prefixMax[i] nums[i];} else {prefixMax[i] fmax(pref…

C++深度解析教程笔记9-静态成员变量,静态成员函数,二阶构造,友元,函数重载,操作符重载

C深度解析教程笔记9 第25课 - 类的静态成员变量实验-数对象个数&#xff08;失败&#xff09;实验-静态变量小结 第26课 - 类的静态成员函数实验-修改对象的静态变量数值实验-利用静态成员函数实验-静态变量静态函数实现统计对象个数小结 第27课 - 二阶构造模式实验-初始化是否…

【JavaEE】HTTP(2)

&#x1f921;&#x1f921;&#x1f921;个人主页&#x1f921;&#x1f921;&#x1f921; &#x1f921;&#x1f921;&#x1f921;JavaEE专栏&#x1f921;&#x1f921;&#x1f921; &#x1f921;&#x1f921;&#x1f921;下一篇文章&#xff1a;【JavaEE】HTTP协议(…

C++——类和对象(下)

文章目录 一、再探构造函数——初始化列表二、 类型转换三、static成员静态成员变量静态成员函数 四、 友元友元函数友元类 五、内部类六、匿名对象 一、再探构造函数——初始化列表 之前我们实现构造函数时&#xff0c;初始化成员变量主要使⽤函数体内赋值&#xff0c;构造函…

【读点论文】ASAM: Boosting Segment Anything Model with Adversarial Tuning,对抗学习提升性能

ASAM: Boosting Segment Anything Model with Adversarial Tuning Abstract 在不断发展的计算机视觉领域&#xff0c;基础模型已成为关键工具&#xff0c;对各种任务表现出卓越的适应性。其中&#xff0c;Meta AI 的 Segment Anything Model (SAM) 在图像分割方面表现突出。然…

第十一届MathorCup高校数学建模挑战赛-C题:海底数据中心的散热优化设计(续)(附MATLAB代码实现)

目录 5.3 问题三的求解 5.3.1 数据分析 5.3.2 数据处理 5.3.4 得出结论 5.4 问题四的求解 5.4.1 数据分析 5.4.2 算法分析 5.5 问题五的求解 六、模型评价与推广 6.1 模型的优点 6.2 模型的缺点 6.3 模型的推广 七、参考文献 代码实现 8.1 图 4 的代码 8.2 图 5 的代码 8.3 图…

旗晟巡检机器人的应用场景有哪些?

巡检机器人作为现代科技的杰出成果&#xff0c;已广泛应用于各个关键场景。从危险的工业现场到至关重要的基础设施&#xff0c;它们的身影无处不在。它们以精准、高效、不知疲倦的特性&#xff0c;担当起保障生产、守护安全的重任&#xff0c;为行业发展注入新的活力。那么&…

VMware安装CentOS 7

在虚拟机中安装无论是Windows还是Linux其实都差不多&#xff0c;主要还是需要熟悉VMware的使用&#xff0c;多新增几次就熟悉了&#xff0c;可以反复删除再新增去练习… 如下是安装CentOS 7 安装过程&#xff1a; VMare Workstation 16 PRO 中安装CentOS 7 CentOS 7 下载推荐…

PTA - 嵌套列表求和

使用递归函数对嵌套列表求和 函数接口定义&#xff1a; def sumtree(L) L是输入的嵌套列表。 裁判测试程序样例&#xff1a; /* 请在这里填写答案 */L eval(input()) print(sumtree(L)) # 调用函数 输入样例&#xff1a; 在这里给出一组输入。例如&#xff1a; [1,[2…

数据结构-C语言-排序(1)

代码位置&#xff1a;test-c-2024: 对C语言习题代码的练习 (gitee.com) 一、前言&#xff1a; 1.1-排序定义&#xff1a; 排序就是将一组杂乱无章的数据按照一定的规律&#xff08;升序或降序&#xff09;组织起来。 1.2-排序分类&#xff1a; 常见的排序算法&#xff1a; 插…

业务终端动态分配IP-DHCP技术、DHCP中继技术

一、为什么需要DHCP? 1、许多设备(主机、无线WiFi终端等)需要动态地址的分配; 2、人工手工配置任务繁琐、容易出错,比如:IP地址冲突; 3、网络规模扩大、复杂度提高,网络配置越来越复杂,计算机的位置变化和数量超过可分配IP地址的数量,造成IP地址变法频繁以及IP地址…

【精品资料】大数据可视化平台数据治理方案(626页WORD)

引言&#xff1a;大数据可视化平台的数据治理方案是一个综合性的策略&#xff0c;旨在确保大数据的质量、安全性、可访问性和合规性&#xff0c;从而支持高效的数据分析和可视化过程。 方案介绍&#xff1a; 大数据可视化平台的数据治理方案是一个综合性的策略&#xff0c;旨在…

SimMIM:一个类BERT的计算机视觉的预训练框架

1、前言 呃…好久没有写博客了&#xff0c;主要是最近时间比较少。今天来做一期视频博客的内容。本文主要讲SimMIM&#xff0c;它是一个将计算机视觉&#xff08;图像&#xff09;进行自监督训练的框架。 原论文&#xff1a;SimMIM&#xff1a;用于掩码图像建模的简单框架 (a…

设计模式-UML类图

1.UML概述 UML-统一建模语言&#xff0c;用来设计软件的可视化建模语言&#xff1b; 1.1 UML分类 静态结构图&#xff1a;类图、对象图、组件图、部署图动态行为图&#xff1a;状态图、活动图、时序图、协作图、构件图等 类图&#xff1a;反应类与类结构之间的关系&#xff0…

【46 Pandas+Pyecharts | 当当网畅销图书榜单数据分析可视化】

文章目录 &#x1f3f3;️‍&#x1f308; 1. 导入模块&#x1f3f3;️‍&#x1f308; 2. Pandas数据处理2.1 读取数据2.2 查看数据信息2.3 去除重复数据2.4 书名处理2.5 提取年份 &#x1f3f3;️‍&#x1f308; 3. Pyecharts数据可视化3.1 作者图书数量分布3.2 图书出版年份…

2-36 基于matlab的流行学习算法程序

基于matlab的流行学习算法程序。通过GUI的形式将MDS、PCA、ISOMAP、LLE、Hessian LLE、Laplacian、Dissusion MAP、LTSA八种算法。程序以可视化界面进行展示&#xff0c;可直接调用进行分析。多种案例举例说明八种方法优劣&#xff0c;并且可设置自己数据进行分析。程序已调通&…

podman 替代 docker ? centos Stream 10 已经弃用docker,开始用podman了!

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

前端vue项目打镜像并拉取镜像包

前端vue项目打镜像并拉取镜像包 如图需要准备三部分的内容 1.前置要求 linux 环境 docker环境2.vue打包后的静态文件&#xff0c;需要自行打包 npm run build 打包后上传到服务器3.nginx配置&#xff08;default.conf文件配置&#xff09; server {listen 80;serve…

浅谈Visual Studio 2022

Visual Studio 2022&#xff08;VS2022&#xff09;提供了众多强大的功能和改进&#xff0c;旨在提高开发者的效率和体验。以下是一些关键功能的概述&#xff1a;12 64位支持&#xff1a;VS2022的64位版本不再受内存限制困扰&#xff0c;主devenv.exe进程不再局限于4GB&#xf…