SpringBoot2.7升级项目到Springboot3.1踩坑指南(jdk17/jdk21)

这篇具有很好参考价值的文章主要介绍了SpringBoot2.7升级项目到Springboot3.1踩坑指南(jdk17/jdk21)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概要

由于SpringBoot3.x全面拥抱JDK17,兼容jdk21,jdk17乃是大势所趋。这里是从SpringBoot2.7-->SpringBoot3.1踩坑指南。
提前阅读:jdk8升级JDK17避坑指南(适用于SpringBoot2.3—SpringBoot2.7升级)

国内顶级开源项目升级情况

国内顶级开源项目升级到springBoot3情况,可以作为升级SpringBoot3的风向标。仅对比国内规模使用,落地过万企业的开源项目
参考:国内顶级开源项目:芋道、ruoyi、JeecgBoot、pig、SpringBlade功能对比

评价项/项目名 yudao-cloud Ruoyi-Cloud RuoYi-Cloud-Plus Dante Cloud pig bladex JeecgBoot
官网 芋道yudao-cloud 开发指南 若依 plus-doc.dromara.org Dante Cloud pig4cloud bladex.cn、看云-SpringBlade开发手册 JeecgBoot
源码收费 免费 免费 免费 免费 免费 + 收费(3999) 免费 + 收费(5000) 免费 + 收费(100000)
文档收费 文档收费 免费、视频收费 文档免费、视频收费 免费 免费、授权收费 文档收费 文档免费、授权收费
github yudao-cloud RuoYi RuoYi-Vue-Plus Dante Cloud pig SpringBlade jeecg-boot
gitee yudao-cloud RuoYi RuoYi-Vue-Plus Dante Cloud 暂无 SpringBlade jeecg-boot
jdk17分支 master-jdk21 RuoYi-Cloud-Plus 2.X dante-cloud 3.1.X pig jdk17 jeecg-boot/springboot3

适配SpringBoot3指南

  • 参考1-微信公众号-这可能是最全的SpringBoot3新版本变化了!、
  • 参考2-SpringBoot官网-Spring Boot 3.0 Release Notes、
  • 参考3-微信公众号-Swagger升级指南:Swagger2与Swagger3注解差异揭秘、
  • 参考4-微信公众号-Dante Cloud 3.2.0.0 发布,首个适配 Spring Boot 3.2版本及经验分享
  • 参考5-JeecgBoot 文档中心-升级SpringBoot3

SpringBoot3升级要点

前提说明,建议先完成springboot2.x—>springBoot2.7.x+jdk17的适配,这里升级难度会小很多。参考:文章最前面的文章。

1、jdk17变动(如javax)

详见: jdk8升级JDK17避坑指南(适用于SpringBoot2.3—SpringBoot2.7升级)

  • 模块化对反射的影响==>对系统类的反射增加了限制,需要打开限制增加jvm启动参数add-opens,自己写的类,可以正常使用反射。
  • 删除sun.misc 下的包,如sun.misc.BASE64Encoder==>java.util.Base64替换
  • 删除JAXB、soup相关==>maven仓库上面有新的maven坐标,引入新依赖即可
  • 删除javax.annotation==>maven仓库上面有新的maven坐标,引入依赖即可

2、redis修改spring.redis.host ===> spring.data.redis.host

redis配置命令空间进行了修改,需要注意。
参考:Spring Boot3.0(九):整合Redis

--- # redis 配置,注意springboot 3.x 有 data,2.x 没有 data
## spring.redis.host ===> spring.data.redis.host
spring:
  data:
    redis:
      host: 10.16.58.180
      port: 6379
      password: Admin123
      database: 6      

3、SpringCloudApplication注解被删除

使用@SpringBootApplication替换

4、不兼容升级import java.servlet====>import jakarta.servlet

servlet捐献给社区,为了避免版权问题,修改了包名,导致不兼容.

  • javax.servlet===> jakarta.servlet
  • javax.validation ===> jakarta.validation
  • javax.annotation ===> jakarta.annotation
  • javax.mail ===> jakarta.mail
  • javax.websocket ==> jakarta.websocket
 <!-- Java Servlet jakarta -->
            <dependency>
                <groupId>jakarta.servlet</groupId>
                <artifactId>jakarta.servlet-api</artifactId>
                <version>6.0.0</version>
            </dependency>  

5、swagger集成 弃用springfox—>springdoc不兼容升级

springfox不维护了,springboot3使用springdoc,并启用openapi3.0,相关注解进行了变化。

参考:spring boot 3 整合 swagger3、
参考:Swagger升级指南:Swagger2与Swagger3注解差异揭秘、
参考:OpenApi3.0注解说明

注解作用 swagger2 swagger2示例 swagger3-openApi3.0 swagger示例 替换
用于Controller @Api @Api(value = "/app/child/v2", tags = "儿童档案") @Tag @Tag(name = "/app/child/v2", description = "儿童档案") @Api(value = "User Management", description = "Operations pertaining to users")—>@Tag(name = "User Management", description = "Operations pertaining to users")@Api(tags = "小程序端Core Controller")—>@Tag(description = "小程序端Core Controller")
用于Controller接口 @ApiOperation @ApiOperation(value = "新增儿童档案绑定监护人", httpMethod = "POST", produces = "application/json") @Operation @Operation(summary = "新增儿童档案绑定监护人", method = "POST") @ApiOperation(value = "根据儿童证件号码查询儿童档案", httpMethod = "POST", produces = "application/json")–>@Operation(summary = "根据儿童证件号码查询儿童档案", method = "POST")
用于Controller接口参数注解 @ApiParam或@ApiImplicitParam @ApiParam("预约id,字段名:personApptId,默认无") @Parameter @Parameter(description = "预约id,字段名:personApptId,默认无", required = true) @ApiParam(value = "追溯码 形如:81900920216939751445,max=32", defaultValue = "81900920216939751445")—>@Parameter(description = "追溯码 形如:81900920216939751445,max=32", example = "81900920216939751445")
参数隐藏 @ApiIgnore @ApiIgnore HttpServletRequest request / / /
实体字段 @ApiModelProperty @ApiModelProperty("接种人员-姓名(冗),max=32")@ApiModelProperty(notes = "The database generated user ID") @Schema @Schema(description = "主键 自增") @ApiModelProperty(value—>@Schema(description@ApiModelProperty(hidden = true)–>@Schema(hidden = true@ApiModelProperty(value = "更新人员", example = "张三",hidden = true)—>@Schema(description = "更新人员", example = "张三",hidden = true)
实体类 @ApiModel @ApiModel("接种人员-姓名(冗),max=32")@ApiModelProperty(notes = "The database generated user ID") @Schema @Schema (description = "根据儿童证件号码查询儿童档案")") @ApiModel(value—>@Schema(description@ApiModel(description —>@Schema(description
// idea正则替换01
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.tags.Tag;
@Api\(tags = "([^\"]+)", hidden = ([^\"]+), description = "([^\"]+)"\)
@Tag(name = "$1", description = "$3")

@Api\(value = "([^\"]+)", tags = "([^\"]+)"\)
@Tag(name = "$1", description = "$2")

@Api\(tags = "([^\"]+)", description = "([^\"]+)"\)
@Tag(name = "$1", description = "$2")

@Api\(tags = "([^\"]+)"\)
@Tag(name = "$1")

// idea正则替换02
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
@ApiOperation\(value = "([^\"]+)", notes = "([^\"]+)", position = ([^\"]+)\)
@Operation(summary = "$1", description= "$2")

@ApiOperation\(value = "([^\"]+)", httpMethod = "([^\"]+)", produces = "([^\"]+)"\)
@Operation(summary = "$1", method = "$2")

@ApiOperation\(value = "([^\"]+)", notes = "([^\"]+)"\)
@Operation(summary = "$1", description = "$2")

@ApiOperation\(value = "([^\"]+)", httpMethod = "([^\"]+)"\)
@Operation(summary = "$1", method = "$2")

@ApiOperation\(value = "([^\"]+)"\)
@ApiOperation\("([^\"]+)"\)
@Operation(summary = "$1")

// idea正则替换03
import io.swagger.annotations.ApiModel;
import io.swagger.v3.oas.annotations.media.Schema;

@ApiModel\(value = "([^\"]+)"\)
@Schema(description = "$1")

@ApiModel\(description = "([^\"]+)"\)
@Schema(description = "$1")

// idea正则替换04
import io.swagger.annotations.ApiModelProperty;
'';
@ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)", required = ([^\"]+)\)
@Schema(description = "$1", example = "$2", required= $3)

@ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)", hidden = ([^\"]+)\)
@Schema(description = "$1", example = "$2", hidden = $3)

@ApiModelProperty\(value = "([^\"]+)", example = "([^\"]+)"\)
@Schema(description = "$1", example = "$2")

@ApiModelProperty\(value = "([^\"]+)", hidden = ([^\"]+)\)
@Schema(description = "$1", hidden = $2)

@ApiModelProperty\(value = "([^\"]+)"\)
@Schema(description = "$1")

@ApiModelProperty\(hidden = ([^\"]+)\)
@Schema(hidden = $1)

@ApiModelProperty\("([^\"]+)"\)
@Schema(description = "$1")

@ApiModelProperty\(value="([^\"]+)"\)
@Schema(description = "$1")

// idea正则替换05
import io.swagger.annotations.ApiParam;
import io.swagger.v3.oas.annotations.Parameter;

@ApiParam\(value = "([^\"]+)", example = "([^\"]+)", defaultValue = "([^\"]+)"\)
@Parameter(name = "$1", example = "$2", description="$3")

@ApiParam\(value = "([^\"]+)", defaultValue = "([^\"]+)"\)
@ApiParam\(value = "([^\"]+)", example = "([^\"]+)"\)
@Parameter(name = "$1", example = "$2")

@ApiParam\(value = "([^\"]+)"\)
@ApiParam\("([^\"]+)"\)
@Parameter(name = "$1")

// idea正则替换06
import springfox.documentation.annotations.ApiIgnore;
import io.swagger.v3.oas.annotations.Hidden;
@ApiIgnore
@Hidden

@ApiIgnore HttpServletResponse response
@ApiIgnore HttpServletRequest request

// idea正则替换07
@ApiImplicitParams
@Parameters

@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)",required = ([^\"]+), example = "([^\"]+)", paramType = "query"\)
@Parameter(name = "$1", description = "$2", required = $3, example = "$4", in = ParameterIn.QUERY)

@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", required = ([^\"]+), paramType = "query"\)
@Parameter(name = "$1", description = "$2", required = true, in = ParameterIn.QUERY)

@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "([^\"]+)", required = ([^\"]+)\)
@Parameter(name = "$1", description = "$2", schema = @Schema(type = "$3"), required = $4)

@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "([^\"]+)"\)
@Parameter(name = "$1", description = "$2", schema = @Schema(type = "$3"))

@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", paramType = "form"\)
@Parameter(name = "$1", description = "$2", in = ParameterIn.QUERY)

@ApiImplicitParam\(name = "([^\"]+)", value = "([^\"]+)", dataType = "__file", paramType = "form"\)
@Parameter(name = "$1", description = "$2", example = "__file", in = ParameterIn.QUERY)

@ApiImplicitParam\(value = "([^\"]+)"\)
@Parameter(description = "$2")

swaggr2实例

// Swagger2 实体类
@Getter
@Setter
@NoArgsConstructor
@ApiModel(value = "根据儿童证件号码查询儿童档案")
public class ChildGetByChildNoVo implements Serializable {
    @ApiModelProperty(value = "身份证")
    private String childNo;

    private Long personId;
}

// Swagger2 Controller
@AllArgsConstructor
@RestController
@RequestMapping("/app/child/v2")
@Api(value = "/app/child/v2", tags = "儿童档案")
public class ChildController extends BaseController 

  @ApiOperation(value = "根据监护人ID分页查询被监护人", httpMethod = "GET", produces = "application/json")
    @GetMapping("/getByPersonId")
    @ApiImplicitParam(value = "预约id")
    public AjaxResult getByPersonId(@RequestParam(value = "pageNum",required = false,defaultValue = "1") Integer pageNum,
                                  @ApiParam("预约id,字段名:personApptId,默认无") @RequestParam(value = "personApptId") Long personApptIdpersonId,
                                   @ApiIgnore HttpServletRequest request
                                   ){

        IPage<ApptChildRelationship> page = new Page<>(pageNum,pageSize);
        IPage<ChildApiVo> pageData = childService.getByPersonId(page,personId);
        TableDataInfo rspData = new TableDataInfo();
        rspData.setCode(200);
        rspData.setRows(pageData.getRecords());
        rspData.setMsg("查询成功");
        rspData.setTotal(pageData.getTotal());
        return AjaxResult.success(rspData);
    }
 
 @PostMapping("/getCode")
    @Operation(summary = "获取预约码", method = "POST")
    @ApiImplicitParam(value = "预约id")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "personId", value = "用户id", paramType = "form"),
            @ApiImplicitParam(name = "apptId", value = "预约id", paramType = "form"),
            @ApiImplicitParam(name = "collectLocationId", value = "采样点id", paramType = "form"),
    })
    public String getCode(Long personId, Long apptId, Long collectLocationId) {
        return vficPersonService.generateApptQrCode(apptId, personId, collectLocationId);
    }
}

swagger3示例


6、动态数据源baomidou的dynamic-datasource依赖变动

经过测试,该条也可以不升级
参考1:baomidou dynamic-datasource、
参考2:kancloud tracy5546 dynamic-datasource

<!-- spring-boot 1.5.x 2.x.x 3.xx -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>

<!-- spring-boot 3.x.x -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
  <version>${version}</version>
</dependency>

7、Spring Framework 6.0 中删除了对 Apache HttpClient 支持(RestTemplate受影响)

升级到SpringBoot3发现依赖提示缺少:rg.apache.httpcomponents:httpclient,一些三方库可能依赖httpclient,就需要自己手工引入依赖。三方包如:spring-data-elasticsearch5、nacos-client 1.4.6、weixin-java-pay 4.0.0、htmlunit 3.6

参考: Spring-Boot-3.0-Migration-Guide#apache-httpclient-in-resttemplate、

<!-- spring framwork 5.x中默认引入apcache httpclient4,在spring framwork6.x中如果需要要手工引入 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.14</version>
</dependency>

<!-- spring framwork 6.x中的apcache httpclient5 -->
<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.3</version>
</dependency>

8、SpringBoot3.0整合RocketMQ时出现未能加载bean文件

springboot2.x使用rocketmq没有问题,springboot3出现,required a bean of type ‘org.apache.rocketmq.spring.core.RocketMQTemplate’ that could not be found.

参考1:SpringBoot3.0整合RocketMQ时出现未能加载bean文件
参考2:Spring boot 3.0整合RocketMQ及不兼容的问题

<dependency>
            <groupId>org.apache.rocketmq</groupId>
            <artifactId>rocketmq-spring-boot-starter</artifactId>
            <!--    目前也没有新版本,roketmq2.2.3没有适配springboot3-->
            <version>2.2.3</version>
        </dependency>
// 定义个配置类,引入也可以:
@Configuration
@Import({RocketMQAutoConfiguration.class})
public class RocketMQConfig {
}

9、springboot3默认依赖 elasticsearch从7.x升级到8.x

  • 配置文件地址修改:spring.elasticsearch.rest.uris==>spring.elasticsearch.uris
  • springboot3要求elasticsearch必须升级到8.x,不能使用7.x,否则报错:缺失响应头X-Elastic-Product(Elasticsearch)
  • 废弃:high client客户端
  • 要求ElasticSearch必须8.x

10、springboot 3.2 openFeign加载失败暂未解决 (3.1没问题)

启动失败 feign导致 not annotated with HTTP method type (ex. GET, POST)

  • https://blog.csdn.net/nailsoul/article/details/105223740
  • https://www.jianshu.com/p/11b4cbc8951a
  • https://juejin.cn/post/7112414513550491656

11、hutool5.8–>hutool6.0升级要要点

说明常见类,如果找不到,请看源码注释,注释上面写的有,或者全局搜索即可,无需多说。

  • ServletUtil–>JakartaServletUtil
  • hutool类变化说明: 【6.0.0】升级到6.0不兼容情况汇总,大家一起来统计维护呀、
  • 国密相关SmUtil,使用6.0x问题,参加:hutool 5.x 和 hutool 6.x crypto 国密SMUtil 模块放在一个工程中会产生冲突

12、Centos7使用jdk21报错

java: /lib64/libc.so.6: version `GLIBC_2.14’ not found (required by /usr/local/java/jdk-21.0.2+13/bin/…/lib/libjli.so)

原因,Centos6缺失GLIBC_2.14,报错缺失2.14、2.15解决办法相同。
只需要安装最高版本,自动安装低版本,比如:安装2.17.自动安装2.14

参考1:version `GLIBC_2.14’ not found 问题解决

参考2:解决 /lib64/libc.so.6: version `GLIBC_2.15’ not found 问题

最新已经是2.38了,可以直接安装:wget --no-check-certificate https://ftp.gnu.org/gnu/glibc/glibc-2.38.tar.gz,安装前确保安装的有gcc

  • Centos6 推荐2.17

13、@Async注解报错Invalid return type for async method (only Future and void

分析原因,springboot2.7一切正常,升级到springboot3.x@Async标记的方法,只能返还void或者Future(@Async+Future+AsyncResult返回值)

参考1:spring 升级到JDK 17和Sping Boot 3后,异步仅接受Future和void作为返回类型
参考2:Async only accepted Future and void as return type after upgrade to JDK 17 and Spring Boot 3文章来源地址https://www.toymoban.com/news/detail-837559.html

到了这里,关于SpringBoot2.7升级项目到Springboot3.1踩坑指南(jdk17/jdk21)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 快速搭建SpringBoot3.x项目

    上一小节中我们从0到1 使用Vite搭建了一个Vue3项目,并集成了Element Plus 实现了一个简单的增删改查页面。 这一篇中我们将使用IDEA快速搭建一个SpringBoot3.x的项目。 1、File-new-project 2、选择“Spring Initializr”,点击next; 3、选择spring boot版本及添加相关依赖 这一步我们需要选择

    2024年02月08日
    浏览(36)
  • springboot升级过程中踩坑定位分析记录 | 京东云技术团队

    作者:京东零售 李文龙 “ 俗话说:为了修复一个小bug而引入了一个更大bug ” 因所负责的系统使用的spring框架版本5.1.5.RELEASE在线上出过一个偶发的小事故,最后定位为spring-context中的一个bug导致的。 为了修复此bug进行了spring版本的升级,最终定的版本为收银台团队使用的版

    2024年02月01日
    浏览(44)
  • 【重磅】:Spring Initializer 已经不支持Java8,也就是SpringBoot2.x项目初始化

    我们可以看到在IDEA内置的Spring Initializer中 Java版本选择模块已经不支持1.8了,同样的,官网也不再支持了 Spring Boot 3.x要求 Java最低版本为17, 最新的SpringBoot版本已经要求Java21了 所以,你可以升级Java版本,使用SpringBoot3.X 我们可以尝试查看一下, 访问Spring官网,按照下图操作

    2024年02月04日
    浏览(53)
  • Java项目实战--基于SpringBoot3.0开发仿12306高并发售票系统--(一)前置知识

    本文参考自 Springboot3+微服务实战12306高性能售票系统 - 慕课网 (imooc.com) 本章将介绍仿12306售票系统实战开发的开发环境、项目核心技术和功能、项目模块和架构设计、开发所需前置知识,若想直接开始编写代码实现,请关注作者,看后面的第二章——项目实现 后端: JDK 17

    2024年04月12日
    浏览(40)
  • 升级springboot3.2.0报Name for argument of type [java.lang.String] not specified, and parameter name inf

    我这里项目版本是jdk17+springboot3.1.5 升级为jdk21和springboot3.2.0;升级过程总体还是挺顺利的,只是访问应用时老报错: Name for argument of type [java.lang.String] not specified, and parameter name information not found in class file either. 这个错误,是因为spring6.1 调整了参数,而springboot会自动帮助设置

    2024年02月04日
    浏览(49)
  • 【Spring实战项目】SpringBoot3整合WebSocket+拦截器实现登录验证!从原理到实战

    🎉🎉 欢迎光临,终于等到你啦 🎉🎉 🏅我是 苏泽 ,一位对技术充满热情的探索者和分享者。🚀🚀 🌟持续更新的专栏 《Spring 狂野之旅:从入门到入魔》 🚀 本专栏带你从Spring入门到入魔   这是苏泽的个人主页可以看到我其他的内容哦👇👇 努力的苏泽 http://suzee.blog.

    2024年04月17日
    浏览(58)
  • 使用Gradle7.6.1 + SpringBoot3.0.2 + java17创建微服务项目(学习)

    这是一个大胆的决定 技术 版本 spring-boot 3.0.2 spring-cloud 2022.0.2 spring-cloud-alibaba 2022.0.0.0-RC2 mybatis-plus-boot-starter 3.5.3.1 mysql-connector-java 8.0.32 技术 版本 java 17 gradle 7.6.1 IDEA 2022.2.4 Nvcat 15 MySQL 8.0.32 打开IDEA创建 SpringBoot项目 删除父项目中的src模块 新建两个子项目(新建时会重新创建

    2024年02月06日
    浏览(89)
  • JAVA新实战1:使用vscode+gradle+openJDK21搭建java springboot3项目开发环境

            作为一个干了多年的全栈技术工程师,厌倦了使用盗版IDE,近些年开发Java一直使用IntelliJ IDEA进行Springboot后端项目开发,对于IntelliJ IDEA 授权问题,一直花钱买学生类的授权,但经常被屏蔽,无法使用,又不舍得花大钱买企业版,索性不再使用了。决定改用 VsCode+Gr

    2024年02月03日
    浏览(65)
  • 【SpringBoot2】SpringBoot开发实用篇

    ​ 什么是热部署?简单说就是你程序改了,现在要重新启动服务器,嫌麻烦?不用重启,服务器会自己悄悄的把更新后的程序给重新加载一遍,这就是热部署。 ​ 热部署的功能是如何实现的呢?这就要分两种情况来说了,非springboot工程和springboot工程的热部署实现方式完全

    2023年04月25日
    浏览(45)
  • Java项目实战笔记--基于SpringBoot3.0开发仿12306高并发售票系统--(二)项目实现-第二篇-前端模块搭建及单点登录的实现

    本文参考自 Springboot3+微服务实战12306高性能售票系统 - 慕课网 (imooc.com) 本文是仿12306项目实战第(二)章——项目实现 的第二篇,详细讲解使用Vue3 + Vue CLI 实现前端模块搭建的过程,同时其中也会涉及一些前后端交互的实现,因此也会开发一些后端接口;搭建好前端页面后,

    2024年03月26日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包