Springboot 2.1.x版本直升 3.1.x版本踩坑过程记录(java8升级到java17)

科技   2025-01-02 17:06   江苏  
将 脚本之家 设为“星标
第一时间收到文章更新

来源公众号:JAVA日知录  ID:javadaily

一、升级目标

springboot 3.1.x

java 17

maven-3.9.x

idea 2023.x

原系统环境:

springboot 2.1.x

java 8

maven-3.3.x

idea 2022.x

二、环境准备

1. JDK17下载oracle

image-20231220211150422

安装步骤不清楚的可以自行查一下bd/gg。

安装完成后配置环境变量:如果需要保留jdk8版本(由于本人参与多个项目的研发,大部分都是java8所以需要保留jdk8,这样在使用不同版本研发时直接切换JDK),可以在环境变更中配置两个JAVA8_HOME、JAVA17_HOME,通过JAVA_HOME进行切换。如果不需要保留多版本,默认直接配置到JAVA_HOME中即可。

image-20231220211233583

配置完成后可以在终端使用java -version命令查一下环境是否成功切换到17版本。

2. maven版本升级(springboot3.0兼容maven3.5+以上版本,不需要升级的可以忽略)

下载最新的maven包解压即可maven安装,本次升级安装使用的版本是apache-maven-3.9.5

安装完成后修改一下配置settings.xml(本次升级直接把以前3.3版本中的settings.xml内容复制过来)

3. idea配置

如果需要升级安装的自行下载安装IntelliJ IDEA

打开相关项目修改idea配置:

文件-->项目结构-->项目设置-->SDK修改为:jdk17(如果没有jdk17的话,可以在下方平台设置SDK中添加一个)

文件-->设置-->构建、执行、部署-->java编译器:字节码版本改为17

文件-->设置-->构建、执行、部署-->构建工具-->Maven:相关路径配置修改为新的maven目录

三、版本升级

1. 更新pom.xml版本号

修改spring-boot-starter-parent版本,本次升级使用的版本3.1.2

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>3.1.2</version>
</parent>

<properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <java.version>17</java.version>
</properties>

版本修改后,点击重新加载Maven项目,项目在编译期会出现大批量的红色错误告警,不要紧张,此时的错误大部分上都是依赖包找不到,主要分两种:

a. 必现错误:import javax.*

原因是:从Spring Boot 3.0开始,原有的Java EE被彻底弃用(说是规避Oracle的版权问题,可以看这个Spring Boot 3.0为什么废弃了JavaEE,改用了Jakarta EE),换用Jakarta EE,所有javaEE中包名为javax.*的引用都需要更换为jakarta.*,其他第三方厂商的工具包也有命名javax.*,所以。

b. 部分依赖包缺失

原因是:在springboot3中很多中间件做了很大的升级,导致有些项目中继承的依赖包找不到了,下面几个是在本次项目升级过程中遇到缺少的包(每个项目中依赖的工具包不一致,不要盲目引用,这里只做参考)。

如果在项目启动时报错:org.springframework.boot:xxx:null:jar, 先检查一下pom中有没有引用xxx这个包,大概率是没有,直接手动引入一下相关包即可。

<dependency>
 <groupId>javax.persistence</groupId>
 <artifactId>javax.persistence-api</artifactId>
 <version>2.2</version>
</dependency>
<dependency>
 <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
 <version>1.9.20.1</version>
 <scope>runtime</scope>
</dependency>

c. 启动时第三方库类依赖注册报错

大部分原因:SpringBoot3 中自动配置注册的 spring.factories 写法已废弃,改为了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,项目中有使用spring.factories的可以在resource目录下创建META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,将相关类迁移过来,注意语法不一样了,imports中直接平铺类全路径即可。

如果第三方厂商未做升级的话,会导致启动时第三方库类依赖报错找不到,springboot3.0发布已经将近1年时间大部分的第三方包都已经做了相应升级,如果项目上有引用公司内部或自己团队自研的工具包暂未升级也可以使用本地的imports注册。

以Dailymart中基于RocketMQ的自定义starter为例,第三方未做升级,我们需要将其配置类迁移到此文件下。

image-20231220211733974

2. 适配mysql--jpa(多数据源)

将pom依赖中: mysql-connector-java修改为com.mysql:mysql-connector-j(改不改都行)

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
 <groupId>com.mysql</groupId>
 <artifactId>mysql-connector-j</artifactId>
 <scope>runtime</scope>
</dependency>

注意:

javax.persistence.EntityManagerFactory 切换为jakarta.persistence.EntityManagerFactory,两个包中都有EntityManagerFactory。

如果启动报错,并提示找不到相关依赖类则需要修改相关配置

Could not load requested class : org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy

Could not load requested class : org.hibernate.dialect.MySQL5InnoDBDialect

原有配置:

@Bean
@Primary
public LocalContainerEntityManagerFactoryBean eduEntityManager(EntityManagerFactoryBuilder builder) {
 Map<String, String> properties = new HashMap<>();
    properties.put("hibernate.implicit_naming_strategy",
            "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
    properties.put("hibernate.physical_naming_strategy",
            "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
    properties.put("hibernate.hbm2ddl.auto",
            env.getProperty("spring.jpa.hibernate.ddl-auto"));
    properties.put("hibernate.dialect",
            env.getProperty("spring.jpa.database-platform"));
    return builder
            .dataSource(primaryDataSource())
            .packages("com.iflytek.rdg.ainote.eduentity")
            .persistenceUnit("edu")
            .properties(properties)
            .build();
}

修改为:

@Bean
@Primary
public LocalContainerEntityManagerFactoryBean eduEntityManager(EntityManagerFactoryBuilder builder) {
   
    Map<String, String> properties = new HashMap<>();
    properties.put("hibernate.implicit_naming_strategy",
            "org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy");
    properties.put("hibernate.physical_naming_strategy",
            "org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy");
    properties.put("hibernate.hbm2ddl.auto",
            env.getProperty("spring.jpa.hibernate.ddl-auto"));
    properties.put("hibernate.dialect""org.hibernate.dialect.MySQLDialect");
    return builder
            .dataSource(primaryDataSource())
            .packages("com.iflytek.rdg.ainote.eduentity")
            .persistenceUnit("edu")
            .properties(properties)
            .build();
}

其他的数据源修改方式一样,properties配置可以改为通过配置文件注入。

3. 适配mongo(多数据源)

主要修改MongoDbFactory的生成方式从SimpleMongoDbFactory更换为SimpleMongoClientDatabaseFactory(spring-data-mongodb3.0之前版本使用SimpleMongoDbFactory,3.0之后的版本切换到SimpleMongoClientDatabaseFactory,本次版本从spring-data-mongodb-2.1.17升级到4.1.2)

旧mongo配置:

import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientURI;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;

public abstract class AbstractMongoConfig {

    private String url;
    
    public MongoDbFactory getMongoDbFactory() {
        return new SimpleMongoDbFactory(new MongoClientURI(url, new MongoClientOptions.Builder()));
    }
    
    /**
     * get mongo template
     *
     * @return
     */

    public abstract MongoTemplate getMongoTemplate();
    
    public String getUrl() {
        return url;
    }
    
    public void setUrl(String url) {
        this.url = url;
    }
}

新mongo配置

import com.mongodb.ConnectionString;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;

public abstract class AbstractMongoConfig {

    private String url;
    
    public SimpleMongoClientDatabaseFactory getMongoDbFactory() {
        ConnectionString connectionString = new ConnectionString(url);
        MongoClient mongoClient = MongoClients.create(connectionString);
        return new SimpleMongoClientDatabaseFactory(mongoClient, connectionString.getDatabase());
    }
    
    /**
     * get mongo template
     *
     * @return
     */

    public abstract MongoTemplate getMongoTemplate();
    
    public String getUrl() {
        return url;
    }
    
    public void setUrl(String url) {
        this.url = url;
    }
}

/**
* 多数据源配置
  */

  @Configuration
  @ConfigurationProperties(prefix = "spring.data.mongodb.result")
  public class ResultMongoConfig extends AbstractMongoConfig {

    @Override
    @Bean(name = "resultMongoTemplate")
    public MongoTemplate getMongoTemplate() {
        return new MongoTemplate(getMongoDbFactory());
    }

}

4. 适配Redis

本项目配置参考:

(本项目使用场景简单,只需要基本配置,如需要高级定制可以自定义Bean,LettuceConnectionFactory):

@Configuration
public class RedisConfig {

    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        // key序列化
        redisTemplate.setKeySerializer(RedisSerializer.string());
        // value序列化
        redisTemplate.setValueSerializer(RedisSerializer.json());
        // Hash key序列化
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        // Hash value序列化
        redisTemplate.setHashValueSerializer(RedisSerializer.json());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

5. swagger2升级swagger3

去除swagger2相关依赖,引入swagger3依赖。

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.1.0</version>
</dependency>

增加配置application.yml

springdoc:
  api-docs:
    path: /api-docs
  swagger-ui:
    path: /swagger-ui.html
  show-actuator: true

swagger注解变化:




swagger2注解swagger3注解作用
@Api@Tag描述Controller
@ApiIgnore@Parameter(hidden = true)  
@Operation(hidden = true)  
@Hidden
描述忽略操作
@ApiImplicitParam@Parameter描述参数
@ApiImplicitParams@Parameters描述参数
@ApiModel@Schema描述对象
@ApiModelProperty(hidden = true)@Schema(accessMode = READ_ONLY)描述对象属性
@ApiOperation(value = "foo", notes = "bar")@Operation(summary = "foo", description = "bar")描述方法
@ApiParam@Parameter描述参数

OpenAPI配置:

 @Bean
  public OpenAPI springShopOpenAPI() {
      return new OpenAPI()
              .info(new Info().title("SpringShop API")
              .description("Spring shop sample application")
              .version("v0.0.1")
              .license(new License().name("Apache 2.0").url("http://springdoc.org")))
              .externalDocs(new ExternalDocumentation()
              .description("SpringShop Wiki Documentation")
              .url("https://springshop.wiki.github.org/docs"));
  }

访问地址

http://localhost:8081/swagger-ui/index.html

四. 总结

升级springboot3.0的相关技术博客在网上有几篇,上面的问题都千奇百怪的,除了两个通用问题外其他的和我遇到的问题大部分都不太一样,有小部分问题当前网上还搜索不到解决方案,需要自己去分析,研究对比一下新旧版本的源码说明。

这次升级直接到了最新的springboot版本,所以随便把依赖的第三方包大部分都升级到了最新版本,暂时没有遇到其他的问题。

  推荐阅读:
  1. 我一直在用 Java,但是我一直都不喜欢 Java!
  2. 这些小 Bug,99% 的程序员都写过!
  3. Java线程池还能死锁?一篇文章带你搞懂线程池中的一些坑点
  4. Kotlin不可能取代Java
  5. 我年薪40万,让你看看一年能到手多少?

脚本之家
脚本之家(jb51.net)每天提供最新IT类资讯、原创内容、编程开发的教程与经验分享,送书福利天天在等你!
 最新文章