Springboot2.x升级到3.x的经验分享
升级原因
随着Spring 各种漏洞的更新发布,springboot升级到3.x迫在眉睫。
2.x升级到3.x是一次大的跨越,以下内容是升级时需要注意的一些地方。
JDK的变化
Spring Boot 3.x 需要 Java 17或更高版本,下载地址Java Archive Downloads,根据自己的系统版本选择下载即可。
Oracle JDK版本说明:
JDK8 之前版本,仍然免费。
JDK8 免费版本到 8u202,从 8u211版本开始收费。
JDK9-10,全版本免费。
JDK11,免费版本到 11.0.2,从 11.0.3 版本开始商用收费。
JDK12-16,全版本商用收费。
JDK17-20,全版本(二进制版本)免费。
特别提醒:
JDK17之后的版本可以免费分发和商用,但是仅有3年时间,3年后无法免费商用。
建议使用OpenJDK是一个最常见的选择,因为它完全免费且开源,能够满足大多数开发需求。
OpenJDK下载地址:OpenJDK Downloads
Linux系统同时安装多个版本的JDK,切换方法如下:
# 需要去除已经配置好的jdk环境变量,通常配置在类似以下文件中
/etc/profile.d/jdk.sh
/etc/profile
# 解压安装包到指定目录
tar -xvf openjdk-21_linux-x64_bin.tar.gz -C /usr/local
# 安装Java环境(末尾参数指优先级,数字越大优先级越高)
update-alternatives --install /usr/bin/java java /usr/local/openjdk-8/bin/java 1
update-alternatives --install /usr/bin/java java /usr/local/openjdk-21/bin/java 2
# 配置Java环境
update-alternatives --config java
# 添加执行权限
chmod +x /usr/bin/java
# 查看Java版本
java -version
# 卸载Java环境
update-alternatives --remove java /usr/local/openjdk-8/bin/java
配置变化
Redis配置变化
Redis配置对比2.x版本,在spring下多了一个data层级,如下所示:
日志配置变化
日志配置主要是日志的大小和保存历史等配置项和2.x版本不同,3.x版本增加了logback节点,如下所示:
代码变化
更新依赖包
<!-- java.version升级为21 -->
<java.version>21</java.version>
<!-- spring-boot版本升级为3.3.5 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.3.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 阿里数据库连接池修改boot-3 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.21</version>
</dependency>
<!-- mysql的依赖替换成 mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
</dependency>
<!-- SpringBoot3.x使用了Jakarta EE的标准,修改servlet包,原包是javax.servlet -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0/version>
</dependency>
<!-- JAXB来处理XML数据,jaxb在Java 9及更高版本中不再默认包含,需要手动添加相关依赖 -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
替换druid.spring.boot3的包
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
javax.servlet包替换为jakarta.servlet
涉及的类包括ServletRequest、HttpServletRequest、ServletResponse、HttpServletResponse等,如下图所示:
javax.validation包替换为jakarta.validation
涉及的注解包括@Valid、@NotNUll、@NotBlank、@NotEmpty
等。
其他javax相关的包替换
javax.persistence.* -> jakarta.persistence.*
javax.annotation.* -> jakarta.annotation.*
javax.transaction.* -> jakarta.transaction.*
支持跨域设置addAllowedOriginPattern
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfig = new CorsConfiguration();
// 是否允许请求带有验证信息
corsConfig.setAllowCredentials(true);
// 设置访问源地址
//corsConfig.addAllowedOrigin("*");
corsConfig.addAllowedOriginPattern("*");
// 设置访问源请求头
corsConfig.addAllowedHeader("*");
// 设置访问源请求方法:post,get,put,delete
corsConfig.addAllowedMethod("*");
// 对接口配置跨域设置
source.registerCorsConfiguration("/**", corsConfig);
return new CorsFilter(source);
}
}
Spring MVC 和 WebFlux的URL匹配更改
从 Spring Framework 6.0 开始,尾部斜杠匹配配置选项已为 deprecated,其默认值设置为 false。
简单来说,就是比如以前@GetMapping("/list") 这样的控制器,可以通过 /list 和 /list/ 两个路径都能访问到,现在只能通过 /list 访问。
调用python接口的开发者要注意,因为python的开发者习惯在路径最后加个斜杠,java的开发者几乎不会去加,如果调用接口的时候默认都在最后加了斜杠的话,那么springboot升级后,后台将会报请求的资源路径不存在的错误。部署之前需要前端排查一下,要么后端加上包含最后有斜杠的路由,要么前端去掉斜杠后访问接口。
mybatis-plus依赖包替换
如果使用了mybatis-plus的话需要将mybatis-plus-boot-starter
依赖包替换成mybatis-plus-spring-boot3-starter
,如果用到了代码生成器mybatis-plus-generator的话,还要同时升级代码生成器的依赖包,我这里升级到3.5.9版本,升级后如下:
<!-- mybatis plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.9</version>
</dependency>
<!-- mybatis plus 代码生成器依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.9</version>
</dependency>
<!-- MyBatis-Plus和PageHelper都同时使用到一个jsqlparser的依赖库,然后,PageHelper要求使用的版本与jsqlparser版本有一个对应关系。 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>3.5.9</version>
</dependency>
<!-- 升级完MyBatis-Plus之后。PageHelper就不能用了。PageHelper目前也是用的最新的 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>2.1.0</version>
<exclusions>
<!-- 由于版本冲突,已单独引用,这里需要排除掉 -->
<exclusion>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
</exclusion>
<!-- 由于mybatis-plus-spring-boot3-starter中包含了以下依赖,且版本不同,这里也需要排除掉 -->
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
启用 -parameters 编译器标志
使用最新版的 Springboot 3 搭建环境进行开发,调用接口时出现奇怪的错。报错主要信息如下:
Name for argument of type [java.lang.String] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the ‘-parameters’ flag.
新版本调整了参数,而spring会自动帮助设置,导致编译时选项“-参数”被禁用。也就是错误信息中的最后提示:Ensure that the compiler uses the '-parameters' flag.
在spring6中,默认情况下Java编译时,不会保留方法参数的名称,这是为了减小生成的字节码文件的大小;为了可以使用忽略@RequestParam
注解的效果,这里需要加上编译选项-parameters
如果不配置,则在使用@PathVariale("id")
注解时必须带参数名,get接口定义参数必须使用注解@RequestParam(value = "name", required = false)
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- 注意与本地Maven版本是否兼容,3.12.1->maven3.6.2, 3.13.0->maven3.6.3 -->
<version>3.12.1</version>
<configuration>
<!-- 启用 -parameters 编译器标志 -->
<parameters>true</parameters>
<source>21</source>
<target>21</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
-parameters 是一个编译器参数,它的作用是在编译 Java 源代码时保留方法参数的名称。这对于反射和某些框架(如 Spring)非常有用,它们需要通过参数名称来进行操作。
修改SecurityConfig配置类
点击查看代码
/**
* spring security配置
*/
@EnableMethodSecurity(securedEnabled = true)
@Configuration
public class SecurityConfig {
/**
* 自定义用户认证逻辑
*/
@Autowired
private UserDetailsService userDetailsService;
/**
* 认证失败处理类
*/
@Autowired
private AuthenticationEntryPointImpl unauthorizedHandler;
/**
* 退出处理类
*/
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;
/**
* token认证过滤器
*/
@Autowired
private JwtAuthenticationTokenFilter authenticationTokenFilter;
/**
* 跨域过滤器
*/
@Autowired
private CorsFilter corsFilter;
/**
* 允许匿名访问的地址
*/
@Autowired
private PermitAllUrlProperties permitAllUrl;
/**
* @return
* @throws Exception
*/
@Bean
AuthenticationManager authenticationManager() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
// 身份认证接口
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());
return new ProviderManager(daoAuthenticationProvider);
}
/**
* anyRequest | 匹配所有请求路径
* access | SpringEl表达式结果为true时可以访问
* anonymous | 匿名可以访问
* denyAll | 用户不能访问
* fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录)
* hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问
* hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问
* hasAuthority | 如果有参数,参数表示权限,则其权限可以访问
* hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
* hasRole | 如果有参数,参数表示角色,则其角色可以访问
* permitAll | 用户可以任意访问
* rememberMe | 允许通过remember-me登录的用户访问
* authenticated | 用户登录后可访问
*/
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity
// CSRF禁用,因为不使用session
.csrf(AbstractHttpConfigurer::disable)
// 禁用HTTP响应标头
.headers(header -> header.cacheControl(HeadersConfigurer.CacheControlConfig::disable).frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
// 认证失败处理类
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
// 基于token,所以不需要session
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 注解标记允许匿名访问的url
.authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.requestMatchers("/login", "/register", "/captchaImage").permitAll()
// 静态资源,可匿名访问
.requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll()
.requestMatchers("/*/api-docs/**", "/*/doc.html", "/druid/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})
// 添加Logout filter
.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
// 添加JWT filter
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
// 添加CORS filter
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
.addFilterBefore(corsFilter, LogoutFilter.class).build();
}
/**
* 强散列哈希加密实现
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
执行cmd命令报错
在升级JDK21后,程序中有使用到Runtime.getRuntime().exec("cmd"),执行报错:java.io.IOException: Cannot run program "bash": error=0
在启动Java程序时,将以下参数添加到命令行中:
java -Djdk.lang.Process.launchMechanism=vfork app.jar
1.这个配置项是针对Java程序的一个内部属性,用于指定Process类使用的创建子进程的机制。用于创建子进程,它与常用的fork系统调用类似;
2.在一般情况下,你不需要手动设置这个属性,因为Java会根据不同的操作系统自动选择合适的机制。如果你在特定的操作系统和Java版本下遇到与Process相关的问题,并且你确认是由于vfork机制引起的,那么你可以通过在Java启动时添加以下参数来设置这个属性;
3.请注意vfork是一个较老的系统调用,可能在某些操作系统或者Java版本中已经被弃用或不支持。
循环依赖问题
自SpringBoot2.6.0版本开始默认禁止了循环依赖,建议开发者自己写代码的时候去减少不必要的互相依赖。如果程序中出现循环依赖就会报错。
解决方案:
1、开启循环依赖(不推荐)
方式1:在全局配置文件设置允许循环引用存在
spring:
main:
allow-circular-references:true #允许循环引用
方式2:在SpringApplicationBuilder 添加设置允许循环引用
public static void main(String[] args) {
new SpringApplicationBuilder(DemoApplication.class).allowCircularReferences(true).run(args);
}
新项目不推荐此配置,主要针对旧项目升级用此配置处理比较方便快捷。
不推荐原因:
性能开销:Spring 会使用代理对象来解决循环依赖的问题。这种方式会增加额外的性能开销和复杂性。
潜在问题:循环依赖可能导致无限递归、死锁等问题,这些问题可能会影响系统的稳定性和性能。
设计缺陷:循环依赖通常是由于设计上的缺陷导致的,我们不应该过度依赖 Spring 而忽视了编码的规范和质量。
2、懒加载
@Lazy:配合使用该注解可以解决循环依赖问题(在需要注入Bean的地方加上该注解)
@Lazy // 使用懒加载
@Autowired
private OneService oneService;
@Lazy 不能解决构造函数循环依赖。
@Lazy 可以推迟 Bean 的加载,但无法解决循环依赖的根本问题。
过度使用@Lazy会隐藏真正的初始化顺序和依赖问题,使得调试变得困难。
3、控制反转
每次使用的时候就去Bean工厂里去获取,这样就不存在循环依赖了
@Service
@RequiredArgsConstructor // 该注解的使用在下面会有介绍和说明
public class OneServiceImpl implements OneService {
private final ConfigurableListableBeanFactory beanFactory;
//代替循环依赖
public TwoService getTwoService(){
return beanFactory.getBean(TwoService.class);
}
}
@RequiredArgsConstructor使用说明
@RequiredArgsConstructor:该注解是由Lombok提供,可以解决掉大量重复的@Autowired代码
注意:使用@RequiredArgsConstructor时,需要使用final关键字
写在类上可以代替@Autowired注解,需要注意的是在注入时需要用final定义,或者使用@notnull注解
@RestController
@RequiredArgsConstructor // 代替@Autowired注解
@RequestMapping("/api/v1/one")
public class OneController{
private final OneService oneService; // 需要final关键字
private final TwoService twoService; // 需要final关键字
@GetMapping("{id}")
public ResultVo<String> getDetails(@PathVariable("id") Long id){
return ResultVo.ok(oneService.getDetailsById(id));
}
}
注意点:
1、必须声明的变量为final。
2、根据构造器注入的,相当于容器调用带有一组带有参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖。基于构造方法为属性赋值,容器通过调用类的构造方法将其进行依赖注入。
3、当需要注入Bean的时候可以直接在类名称上使用@RequiredArgsConstructor,从而代替了大量的@Autowrited注解。
自动配置包位置变化
-
SpringBoot2.x:
META-INF/spring.factories
-
SpringBoot3.x:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
集成Knife4j-4.5.0
SpringBoot升级至3.x后,Knife4j进行同步升级(Spring Boot 3 只支持OpenAPI3规范),需要把knife4j-spring-boot-starter
依赖替换成knife4j-openapi3-jakarta-spring-boot-starter
,以下是升级过程与注意事项等。
更新依赖包
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
YML配置
# springdoc配置
springdoc:
# get请求多个参数时不需要添加额外的@ParameterObject注解
default-flat-param-object: true
api-docs:
# 线上禁用,配合knife4j.enable=false一起使用才有效果
enabled: true
# Knife4j配置
knife4j:
enable: true
setting:
language: zh_cn
Knife4jConfig配置类
@Configuration
public class Knife4jConfig
{
/**
* 自定义分组
*/
@Bean
public GroupedOpenApi api(){
return GroupedOpenApi.builder()
.group("api")
.displayName("业务接口")
// 指定包分组
// .packagesToScan("com.cn.web.controller.common", "com.cn.web.controller.editor")
// 扫描使用@Operation注解的接口
.addOpenApiMethodFilter(method -> method.isAnnotationPresent(io.swagger.v3.oas.annotations.Operation.class))
.build();
}
/**
* 创建API
*/
@Bean
public OpenAPI customOpenApi()
{
return new OpenAPI()
.components(new Components().addSecuritySchemes(HttpHeaders.AUTHORIZATION, securityScheme()))
.addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION))
.info(getApiInfo());
}
/**
* 安全模式,这里指定token通过Authorization头请求头传递
*/
@Bean
public SecurityScheme securityScheme()
{
return new SecurityScheme()
.type(SecurityScheme.Type.APIKEY)
.name("Authorization")
.in(SecurityScheme.In.HEADER)
.scheme("Bearer");
}
/**
* 添加摘要信息
*/
private Info getApiInfo()
{
return new Info()
// 设置标题
.title("XXX服务_接口文档")
// 描述
.description("...")
// 作者信息
.contact(new Contact().name("hviger"))
// 版本
.version("1.0");
}
}
添加全局请求头
@Component
public class Knife4jOperationCustomizer implements GlobalOperationCustomizer {
@Override
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
List<SecurityRequirement> security = operation.getSecurity();
if (security == null) {
security = List.of(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION));
operation.setSecurity(security);
}
return operation;
}
}
注解替换
swagger 3 注释的包是io.swagger.v3.oas.annotations
1、原生注解更新
# Controller注解更新
@Api → @Tag
@ApiSort → @ApiSupport
# 类接口注解更新
@ApiIgnore→@Parameter(hidden = true)或@Operation(hidden = true)或@Hidden
@ApiImplicitParam → @Parameter
@ApiImplicitParams → @Parameters
@ApiOperation(value = "foo", notes = "bar") → @Operation(summary = "foo", description = "bar")
@ApiResponse(code = 404, message = "foo") → @ApiResponse(responseCode = "404", description = "foo")
# 实体类注解更新
@ApiModel → @Schema
@ApiModelProperty(hidden = true) → @Schema(accessMode = READ_ONLY)
@ApiModelProperty → @Schema
@ApiParam → @Parameter
2、全局替换示例
## 全局替换原有注解
@Api(tags
->
@Tag(name
@ApiSort(
->
@ApiSupport(order =
, dataType = "Integer", dataTypeClass = Integer.class
->
, in = ParameterIn.DEFAULT
, dataType = "String", dataTypeClass = String.class
->
, in = ParameterIn.DEFAULT
, paramType = "path", in = ParameterIn.DEFAULT
, paramType = "path", dataType = "Integer", dataTypeClass = Integer.class
->
, in = ParameterIn.PATH
, dataType = "Date", dataTypeClass = Date.class
->
@ApiOperation(value
->
@Operation(summary
@ApiImplicitParams
->
@Parameters
@ApiModel(value | @ApiModelProperty(value
->
@Schema(name | @Schema(description
required = true | required = false (限定为entity或vo等实体类包进行更换)
->
requiredMode = Schema.RequiredMode.REQUIRED
requiredMode = Schema.RequiredMode.NOT_REQUIRED
## javax注解更改(jakarta)
import javax.xxx;
->
import jakarta.xxx;
集成Nacos
更新依赖包
如果你的springboot版本是3.2.x及以上,请添加cloud的包,nacos服务端需要安装最新版本2.4.3
<!--nacos配置中心-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2023.0.1.2</version>
</dependency>
端口授权问题
在Nacos 2.x版本中,默认使用的端口为8848(HTTP管理端口)、9848(客户端gRPC请求服务端端口)和9849(服务端gRPC请求服务端端口)。
客户端在连接时,虽然主要配置的是管理端访问端口8848,但实际上客户端会根据服务端的配置自动计算其他端口进行通信。
Nacos服务端的主端口由server.port指定,gRPC端口为(主端口+1000)。
通过修改Nacos的配置文件application.properties来实现:
server.port=8848
# 配置gRPC端口的偏移量
nacos.server.grpc.port.offset=1
SpringBoot3的新特性
-
支持 Java 17
Spring Boot 3 要求最低 Java 17,这意味着开发者可以利用 Java 17 的新特性,如密封类(Sealed Classes)、模式匹配等。 -
支持 Jakarta EE 9
Spring Boot 3 升级到了 Jakarta EE 9,将 javax.* 命名空间迁移到了 jakarta.*。这一变化影响了与 Jakarta EE 相关的依赖,如 JPA、Servlet 等。 -
GraalVM 原生镜像支持
Spring Boot 3 增强了对 GraalVM 原生镜像的支持,使得 Spring 应用可以编译为原生可执行文件,从而显著提升启动速度和减少内存占用。 -
改进的 Micrometer 支持
Micrometer 是 Spring Boot 的指标收集库,Spring Boot 3 进一步增强了与 Micrometer 的集成,提供了更多的开箱即用的指标收集功能。 -
新的 AOT(Ahead-of-Time)编译支持
Spring Boot 3 引入了对 AOT 编译的支持,允许在应用启动前进行部分编译,从而优化启动时间和运行时性能。 -
增强的安全特性
Spring Boot 3 引入了更多的安全特性,包括对 OAuth2 和 OpenID Connect 的改进支持,以及对 Spring Security 6 的全面支持。 -
改进的依赖管理
Spring Boot 3 进一步优化了依赖管理,减少了不必要的依赖冲突,并提供了更清晰的依赖树。 -
新的 Starter 和自动配置
Spring Boot 3 引入了新的 Starter 和自动配置,简化了与第三方库的集成,如 Kafka、Cassandra 等。 -
改进的测试支持
Spring Boot 3 增强了测试支持,提供了更多的测试工具和注解,使得编写和运行测试更加方便。 -
更好的可观测性
Spring Boot 3 提供了更好的可观测性支持,包括对分布式追踪、日志记录和指标收集的改进。 -
Kotlin 1.7 支持
Spring Boot 3 支持 Kotlin 1.7,允许开发者使用 Kotlin 的最新特性来编写 Spring 应用。 -
改进的错误处理和日志记录
Spring Boot 3 改进了错误处理和日志记录机制,使得调试和排查问题更加方便。 -
新的配置属性
Spring Boot 3 引入了许多新的配置属性,允许开发者更灵活地配置应用行为。 -
弃用和移除的功能
Spring Boot 3 弃用了一些过时的功能,并移除了一些不再维护的依赖,以保持代码库的简洁和现代性。 -
改进的文档和示例
Spring Boot 3 提供了更详细的文档和示例,帮助开发者更快地上手和掌握新特性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步