前言

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包名替换

全局替换javaxjakarta

  • javax.servletjakarta.servlet
  • javax.persistencejakarta.persistence
  • javax.annotationjakarta.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_cn

3.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);
}

五、测试与验证

升级完成后,需要进行全面测试:

  1. 单元测试:运行所有单元测试,确保业务逻辑正确
  2. 集成测试:测试各模块间的集成和接口调用
  3. 安全测试:验证权限控制和认证机制正常工作
  4. 性能测试:确保系统性能没有下降

总结

Spring Boot 3升级虽然涉及较多改动,但遵循系统化的升级步骤可以大大降低风险。主要工作包括:

  1. 更新POM依赖和版本号
  2. 替换javax包为jakarta包
  3. 适配Spring Security 6的新配置方式
  4. 更新JWT等第三方库的使用方式
  5. 更新swagger2到openapi3
  6. 解决循环依赖等兼容性问题

升级完成后,系统将获得更好的性能、更安全的特性和更长的技术支持周期。


标签: 若依, ruoyi, springboot3

评论已关闭