Ruoyi-Vue 升级JDK21、Springboot3、Mybatis3

一、需要升级的组件

名称原版本目标版本
JDK1.821
Springboot、Redis2.5.133.2.5
Servlet4.0.16.0.0
Mybatis2.0.73.0.3
Mysql驱动8.0.288.3.0
Lombok1.18.201.18.30
Kaptcha2.3.22.3.3
Flowable1.821
jaxb-api2.3.02.3.1
pagehelper1.4.52.1.1

二、详细说明

JDK
<java.version>1.8</java.version>

<!-- ↓↓↓改为下面↓↓↓ -->

<java.version>21</java.version>
Springboot、Redis
<springboot.version>2.5.13</springboot.version>

<!-- ↓↓↓改为下面↓↓↓ -->

<springboot.version>3.2.5</springboot.version>
Servlet

依赖调整

<!-- servlet包 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
</dependency>

<!-- ↓↓↓改为下面↓↓↓ -->

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
</dependency>

全局包名更换

javax.servlet  →  jakarta.servlet

javax.validation  →  jakarta.validation

javax.annotation  →  jakarta.annotation
Mybatis
<mybatis-spring-boot.version>2.2.2</mybatis-spring-boot.version>

<!-- ↓↓↓改为下面↓↓↓ -->

<mybatis-spring-boot.version>3.0.3</mybatis-spring-boot.version>
Mysql驱动
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!-- ↓↓↓改为下面↓↓↓ -->

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>
Lombok
<lombok.version>1.18.20</lombok.version>

<!-- ↓↓↓改为下面↓↓↓ -->

<lombok.version>1.18.30</lombok.version>
Kaptcha
<kaptcha.version>2.3.2</kaptcha.version>
<!-- ↓↓↓改为下面↓↓↓ -->
<kaptcha.version>2.3.3</kaptcha.version>


<!-- 验证码 -->
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>${kaptcha.version}</version>
</dependency>

<!-- ↓↓↓改为下面↓↓↓ -->

<!-- 验证码 -->
 <dependency>
     <groupId>pro.fessional</groupId>
     <artifactId>kaptcha</artifactId>
     <exclusions>
         <exclusion>
             <artifactId>servlet-api</artifactId>
             <groupId>jakarta.servlet</groupId>
         </exclusion>
     </exclusions>
 </dependency>
Easy-captcha
<dependency>
    <groupId>com.github.whvcse</groupId>
    <artifactId>easy-captcha</artifactId>
    <version>1.6.2</version>
</dependency>

<!-- ↓↓↓后面添加↓↓↓ -->

<dependency>
    <groupId>org.openjdk.nashorn</groupId>
    <artifactId>nashorn-core</artifactId>
    <version>15.4</version>
</dependency>
Flowable
<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-engine</artifactId>
    <version>6.7.2</version>
</dependency>
<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter-basic</artifactId>
    <version>6.7.2</version>
</dependency>

<!-- ↓↓↓改为下面↓↓↓ -->

<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>7.1.0</version>
</dependency>
jaxb-api
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>

<!-- ↓↓↓后面添加↓↓↓ -->

<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>
pagehelper
<pagehelper.boot.version>1.4.5</pagehelper.boot.version>

<!-- ↓↓↓后面添加↓↓↓ -->

<pagehelper.boot.version>2.1.1</pagehelper.boot.version>

三、相关配置类调整

PermitAllUrlProperties
@Configuration
public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware {
    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");

    private ApplicationContext applicationContext;

    private List<String> urls = new ArrayList<>();

    public String ASTERISK = "*";

        @Override
    public void afterPropertiesSet() {
        RequestMappingHandlerMapping mapping = applicationContext.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();

        map.forEach((info, handlerMethod) -> {
            Set<String> patterns = Optional.ofNullable(info.getPathPatternsCondition())
                    .map(PathPatternsRequestCondition::getPatterns)
                    .stream()
                    .flatMap(Set::stream)
                    .map(PathPattern::getPatternString)
                    .collect(Collectors.toSet());

            processAnnotation(handlerMethod.getMethod(), patterns);
            processAnnotation(handlerMethod.getBeanType(), patterns);
        });
    }

    private <T extends AnnotatedElement> void processAnnotation(T element, Set<String> patterns) {
        Optional.ofNullable(AnnotationUtils.findAnnotation(element, Anonymous.class))
                .ifPresent(annotation -> patterns.forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK))));
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.applicationContext = context;
    }

    public List<String> getUrls() {
        return urls;
    }

    public void setUrls(List<String> urls) {
        this.urls = urls;
    }
}
SecurityConfig
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class SecurityConfig {
    /**
     * 允许匿名访问的地址
     */
    @Resource
    private PermitAllUrlProperties permitAllUrl;

    /**
     * 自定义用户认证逻辑
     */
    @Resource
    private UserDetailsService userDetailsService;

    /**
     * 认证失败处理类
     */
    @Resource
    private AuthenticationEntryPointImpl unauthorizedHandler;

    /**
     * 退出处理类
     */
    @Resource
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    /**
     * token认证过滤器
     */
    @Resource
    private JwtAuthenticationTokenFilter authenticationTokenFilter;

    /**
     * 跨域过滤器
     */
    @Resource
    private CorsFilter corsFilter;


    @Bean
    public AuthenticationManager authenticationManagerBean(){
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(bCryptPasswordEncoder());
        return new ProviderManager(provider);
    }

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // CSRF禁用,因为不使用session
        http.csrf(AbstractHttpConfigurer::disable)
                // authenticationEntryPoint认证失败处理类;accessDeniedHandler授权失败
                .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
                // 基于token,所以不需要session
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                // 过滤请求
                // 对于登录login 允许匿名访问,但是如果携带认证token反而不能访问
                .authorizeHttpRequests(auth -> {
                    permitAllUrl.getUrls().forEach(url -> auth.requestMatchers(url).permitAll());
                    auth.requestMatchers("/login", "/app-user/login").anonymous()
                        .requestMatchers(
                            HttpMethod.GET,
                            "/",
                            "/*.html",
                            "/**.html",
                            "/*.css",
                            // 前端資源放行
                            "/static/**",
                            // 由于安卓手机微信公众号页面无法实现blog二进制流的读取下载pdf,
                            // 故这里暂时先放开,后续有客户沟通是否需要做公众号卡号绑定业务增强安全性
                            "/*.js",
                            // 文件上傳路徑放行
                            "/captcha/*",
                            "/home/management/uploadPath/**`").permitAll()
                        // 除上面外的所有请求全部需要鉴权认证
                        .anyRequest().authenticated();
                    }
                ).headers(AbstractHttpConfigurer::disable);

        //注销配置,退出后跳转到/logout,退出成功后处理logoutSuccessHandler
        http.logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler));
        // 添加JWT filter,添加该过滤器放在UsernamePasswordAuthenticationFilter过滤器之前
        http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 添加CORS filter
        http.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
        http.addFilterBefore(corsFilter, LogoutFilter.class);

        return http.build();
    }

    /**
     * 强散列哈希加密实现,这里会使得密码校验的时候默认使用该加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
application.yml

redis配置

spring:
  data:  # ←----- ######## 改动在这里,多了一级data #########
    # redis配置
    redis:
      # 地址
      host: localhost
      port: 6379
      # 数据库索引
      database: 2
      # 密码
      password: 
      # 连接超时时间
      timeout: 10s
      lettuce:
        pool:
          # 连接池中的最小空闲连接
          min-idle: 0
          # 连接池中的最大空闲连接
          max-idle: 8
          # 连接池的最大数据库连接数
          max-active: 8
          # #连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
pom.yml

maven打包插件

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
    </configuration>
</plugin>


<!-- ↓↓↓改为下面↓↓↓ -->

 <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>${java.version}</source>
        <target>${java.version}</target>
        <encoding>${project.build.sourceEncoding}</encoding>
        <compilerArgs>   <!--←----- ######## 改动在这里,加了这个配置 ######### -->
            <arg>-parameters</arg>
        </compilerArgs>
    </configuration>
</plugin>

四、补充

  1. 项目启动报错
    Failed to start bean ‘documentationPluginsBootstrapper‘; nested exception is java.lang.NullPointerException

解决:暂时关闭swagger 或 升级
在这里插入图片描述

  1. 循环依赖问题
    解决:加@Lasy注解或根据实际业务情况调整代码调用

  2. 验证码问题
    在这里插入图片描述
    解决:添加解析器(上面已提到)

<dependency>
    <groupId>org.openjdk.nashorn</groupId>
    <artifactId>nashorn-core</artifactId>
    <version>15.4</version>
</dependency>
  1. 还有其它报错,可根据具体错误信息查看源码、百度、大模型解决,提升解决问题能力。相信自己是最棒的!!
<think>好的,我需要帮助用户将Ruoyi-Vue升级Spring Boot 3。首先,我应该回顾一下Ruoyi-Vue的当前技术栈。Ruoyi-Vue是基于Spring BootMyBatisVue的前后端分离框架,所以升级Spring Boot 3可能会涉及多个组件的兼容性问题。 首先,用户需要确认当前使用的Spring Boot版本。假设他们使用的是Spring Boot 2.x,那么升级3.0需要遵循官方迁移指南。Spring Boot 3.0需要Java 17及以上,所以第一步是检查项目中的Java版本是否至少是17,并升级JDK。 接下来,修改项目的pom.xml文件,将Spring Boot的版本号改为3.0.0或更高。同时,Spring Boot 3.0对应的Spring Framework版本是6.0,因此可能需要更新相关的依赖项。例如,Spring Security可能会有版本变化,需要对应升级到6.x版本。 另一个关键点是依赖库的兼容性检查。例如,MyBatisSpring Boot Starter可能需要升级3.0以上版本。此外,像Druid数据库连接池、PageHelper分页插件等第三方库也需要确认是否支持Spring Boot 3.0。如果有不兼容的情况,可能需要寻找替代库或等待更新。 Jakarta EE的包名变化也是一个重要问题。Spring Boot 3.0将javax替换为jakarta,所以所有涉及到javax.persistence、javax.servlet等的地方都需要替换为jakarta下的相应包。这可能需要批量替换导入语句,或者使用IDE的全局替换功能。 配置文件的调整可能包括安全配置和JPA配置的变化。例如,Spring Security 6.0中的一些配置类或方法可能已被弃用或修改,需要根据新的文档进行调整。同时,数据库驱动类名在Spring Boot 3中可能有变化,比如MySQL的驱动类名可能从com.mysql.cj.jdbc.Driver调整为新的格式,需要检查并更新application.yml中的配置。 代码层面的修改可能涉及废弃API的替换。例如,Hibernate 6.x中某些方法可能不再使用,需要查找并替换为新的方法。此外,单元测试中使用的JUnit版本可能需要升级到JUnit 5,并调整测试注解和断言方法。 升级过程中可能会遇到各种编译错误和运行时异常,需要逐步解决。例如,升级后启动项目时出现ClassNotFoundException或NoSuchMethodError,可能是由于依赖版本不匹配,需要检查并调整依赖版本。同时,注意日志中的错误信息,按照提示进行修复。 测试阶段需要全面覆盖,包括单元测试、集成测试和前端功能测试。确保所有接口正常响应,权限控制生效,数据库操作无误。前端部分如果使用了Vue,可能不需要太多改动,但要确保后端API的变更不会影响前端调用。 最后,参考官方文档和社区资源非常重要。Spring Boot 3的迁移指南、Ruoyi社区的讨论帖、以及GitHub上的Issues都可能提供有用的解决方案。如果遇到无法解决的问题,可以在这些地方寻求帮助。</think>将 Ruoyi-Vue 升级Spring Boot 3 需分步骤处理兼容性问题,以下是详细流程: --- ### **1. 环境准备** - **JDK 版本升级**:Spring Boot 3 要求 Java 17+,需安装 JDK 17 并配置环境变量。 - **IDE 支持**:确保 IntelliJ IDEA 或 Eclipse 支持 Java 17 语法。 --- ### **2. 修改依赖版本** 在 `ruoyi/pom.xml` 中更新 Spring Boot 版本: ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0</version> <!-- 或最新版本 --> </parent> ``` 同时更新相关依赖: - Spring Security 升级到 6.0+ - MyBatis Spring Boot Starter 升级3.0+ - 其他依赖如 `druid-spring-boot-starter`、`pagehelper-spring-boot-starter` 需兼容 Spring Boot 3[^1]。 --- ### **3. Jakarta EE 包替换** 全局替换 `javax.*` 为 `jakarta.*`,例如: - `javax.servlet` → `jakarta.servlet` - `javax.persistence` → `jakarta.persistence` 可通过 IDE 的 **Replace in Path** 功能批量操作。 --- ### **4. 配置文件调整** - **数据库驱动类名**:在 `application.yml` 中修改: ```yaml spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ry_vue?useSSL=false ``` - **安全配置**:Spring Security 6 默认启用 CSRF,若需禁用,需显式配置: ```java @Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable(); return http.build(); } } ``` --- ### **5. 代码适配** - **JPA/Hibernate**:检查实体类注解是否兼容 Jakarta EE。 - **废弃 API**:例如 `SpringExtension` 在 JUnit 5 中需替换为 `@ExtendWith`。 - **Web 层**:若使用 `Servlet API`,需确保导入 `jakarta.servlet.http.HttpServletRequest`。 --- ### **6. 测试与调试** - **编译错误**:优先解决依赖冲突(使用 `mvn dependency:tree` 分析)。 - **权限问题**:Spring Security 6 的权限表达式语法可能变化,需检查 `@PreAuthorize` 注解。 - **前端联调**:确保接口路径和参数格式与 Vue 前端匹配。 --- ### **参考资源** - [Spring Boot 3 迁移指南](https://spring.io/projects/spring-boot#overview)[^2] - [Ruoyi 社区讨论帖](https://gitee.com/y_project/RuoYi-Vue/issues) ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值