Spring Boot 2 到 3 升级详细步骤

Spring Boot 2 到 3 升级详细步骤

项目升级概览

基于 公司科创项目 的实际升级经验,本文档详细记录了从 Spring Boot 2.7.0 升级到 Spring Boot 3.2.5 的完整过程。

升级时间线

  • 升级前版本: Spring Boot 2.7.0
  • 升级后版本: Spring Boot 3.2.5
  • Java 版本: 升级到 Java 17
  • 主要升级时间: 2025年6月

第一步:环境准备

1.1 Java 环境升级

检查当前 Java 版本:

java -version

安装 Java 17:

# macOS (使用 Homebrew)
brew install openjdk@17

# 或者下载官方 JDK 17
# https://www.oracle.com/java/technologies/downloads/#java17

设置 JAVA_HOME:

export JAVA_HOME=/Library/Java/JavaVirtualMachines/openjdk-17.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH

1.2 Maven 配置更新

更新 Maven 编译器插件:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.11.0</version>
    <configuration>
        <source>17</source>
        <target>17</target>
        <encoding>UTF-8</encoding>
        <release>17</release>
        <parameters>true</parameters>
        <compilerArgs>
            <arg>--add-opens</arg>
            <arg>jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
            <arg>--add-opens</arg>
            <arg>jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
        </compilerArgs>
    </configuration>
</plugin>

第二步:核心依赖升级

2.1 Spring Boot 版本升级

父 POM 更新:

<properties>
    <spring-boot.version>3.2.5</spring-boot.version>
    <java.version>17</java.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Spring Cloud 版本升级:

<properties>
    <spring-cloud.version>2022.0.4</spring-cloud.version>
    <spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
</properties>

2.2 数据库相关依赖升级

MyBatis Plus 升级:

<!-- 升级前 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>

<!-- 升级后 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.5</version>
</dependency>

重要变更:

  • 主键生成策略 ID_WORKER_STR 被废弃,使用 ASSIGN_ID 替代
  • selectCount 方法不再返回 int 类型,现在返回 long 类型
// 升级前
@TableId(type = IdType.ID_WORKER_STR)
private String id;

int count = userMapper.selectCount(wrapper);

// 升级后
@TableId(type = IdType.ASSIGN_ID)
private String id;

long count = userMapper.selectCount(wrapper);

动态数据源升级:

<!-- 升级前 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.4.1</version>
</dependency>

<!-- 升级后 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
    <version>4.3.1</version>
</dependency>

注意: 如果不升级到 4.3.1 版本,可能会出现数据源配置读取失败的错误:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Druid 连接池升级:

<!-- 升级前 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.9</version>
</dependency>

<!-- 升级后 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-3-starter</artifactId>
    <version>1.2.25</version>
</dependency>

分页插件升级:

<!-- 升级前 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.3.0</version>
</dependency>

<!-- 升级后 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>

ShardingSphere 升级:

<!-- 升级前 -->
<dependency>
    <groupId>io.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>4.1.0</version>
</dependency>

<!-- 升级后 -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.1.0</version>
</dependency>

注意: 如果不升级到 5.1.0 版本,可能读取不到数据库配置。

第三步:包名迁移 (javax → jakarta)

3.1 核心包名变更

这是升级过程中最重要的变更之一。Spring Boot 3 将所有的 javax.* 包迁移到 jakarta.*

主要变更列表:

升级前升级后
javax.annotation.*jakarta.annotation.*
javax.servlet.*jakarta.servlet.*
javax.persistence.*jakarta.persistence.*
javax.validation.*jakarta.validation.*
javax.transaction.*jakarta.transaction.*
javax.xml.*jakarta.xml.*

3.2 具体代码变更示例

注解变更:

// 升级前
import javax.annotation.PostConstruct;
import javax.annotation.Resource;

// 升级后
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;

Servlet 相关变更:

// 升级前
import javax.servlet.Servlet;
import javax.servlet.Filter;
import javax.servlet.ServletRegistrationBean;

// 升级后
import jakarta.servlet.Servlet;
import jakarta.servlet.Filter;
import jakarta.servlet.ServletRegistrationBean;

Druid 配置变更:

// 升级前
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;

// 升级后
import com.alibaba.druid.support.jakarta.StatViewServlet;
import com.alibaba.druid.support.jakarta.WebStatFilter;

3.3 批量替换脚本

可以使用以下命令批量替换包名:

# 替换 javax.annotation 为 jakarta.annotation
find . -name "*.java" -exec sed -i '' 's/javax\.annotation/jakarta.annotation/g' {} \;

# 替换 javax.servlet 为 jakarta.servlet
find . -name "*.java" -exec sed -i '' 's/javax\.servlet/jakarta.servlet/g' {} \;

# 替换 javax.persistence 为 jakarta.persistence
find . -name "*.java" -exec sed -i '' 's/javax\.persistence/jakarta.persistence/g' {} \;

第四步:API 文档框架迁移

4.1 Swagger 到 SpringDoc OpenAPI

依赖变更:

<!-- 移除旧的 Swagger 依赖 -->
<!--
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-boot-starter</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>swagger-bootstrap-ui</artifactId>
    <version>1.9.6</version>
</dependency>
-->

<!-- 添加新的 SpringDoc OpenAPI 依赖 -->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.8.8</version>
</dependency>

注解映射表:

Swagger 注解SpringDoc OpenAPI 注解
@Api@Tag
@ApiOperation@Operation
@ApiParam@Parameter
@ApiModel@Schema
@ApiModelProperty@Schema
@ApiResponse@ApiResponse
@ApiResponses@ApiResponses

具体代码变更:

// 升级前
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

@Api(tags = "用户管理")
@RestController
public class UserController {
    
    @ApiOperation("获取用户信息")
    @GetMapping("/user/{id}")
    public User getUser(@ApiParam("用户ID") @PathVariable String id) {
        return userService.getUser(id);
    }
}

// 升级后
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;

@Tag(name = "用户管理")
@RestController
public class UserController {
    
    @Operation(summary = "获取用户信息")
    @GetMapping("/user/{id}")
    public User getUser(@Parameter(description = "用户ID") @PathVariable String id) {
        return userService.getUser(id);
    }
}

4.2 Knife4j 升级

依赖变更:

<!-- 升级前 -->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>2.0.8</version>
</dependency>

<!-- 升级后 -->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
    <version>4.4.0</version>
</dependency>

注意: Knife4j 4.4.0 版本较高,公司私服和阿里云镜像中可能不存在,需要添加镜像源:

<repository>
    <id>central</id>
    <name>Maven Central</name>
    <url>https://repo1.maven.org/maven2</url>
</repository>

第五步:安全认证框架升级

5.1 Sa-Token 升级

依赖变更:

<!-- 升级前 -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot-starter</artifactId>
    <version>1.31.0</version>
</dependency>

<!-- 升级后 -->
<dependency>
    <groupId>cn.dev33</groupId>
    <artifactId>sa-token-spring-boot3-starter</artifactId>
    <version>1.34.0</version>
</dependency>

代码变更:

// 升级前
import cn.dev33.satoken.id.SaIdUtil;

public class TokenUtil {
    public static String getInnerAuthToken() {
        return SaIdUtil.getToken();
    }
    
    public static void checkInnerToken(String token) {
        SaIdUtil.checkToken(token);
    }
}

// 升级后
import cn.dev33.satoken.stp.StpUtil;

public class TokenUtil {
    public static String getInnerAuthToken() {
        return StpUtil.getTokenValue();
    }
    
    public static void checkInnerToken(String token) {
        // 根据新版本API调整
        // SaIdUtil.checkToken(token);
    }
}

5.2 Shiro 升级

依赖变更:

<!-- 升级前 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>2.0.4</version>
</dependency>

<!-- 升级后 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-jakarta-ee</artifactId>
    <version>2.0.4</version>
</dependency>

注意: shiro-spring-boot-starter 2.0.4 不再支持 jakarta,需要使用 shiro-jakarta-ee

5.3 OAuth2 升级

升级到 Spring Authorization Server:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
</dependency>

注意: 授权中心需要重构,使用 Spring Authorization Server 替换原有的 OAuth2 实现。

5.4 JWT 升级

依赖变更:

<!-- 升级前 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

<!-- 升级后 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.3</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.3</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.12.3</version>
    <scope>runtime</scope>
</dependency>

代码变更:

// 升级前
Claims claims = Jwts.parser()
    .setSigningKey(secret)
    .parseClaimsJws(token)
    .getBody();

// 升级后
SecretKey key = Keys.hmacShaKeyFor(TokenConstants.SECRET.getBytes());
JwtParser parser = Jwts.parser().verifyWith(key).build();
Claims claims = parser.parseSignedClaims(token).getPayload();

第六步:工具库升级

6.1 常用工具库版本升级

<properties>
    <!-- Apache Commons -->
    <common-lang3.version>3.13.0</common-lang3.version>
    <commons-io.version>2.15.1</commons-io.version>
    <commons-text.version>1.11.0</commons-text.version>
    
    <!-- JSON 处理 -->
    <fastjson.version>2.0.43</fastjson.version>
    <jackson.version>2.15.3</jackson.version>
    
    <!-- 其他工具库 -->
    <hutool-all.version>5.8.24</hutool-all.version>
    <guava.version>33.0.0-jre</guava.version>
    <okhttp3.version>4.12.0</okhttp3.version>
    <lombok.version>1.18.30</lombok.version>
</properties>

6.2 Apache POI 升级

依赖变更:

<!-- 升级前 -->
<poi.version>4.1.2</poi.version>

<!-- 升级后 -->
<poi.version>5.2.3</poi.version>

6.3 包名变更处理

Apache Commons Lang:

// 升级前
import org.apache.commons.lang.StringUtils;

// 升级后
import org.apache.commons.lang3.StringUtils;

6.4 Base64 编码解码变更

问题: Cannot resolve symbol 'BASE64Decoder'

解决方案: 使用标准的 java.util.Base64 替代

// 升级前(解码)
static BASE64Decoder decoder = new BASE64Decoder();
byte[] b = decoder.decodeBuffer(regReplaceBase64(imgStr));

// 升级后(解码)
byte[] b = Base64.getDecoder().decode(regReplaceBase64(imgStr));

// 升级前(编码)
BASE64Encoder encode = new BASE64Encoder();
String s = encode.encode(data);

// 升级后(编码)
String s = Base64.getEncoder().encodeToString(data);

6.5 XPath 包名变更

问题: Package 'com.sun.org.apache.xpath.internal.operations' is declared in module 'java.xml', which does not export it to the unnamed module

解决方案: 使用 javax.xml.xpath 替代

// 升级前
import com.sun.org.apache.xpath.internal.operations.String;

// 升级后
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

第七步:配置类调整

7.1 动态数据源配置调整

配置类变更:

// 升级前
@Configuration
public class MybatisPlusConfig {
    
    @Primary
    @Bean(name = "dataSourceSystem")
    public DataSource dataSourceOne(DynamicDataSourceProperties properties) throws SQLException, IOException, URISyntaxException {
        DataSource dataSource = dynamicDataSource(properties);
        initDynamicDataSource(dataSource, properties);
        return dataSource;
    }
}

// 升级后
@Configuration
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class MybatisPlusConfig {
    
    private final DynamicDataSourceProperties dynamicDataSourceProperties;
    
    public MybatisPlusConfig(DynamicDataSourceProperties dynamicDataSourceProperties) {
        this.dynamicDataSourceProperties = dynamicDataSourceProperties;
    }
    
    @Primary
    @Bean
    public DynamicDataSourceProperties primaryDynamicDataSourceProperties() {
        return dynamicDataSourceProperties;
    }
    
    @Primary
    @Bean(name = "dataSourceSystem")
    public DataSource dataSourceOne() throws SQLException, IOException, URISyntaxException {
        DataSource dataSource = dynamicDataSource(dynamicDataSourceProperties);
        initDynamicDataSource(dataSource, dynamicDataSourceProperties);
        return dataSource;
    }
}

7.2 Druid 配置调整

Druid 配置类变更:

// 升级前
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;

@Bean
public ServletRegistrationBean statViewServlet(){
    ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
    // ...
}

// 升级后
import com.alibaba.druid.support.jakarta.StatViewServlet;
import com.alibaba.druid.support.jakarta.WebStatFilter;
import jakarta.servlet.Filter;
import jakarta.servlet.Servlet;

@Bean
public ServletRegistrationBean statViewServlet(){
    ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
    // ...
}

7.3 循环依赖配置

应用配置调整:

# 解决循环依赖问题
spring:
  main:
    allow-circular-references: true

7.4 参数名发现器变更

LocalVariableTableParameterNameDiscoverer 被废弃:

// 升级前
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

// 升级后
import org.springframework.core.StandardReflectionParameterNameDiscoverer;

第八步:异常处理调整

8.1 移除不必要的异常声明

// 升级前
public static Connection getConn(DbLinkEntity dbLinkEntity) throws DataException {
    try{
        return new PrepSqlDTO().withConn(dbLinkEntity).switchConn().getConnection();
    }catch (SQLException d){
        throw new DataException("数据库连接失败");
    }
}

// 升级后
public static Connection getConn(DbLinkEntity dbLinkEntity) {
    try{
        return new PrepSqlDTO().withConn(dbLinkEntity).switchConn().getConnection();
    }catch (SQLException d){
        throw new RuntimeException("数据库连接失败", d);
    }
}

第九步:测试和验证

9.1 编译测试

# 清理并编译
mvn clean compile

# 运行测试
mvn test

# 打包
mvn package -DskipTests

9.2 功能测试

  1. 数据库连接测试
  2. API 接口测试
  3. 认证授权测试
  4. 文件上传下载测试

9.3 性能测试

  1. 启动时间对比
  2. 内存使用情况
  3. 响应时间对比

第十步:常见问题解决

10.1 Lombok 编译问题

问题: Unable to make field private com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors com.sun.tools.javac.processing.JavacProcessingEnvironment.discoveredProcs accessible: module jdk.compiler does not "opens com.sun.tools.javac.processing" to unnamed module

解决方案:

  1. 升级 Lombok 到 1.18.30
  2. 升级 maven-compiler-plugin 到 3.11.0
  3. 添加编译器参数
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.11.0</version>
    <configuration>
        <source>17</source>
        <target>17</target>
        <release>17</release>
        <compilerArgs>
            <arg>--add-opens</arg>
            <arg>jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED</arg>
            <arg>--add-opens</arg>
            <arg>jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
        </compilerArgs>
    </configuration>
</plugin>

10.2 包名冲突问题

问题: 编译时出现 javax.*jakarta.* 包冲突

解决方案:

<dependency>
    <groupId>conflicting-library</groupId>
    <artifactId>conflicting-artifact</artifactId>
    <version>1.0.0</version>
    <exclusions>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

10.3 配置类加载失败

问题: 配置类无法正确加载

解决方案:

  1. 检查包名是否正确
  2. 确保使用 jakarta.*
  3. 检查依赖版本兼容性

10.4 数据库连接问题

问题: 数据库连接失败

解决方案:

  1. 检查驱动版本兼容性
  2. 更新连接配置
  3. 验证数据库服务状态

10.5 Maven 打包问题

问题: Maven 打包时出现模块访问权限错误

解决方案: 添加 VM 参数

mvn clean package -Dmaven.compiler.fork=true -Dmaven.compiler.compilerArgs="--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"

升级检查清单

升级前检查

  • 创建代码备份分支
  • 记录当前系统状态
  • 准备测试环境
  • 确认 Java 17 环境
  • 备份数据库配置

升级过程检查

  • 更新 Spring Boot 版本
  • 更新 Java 版本配置
  • 迁移包名 (javax → jakarta)
  • 更新数据库相关依赖
  • 迁移 API 文档框架
  • 更新安全认证框架
  • 升级工具库版本
  • 调整配置文件
  • 修复编译错误
  • 运行单元测试
  • 升级 Lombok 和编译器插件
  • 处理 Base64 编码解码变更
  • 更新 XPath 包名
  • 替换废弃的类和方法
  • 配置循环依赖允许
  • 更新 JWT 解析方式

升级后验证

  • 编译通过
  • 单元测试通过
  • 集成测试通过
  • 功能测试通过
  • 性能测试通过
  • 文档更新
  • 部署验证

总结

通过本项目的实际升级经验,Spring Boot 2 到 3 的升级主要涉及以下几个方面:

  1. 环境升级: Java 17 + Maven 3.11.0
  2. 包名迁移: javax → jakarta
  3. 依赖升级: 多个核心依赖需要升级到兼容版本
  4. API 变更: 部分 API 接口发生变化
  5. 配置调整: 配置文件需要相应调整
  6. 工具库变更: Base64、XPath 等工具类的使用方式发生变化
  7. 框架重构: 授权中心、JWT 解析等需要重构

升级过程中需要特别注意:

  • 逐步进行,避免一次性修改过多内容
  • 充分测试每个模块的变更
  • 保持良好的版本控制和回滚机制
  • 及时更新相关文档
  • 注意依赖版本查询:https://mvnrepository.com/
### Spring Boot 2.x 升级Spring Boot 3.x 的步骤 #### 准备工作 确保项目依赖管理工具(Maven 或 Gradle)是最新的稳定版本。对于 Maven 用户,建议使用最新发布的 Apache Maven 版本;Gradle 用户应确认使用的 Gradle 是最新的长期支持版。 #### 修改依赖版本号 在项目的 `pom.xml` 文件中找到 `<parent>` 节点下的 `<version>` 属性,并将其值更改为目标版本 "3.0.0" 或更高版本[^1]: ```xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.0.0</version> <!-- ... --> </parent> ``` 如果使用的是 Gradle,则需修改 `build.gradle` 中的相关部分: ```groovy plugins { id &#39;org.springframework.boot&#39; version &#39;3.0.0&#39; } ``` #### 更新其他依赖项 除了更改父 POM/插件版本外,还需同步调整应用程序所依赖的库及其版本,特别是那些由 Spring 提供的基础组件,如 spring-webmvc, spring-data-jpa 等等。这一步骤非常重要,因为不同版本之间可能存在不兼容的情况。 #### 解决潜在问题 随着框架本身的演进,在某些情况下可能会引入破坏性的变更。因此,在完成上述改动之后,应该仔细阅读官方文档中的迁移指南以及发行说明来识别可能影响现有功能的地方。例如,Java 版本的要求提升到了 JDK 17+,这意味着需要先安装相应环境才能继续开发和运行程序[^4]。 #### 测试验证 最后也是最关键的一环就是进行全面的功能性和性能测试。通过单元测试、集成测试等方式确保升级后的系统仍然能够正常运作并且满足预期的质量标准。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值