若依(RuoYi)系统升级Spring Boot 3实战指南
前言
Spring Boot 3于2022年11月正式发布,带来了诸多新特性和改进,包括对Java 17+的强制要求、全新的Spring Security 6配置方式、以及更好的原生编译支持等。本文将详细介绍如何将若依(RuoYi)系统从Spring Boot 2.x升级到3.x版本。
环境准备
- JDK 17或更高版本
- Maven 3.5+ 或 Gradle 7.x+
- 若依版本:4.7.6
一、POM依赖升级
1.1 更新Spring Boot版本
首先需要更新主POM文件中的Spring Boot版本:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>1.2 更新相关依赖版本
需要更新以下关键依赖的版本:
<properties>
<reqm.version>0.2</reqm.version>
<springBoot.version>3.1.2</springBoot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<bitwalker.version>1.21</bitwalker.version>
<mybatis-spring-boot.version>3.0.3</mybatis-spring-boot.version>
<pagehelper.boot.version>2.1.0</pagehelper.boot.version>
<fastjson.version>1.2.83</fastjson.version>
<commons.io.version>2.11.0</commons.io.version>
<commons.fileupload.version>1.5</commons.fileupload.version>
<commons.collections.version>3.2.2</commons.collections.version>
<poi.version>5.2.3</poi.version>
<jwt.version>0.12.0</jwt.version>
<snakeyaml.version>2.4</snakeyaml.version>
<knife4j.version>4.5.0</knife4j.version>
<lombok.version>1.18.28</lombok.version>
<hutool.version>5.8.11</hutool.version>
<guava.version>32.0.1-jre</guava.version>
<dm.version>8.1.1.49</dm.version>
<httpclient.version>4.5.14</httpclient.version>
<jakarta.servlet-api.version>6.0.0</jakarta.servlet-api.version>
</properties>
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- yml解析器 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springBoot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 解析客户端操作系统、浏览器等 -->
<dependency>
<groupId>eu.bitwalker</groupId>
<artifactId>UserAgentUtils</artifactId>
<version>${bitwalker.version}</version>
</dependency>
<!-- SpringBoot集成mybatis框架 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot.version}</version>
</dependency>
<!-- page helper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>${pagehelper.boot.version}</version>
</dependency>
<!-- swagger -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!--io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons.io.version}</version>
</dependency>
<!--文件上传工具类 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons.fileupload.version}</version>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!-- collections工具类 -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons.collections.version}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!--Token生成与解析-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>${jakarta.servlet-api.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>二、代码适配与修改
2.1 更新Jakarta EE包名替换
全局替换javax为jakarta:
javax.servlet→jakarta.servletjavax.persistence→jakarta.persistencejavax.annotation→jakarta.annotation
2.2 Security配置更新
Spring Security 6对配置方式进行了重大调整,以下是关键修改:
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
@Configuration
public class SecurityConfig {
/**
* 自定义用户认证逻辑
*/
@Resource
private UserDetailsService userDetailsService;
/**
* 认证失败处理类
*/
@Resource
private AuthenticationEntryPointImpl unauthorizedHandler;
/**
* 退出处理类
*/
@Resource
private LogoutSuccessHandlerImpl logoutSuccessHandler;
/**
* token认证过滤器
*/
@Resource
private JwtAuthenticationTokenFilter authenticationTokenFilter;
/**
* 跨域过滤器
*/
@Resource
private CorsFilter corsFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// CSRF禁用,因为不使用session
.csrf(AbstractHttpConfigurer::disable)
// 禁用HTTP响应标头
.headers(conf -> conf.cacheControl(HeadersConfigurer.CacheControlConfig::disable))
// 认证失败处理类
.exceptionHandling(conf -> conf.authenticationEntryPoint(unauthorizedHandler))
// 基于token,所以不需要session
.sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// 过滤请求
.authorizeHttpRequests(conf ->
conf.requestMatchers(toAntMatcher("/login", "/register", "/captchaImage")).anonymous()
.requestMatchers(toAntMatcher(HttpMethod.GET,
"/",
"/*.html",
"/*/*.html",
"/*/*.css",
"/*/*.js",
"/profile/*")).permitAll()
.requestMatchers(toAntMatcher("/v3/*", "/doc.html", "/swagger-ui/", "/swagger-resources/*", "/webjars/*", "/*/api-docs")).permitAll()
.anyRequest().authenticated()
)
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.logout(conf -> conf
.logoutUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler)
);
httpSecurity
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
.addFilterBefore(corsFilter, LogoutFilter.class);
return httpSecurity.build();
}
/**
* 强散列哈希加密实现
*/
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 将原来的ulr字符串转为AntPathRequestMatcher
* @param str url链接
* @return AntPathRequestMatcher[]
*/
private AntPathRequestMatcher[] toAntMatcher(String...str) {
List<AntPathRequestMatcher> list = new ArrayList<>();
for (String item : str) {
list.add(AntPathRequestMatcher.antMatcher(item));
}
return list.toArray(new AntPathRequestMatcher[0]);
}
/**
* 将原来的ulr字符串转为AntPathRequestMatcher
* @param method
* @param str url链接
* @return AntPathRequestMatcher[]
*/
private AntPathRequestMatcher[] toAntMatcher(HttpMethod method,String...str) {
List<AntPathRequestMatcher> list = new ArrayList<>();
for (String item : str) {
list.add(AntPathRequestMatcher.antMatcher(method,item));
}
return list.toArray(new AntPathRequestMatcher[0]);
}
@Bean
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder);
return new ProviderManager(provider);
}
}2.3 JWT相关修改
由于JJWT库的API变化,需要更新JWT工具类:
public class JwtUtils {
// 创建JWT
public static String createToken(Map<String, Object> claims) {
return Jwts.builder()
.claims(claims)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + expireTime))
.signWith(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
.compact();
}
// 解析JWT
public static Claims parseToken(String token) {
return Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
.build()
.parseSignedClaims(token)
.getPayload();
}
}三、升级openAPI3
3.1 创建OpenAPI配置类
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class OpenApiConfig {
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("若依管理系统接口文档")
.version("1.0")
.description("基于Spring Boot 3和OpenAPI 3的若依系统接口文档")
.contact(new Contact()
.name("若依团队")
.url("https://ruoyi.vip")
.email("ruoyi@163.com"))
.license(new License()
.name("MIT License")
.url("https://opensource.org/licenses/MIT")))
.components(new Components()
.addSecuritySchemes("BearerAuth", new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.in(SecurityScheme.In.HEADER)
.name("Authorization")))
.addSecurityItem(new SecurityRequirement().addList("BearerAuth"));
}
}3.2 修改application.yml配置
springdoc:
swagger-ui:
path: /swagger-ui.html
tags-sorter: alpha
operations-sorter: alpha
enabled: true
# 自定义文档标题
title: 若依管理系统API文档
# 分组配置
api-docs:
path: /v3/api-docs
enabled: true
group-configs:
- group: 'default'
paths-to-match: '/**'
packages-to-scan: 'com.ruoyi.web.controller'
default-consumes-media-type: application/json
default-produces-media-type: application/json
show-actuator: false
# 如果需要Knife4j增强UI(可选)
knife4j:
enable: true
setting:
language: zh_cn3.3 Swagger2到OpenAPI3注解对照表
| Swagger2注解 | OpenAPI3注解 | 说明 |
|---|---|---|
@Api | @Tag | 类级别的API描述 |
@ApiOperation | @Operation | 方法级别的操作描述 |
@ApiParam | @Parameter | 参数描述 |
@ApiModel | @Schema | 模型描述 |
@ApiModelProperty | @Schema | 模型属性描述 |
@ApiResponse | @ApiResponse | 响应描述 |
四、常见问题与解决方案
4.1 循环依赖问题
Spring Boot 3对循环依赖的检测更加严格,需要检查并解决可能的循环依赖:
// 使用@Lazy注解解决循环依赖
@Bean
@Lazy
public ServiceA serviceA(ServiceB serviceB) {
return new ServiceA(serviceB);
}五、测试与验证
升级完成后,需要进行全面测试:
- 单元测试:运行所有单元测试,确保业务逻辑正确
- 集成测试:测试各模块间的集成和接口调用
- 安全测试:验证权限控制和认证机制正常工作
- 性能测试:确保系统性能没有下降
总结
Spring Boot 3升级虽然涉及较多改动,但遵循系统化的升级步骤可以大大降低风险。主要工作包括:
- 更新POM依赖和版本号
- 替换javax包为jakarta包
- 适配Spring Security 6的新配置方式
- 更新JWT等第三方库的使用方式
- 更新swagger2到openapi3
- 解决循环依赖等兼容性问题
升级完成后,系统将获得更好的性能、更安全的特性和更长的技术支持周期。
评论已关闭