news 2026/9/2 8:44:41

SpringBoot 2小时极速入门:从零构建RESTful API与数据库应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot 2小时极速入门:从零构建RESTful API与数据库应用

很多 Java 开发者,尤其是刚接触企业级开发的同学,在面对 Spring 框架复杂的 XML 配置时,常常感到无从下手。SpringBoot 的出现,正是为了解决这个痛点,它通过“约定大于配置”的理念,让开发者能快速搭建一个独立、生产级的 Spring 应用。本文将带你用最直接的方式,在 2 小时内快速上手 SpringBoot,避开那些新手常踩的坑,让你把时间花在业务逻辑上,而不是环境配置上。

1. SpringBoot 是什么?为什么是它?

在深入动手之前,我们先搞清楚两个核心问题:SpringBoot 是什么,以及为什么它现在几乎是 Java 后端开发的“标配”。

1.1 从 Spring 到 SpringBoot:化繁为简

Spring 框架无疑是 Java 企业开发的基石,它提供了强大的依赖注入(IoC)和面向切面编程(AOP)能力。但它的灵活性也带来了复杂性:大量的 XML 配置文件、繁琐的依赖管理和项目构建配置,让新手望而却步。

SpringBoot 是 Spring 官方团队提供的一个全新框架,它并不是用来替代 Spring 的,而是对 Spring 的一种“增强”和“封装”。它的核心设计目标是:

  • 简化配置:几乎零 XML 配置,大量使用 Java 注解和默认配置。
  • 内嵌容器:内置了 Tomcat、Jetty 或 Undertow 等 Web 服务器,无需再打包成 WAR 文件部署到外部容器。
  • 自动装配:根据项目类路径下的 Jar 包依赖,自动配置 Spring 应用。比如引入了spring-boot-starter-web,SpringBoot 就认为你要开发一个 Web 应用,并自动配置相关的 Bean(如 DispatcherServlet)。
  • 生产就绪:提供了一系列用于生产环境运行时的特性,如健康检查、指标收集、外部化配置等。

简单来说,SpringBoot 让你能用一个main方法就启动一个完整的 Web 应用,极大地提升了开发效率。

1.2 SpringBoot 的核心优势与应用场景

优势:

  1. 快速启动:几分钟内即可创建一个可运行的 Web 服务。
  2. 开箱即用:通过一系列spring-boot-starter-*依赖,轻松集成常用技术(如 Redis, MyBatis, Security 等)。
  3. 简化部署:打包成可执行的 JAR 文件,通过java -jar命令即可运行。
  4. 易于监控:内置 Actuator 模块,方便监控应用状态。

典型应用场景:

  • 微服务架构中的单个服务。
  • 快速构建 RESTful API 接口。
  • 开发后台管理系统。
  • 作为数据访问、消息中间件等组件的集成平台。

2. 环境准备:万事开头不难

工欲善其事,必先利其器。确保你的开发环境正确,是成功的第一步。

2.1 基础环境清单

你需要准备以下工具,版本建议选择主流稳定版:

  1. Java 开发工具包 (JDK):SpringBoot 3.x 需要JDK 17或更高版本;SpringBoot 2.x 需要JDK 8或更高版本。本文以目前广泛使用的 SpringBoot 2.7.x + JDK 8 为例。请确保java -version命令能正确输出。
  2. 构建工具MavenGradle。SpringBoot 官方推荐 Maven,国内使用广泛。请确保mvn -v命令能正确输出。
  3. 集成开发环境 (IDE)IntelliJ IDEA (推荐)或 Eclipse (STS)。IDEA 对 SpringBoot 的支持更为友好,提供了图形化的项目创建工具。
  4. 网络:能正常访问 Maven 中央仓库(或配置了国内镜像,如阿里云镜像)。

2.2 关键配置:Maven 镜像加速

国内访问国外仓库较慢,强烈建议配置阿里云镜像。找到你的 Maven 安装目录下的conf/settings.xml文件,在<mirrors>标签内添加:

<mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>

这个配置能极大提升依赖下载速度,是避免“卡在下载依赖”这个坑的关键。

3. 创建第一个 SpringBoot 项目:Hello World

我们将使用最常用的两种方式创建项目:通过 IDEA 的 Spring Initializr 和通过官方网站。推荐第一种,更集成化。

3.1 方式一:使用 IntelliJ IDEA 创建(推荐)

  1. 打开 IDEA,点击File->New->Project...
  2. 在左侧选择Spring Initializr
  3. Project SDK选择你安装的 JDK 8(或更高版本)。
  4. 点击Next,进入项目信息配置页面:
    • Group:通常为公司或组织域名倒写,如com.example
    • Artifact:项目名称,如demo
    • Type:选择Maven
    • Language:选择Java
    • Packaging:选择Jar(SpringBoot 推荐)
    • Java Version:选择8
    • Version:默认即可
  5. 点击Next,进入依赖选择页面。这是 SpringBoot 的核心便利之处。我们搜索并勾选:
    • Spring Web:用于构建 Web 应用,包含 RESTful API 支持。
    • (可选)Spring Boot DevTools:提供热部署功能,修改代码后无需重启。
    • (可选)Lombok:通过注解简化 Java Bean 的 Getter/Setter 等方法编写。
  6. 点击Next,选择项目存储路径,然后点击Finish

IDEA 会自动下载项目模板和初始依赖,生成一个完整的 SpringBoot 项目结构。

3.2 方式二:通过官网生成后导入

  1. 访问 Spring Initializr 。
  2. 在网页上选择与上述 IDEA 中类似的配置(Project: Maven, Language: Java, Spring Boot: 2.7.x)。
  3. Dependencies中添加Spring Web
  4. 点击Generate按钮,下载一个压缩包。
  5. 在 IDEA 中,File->Open...,选择解压后的文件夹即可。

3.3 项目结构解析

创建完成后,你会看到类似如下的目录结构:

demo ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ └── example │ │ │ └── demo │ │ │ └── DemoApplication.java // 主启动类 │ │ └── resources │ │ ├── application.properties // 配置文件(或 application.yml) │ │ └── static // 静态资源(CSS, JS, 图片) │ │ └── templates // 模板文件(如 Thymeleaf) │ └── test // 测试代码目录 └── pom.xml // Maven 项目对象模型文件
  • DemoApplication.java:这是整个应用的入口。类上的@SpringBootApplication注解是核心,它开启了 SpringBoot 的自动配置和组件扫描。
  • application.properties:主要的配置文件。所有关于端口、数据库连接、日志级别等的设置都在这里。你也可以使用application.yml,语法更简洁。
  • pom.xml:Maven 的依赖管理文件。你会看到父项目是spring-boot-starter-parent,并且引入了spring-boot-starter-web

4. 编写第一个 RESTful 接口

现在,让我们在 5 分钟内创建一个返回 “Hello, SpringBoot!” 的 HTTP 接口。

4.1 创建控制器 (Controller)

com.example.demo包下(或新建一个controller子包),创建一个新的 Java 类HelloController.java

package com.example.demo.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController // 1. 声明这是一个REST风格的控制器 @RequestMapping("/api") // 2. 定义类级别的请求路径前缀 public class HelloController { @GetMapping("/hello") // 3. 处理GET请求,路径为 /api/hello public String sayHello() { return "Hello, SpringBoot!"; } @GetMapping("/user") public User getUser() { User user = new User(); user.setId(1); user.setName("CSDN Reader"); return user; // 4. SpringBoot会自动将对象转换为JSON } // 内部类,用于演示返回JSON对象 static class User { private Integer id; private String name; // 省略 getter 和 setter,实际开发中请务必加上 // 如果使用了Lombok,可以用 @Data 注解替代 } }

代码解释:

  1. @RestController:组合了@Controller@ResponseBody,意味着这个类中的所有方法返回值都会直接写入 HTTP 响应体,而不是跳转到一个视图页面。这是开发 RESTful API 的标准注解。
  2. @RequestMapping(“/api”):为这个控制器中的所有方法指定一个统一的 URL 前缀。
  3. @GetMapping(“/hello”):一个具体的映射,将 HTTP GET 请求到/api/hello的请求,交给sayHello方法处理。
  4. 返回一个User对象:SpringBoot 默认使用 Jackson 库,会自动将 Java 对象序列化成 JSON 格式返回给客户端。

4.2 启动并测试应用

回到DemoApplication.java,直接运行其main方法。你会在控制台看到 SpringBoot 的启动日志,最后一行通常是:

Started DemoApplication in 2.345 seconds (JVM running for 3.012)

这表示你的应用已经在默认的 8080 端口启动了。

打开浏览器或使用 Postman、curl 等工具进行测试:

  • 访问http://localhost:8080/api/hello,页面会显示Hello, SpringBoot!
  • 访问http://localhost:8080/api/user,页面会显示 JSON 数据:{“id”:1, “name”:”CSDN Reader”}

恭喜!你的第一个 SpringBoot 应用已经成功运行了!

5. 核心配置详解:application.properties/yml

SpringBoot 的配置高度可定制,所有配置都集中在application.propertiesapplication.yml中。YAML 格式更清晰,推荐使用。

5.1 常用基础配置

src/main/resources下,将application.properties重命名为application.yml(或新建),并添加以下内容:

# 服务器配置 server: port: 8081 # 修改启动端口为8081 servlet: context-path: /boot # 应用上下文路径,访问地址变为 http://localhost:8081/boot/api/hello # Spring 应用配置 spring: application: name: demo-application # 应用名称,用于服务发现、监控等 # 数据源配置 (以H2内存数据库为例,无需安装) datasource: url: jdbc:h2:mem:testdb # H2内存数据库连接地址 driver-class-name: org.h2.Driver username: sa password: # 启用H2数据库控制台 h2: console: enabled: true # 访问 http://localhost:8081/boot/h2-console path: /h2-console # 日志配置 logging: level: com.example.demo: DEBUG # 将我们自己的包日志级别设为DEBUG,便于调试 org.springframework: INFO file: name: logs/app.log # 将日志输出到文件

配置说明:

  • server.port:这是新手最常改的配置之一,解决端口冲突问题。
  • spring.datasource:一旦配置了数据源,SpringBoot 会自动配置一个DataSourceBean 和相关的数据库连接池(如 HikariCP)。
  • spring.h2.console.enabled:H2 是一个纯 Java 编写的内存数据库,非常适合演示和测试。开启控制台后可以通过网页访问和管理数据库。

5.2 多环境配置

在实际开发中,我们需要为开发、测试、生产等不同环境使用不同的配置。SpringBoot 支持通过文件名来区分。

  1. 主配置文件application.yml,存放通用配置。
  2. 环境特定配置文件
    • application-dev.yml:开发环境配置。
    • application-test.yml:测试环境配置。
    • application-prod.yml:生产环境配置。

application.yml中,使用spring.profiles.active来激活特定环境的配置:

spring: profiles: active: dev # 激活 dev 环境配置

当应用启动时,会先加载application.yml,再加载application-dev.yml,后者会覆盖前者的相同配置项。

启动时指定环境(优先级更高):

  • 命令行java -jar demo.jar --spring.profiles.active=prod
  • IDEA 启动配置:在Program arguments中添加--spring.profiles.active=test

6. 连接数据库:Spring Data JPA 实战

Web 应用离不开数据库。SpringBoot 通过spring-boot-starter-data-jpa可以极简地操作数据库。

6.1 添加依赖与配置

首先,在pom.xml中添加 JPA 和 H2 数据库的依赖(如果之前没加的话):

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> <!-- 运行时才需要 --> </dependency>

配置已经在上一节的application.yml中完成了(spring.datasourcespring.h2.console)。

6.2 创建实体类 (Entity)

实体类对应数据库中的表。在com.example.demo.entity包下创建User.java

package com.example.demo.entity; import javax.persistence.*; @Entity // 1. 标明这是一个JPA实体类,对应数据库中的一张表 @Table(name = "t_user") // 2. 指定表名,默认为类名user public class User { @Id // 3. 标明主键 @GeneratedValue(strategy = GenerationType.IDENTITY) // 4. 主键生成策略:自增 private Long id; @Column(name = "username", nullable = false, length = 50) // 5. 对应列定义 private String name; private Integer age; private String email; // 省略构造方法、getter、setter、toString方法 // 务必生成!可以使用IDE快捷键或Lombok的 @Data 注解 }

6.3 创建数据访问层 (Repository)

Spring Data JPA 最强大的地方在于它的 Repository 接口。在com.example.demo.repository包下创建UserRepository.java

package com.example.demo.repository; import com.example.demo.entity.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; @Repository // 可省略,Spring会自动扫描继承JpaRepository的接口 public interface UserRepository extends JpaRepository<User, Long> { // 无需实现任何方法!JpaRepository已提供了基础的CRUD方法。 // 自定义查询:根据姓名查询 List<User> findByName(String name); // 自定义查询:根据年龄大于某个值查询 List<User> findByAgeGreaterThan(Integer age); // 复杂查询可以使用 @Query 注解写JPQL或原生SQL // @Query("SELECT u FROM User u WHERE u.email LIKE %?1%") // List<User> findByEmailContaining(String email); }

神奇之处:你只需要定义一个接口,并继承JpaRepository<实体类, 主键类型>,Spring Data JPA 就会在运行时自动为你生成这个接口的实现。方法名遵循特定的规则(如findByAgeGreaterThan),就能自动解析成对应的 SQL 查询。

6.4 在控制器中调用 Repository

修改之前的HelloController或新建一个UserController来测试数据库操作。

package com.example.demo.controller; import com.example.demo.entity.User; import com.example.demo.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/users") public class UserController { @Autowired // 自动注入UserRepository实例 private UserRepository userRepository; @PostMapping public User createUser(@RequestBody User user) { // @RequestBody 将请求体中的JSON自动绑定到User对象 return userRepository.save(user); // 保存用户 } @GetMapping("/{id}") public User getUserById(@PathVariable Long id) { return userRepository.findById(id).orElse(null); // 根据ID查询 } @GetMapping public List<User> getAllUsers() { return userRepository.findAll(); // 查询所有用户 } @GetMapping("/search") public List<User> getUsersByName(@RequestParam String name) { return userRepository.findByName(name); // 调用自定义方法 } }

6.5 测试与验证

  1. 重启应用(如果 DevTools 生效则自动重启)。
  2. 使用 Postman 或 curl 测试:
    • POSThttp://localhost:8081/boot/api/usersBody (raw JSON):{“name”: “张三”, “age”: 25, “email”: “zhangsan@example.com”}响应会返回带 ID 的用户信息。
    • GEThttp://localhost:8081/boot/api/users查看所有用户。
    • GEThttp://localhost:8081/boot/api/users/search?name=张三根据名字查询。
  3. 访问http://localhost:8081/boot/h2-console查看 H2 数据库控制台。
    • JDBC URL:jdbc:h2:mem:testdb
    • User Name:sa
    • Password: (空) 点击 Connect,可以看到自动创建的T_USER表和里面的数据。

7. 常见问题与避坑指南

在入门过程中,你几乎一定会遇到下面这些问题。提前了解,能节省大量排查时间。

7.1 启动类位置与组件扫描

问题:自己创建的@Controller,@Service,@Repository等注解的类,SpringBoot 没有扫描到,导致注入失败或 404。

原因:SpringBoot 默认只扫描主启动类所在包及其子包下的组件。

解决

  • 最佳实践:将所有自定义类都放在主启动类所在的包(如com.example.demo)或其子包下(如com.example.demo.controller)。
  • 如果非要放在其他位置,可以在主启动类上使用@ComponentScan(basePackages = {“com.example”})指定扫描路径。

7.2 端口被占用

问题:启动时报错Web server failed to start. Port 8080 was already in use.

解决

  1. application.yml中修改server.port,如改为8081
  2. 查找并关闭占用端口的进程(命令行:netstat -ano | findstr :8080,然后taskkill /PID <进程号> /F)。

7.3 依赖冲突与版本问题

问题:引入某个 starter 或第三方库后,项目启动报ClassNotFoundException,NoSuchMethodErrorBeanCreationException

原因:通常是 Jar 包版本冲突,或者 SpringBoot 父 Pom 管理的版本与手动引入的版本不一致。

解决

  1. 优先使用 SpringBoot 官方 Starter:它们经过了严格的版本兼容性测试。
  2. 查看依赖树:在项目根目录运行mvn dependency:tree,查看是否有重复或冲突的依赖。
  3. 排除冲突依赖:在pom.xml中,使用<exclusions>标签排除掉冲突的传递性依赖。
    <dependency> <groupId>problematic.group</groupId> <artifactId>problematic-artifact</artifactId> <exclusions> <exclusion> <groupId>conflict.group</groupId> <artifactId>conflict-artifact</artifactId> </exclusion> </exclusions> </dependency>
  4. 统一版本管理:对于非 Starter 管理的依赖,可以在<properties>标签中定义版本号,然后在多个地方引用。

7.4 配置文件不生效

问题:修改了application.yml中的配置,但重启后没有效果。

排查

  1. 检查配置文件名称和位置是否正确 (src/main/resources)。
  2. 检查 YAML 语法,特别是缩进(必须使用空格,不能使用 Tab)。
  3. 检查配置项的拼写是否正确,SpringBoot 的配置项是kebab-case(短横线分隔),如spring.datasource.url
  4. 确认是否被更高优先级的配置覆盖(如命令行参数、系统环境变量)。

7.5 热部署失效

问题:使用了spring-boot-devtools,但修改 Java 代码后应用没有自动重启。

解决

  1. 确保 IDEA 开启了自动编译:File->Settings->Build, Execution, Deployment->Compiler-> 勾选Build project automatically
  2. Ctrl+Shift+Alt+/,选择Registry...,勾选compiler.automake.allow.when.app.running
  3. DevTools 默认只监控classpath下的文件变化。对于静态资源的热加载,可能需要手动触发(Ctrl+F9构建一下)。

8. 下一步学习路线与最佳实践

通过上面的步骤,你已经成功搭建了一个具备 Web 接口和数据库访问能力的 SpringBoot 应用。但这只是起点,要用于实际项目,还需要掌握更多。

8.1 推荐学习路径

  1. 巩固核心:深入理解 SpringBoot 的自动装配原理@SpringBootApplication,@EnableAutoConfiguration)和启动过程
  2. 数据持久化
    • 掌握Spring Data JPA的复杂查询、分页、排序。
    • 学习MyBatis-Plus(国产,功能强大),它是 MyBatis 的增强工具,在复杂 SQL 和国内开发环境中更受欢迎。
  3. Web 开发进阶
    • 统一响应封装:设计一个通用的Result类来包装所有接口的返回结果(包含 code, msg, data)。
    • 全局异常处理:使用@ControllerAdvice@ExceptionHandler捕获并统一处理异常,给前端友好的错误信息。
    • 参数校验:使用javax.validation注解(如@NotNull,@Email)或 Hibernate Validator 校验请求参数。
    • 接口文档:集成Swagger/OpenAPIKnife4j,自动生成和测试 API 文档。
  4. 安全与权限:集成Spring Security,实现用户认证(登录)和授权(权限控制)。
  5. 缓存与性能:集成Redis,用于缓存热点数据,提升性能。
  6. 消息队列:集成RabbitMQKafka,实现应用解耦和异步处理。
  7. 微服务生态:学习Spring Cloud系列组件(Nacos, OpenFeign, Gateway, Sentinel),构建分布式系统。

8.2 工程化最佳实践

  1. 项目分层:严格遵守controller(控制层)、service(业务层)、repository/dao(数据层)、entity/model(实体层)的分层架构。controller只负责参数校验和路由,业务逻辑放在service中。
  2. 使用 Lombok:在pom.xml中引入 Lombok 依赖,并在 IDEA 中安装 Lombok 插件。使用@Data,@Getter,@Setter,@NoArgsConstructor,@AllArgsConstructor等注解,可以极大减少样板代码。
  3. 日志规范:不要使用System.out.println()。使用 SLF4J 门面配合 Logback/Log4j2。在方法开始、结束、关键分支处记录日志,使用@Slf4j注解(Lombok 提供)快速注入日志对象。
  4. 配置文件管理:敏感信息(如数据库密码)绝不能硬编码在配置文件中。应使用环境变量、配置中心(如 Apollo, Nacos Config)或启动参数传入。application.yml中可以使用${VARIABLE:default}引用环境变量。
  5. 单元测试:为servicecontroller编写单元测试(使用 JUnit 5 和 SpringBootTest),这是保证代码质量的重要手段。
  6. 打包与部署:使用mvn clean package打包,生成的可执行 Jar 文件位于target目录。生产环境使用java -jar -Dspring.profiles.active=prod your-app.jar启动。考虑使用 Docker 容器化部署,环境更一致。

SpringBoot 的强大在于其生态和“开箱即用”的理念。入门后,你的学习重心应从“如何配置”转向“如何更好地设计业务代码”和“如何利用生态组件解决实际问题”。多动手,多踩坑,多查阅官方文档,是快速成长的不二法门。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 8:43:42

AI多智能体协作编程实战:从概念到项目落地

如果你在2023年初告诉一个开发者&#xff0c;AI能在9个月内从“帮你写几行代码”进化到“自主规划、协作完成整个项目”&#xff0c;他大概率会觉得你在讲科幻故事。但今天&#xff0c;这已经是正在发生的现实。从OpenAI Codex的惊艳亮相&#xff0c;到如今多智能体&#xff08…

作者头像 李华
网站建设 2026/9/2 8:43:39

C++:5.继承

继承 1.概念 继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段&#xff0c;它允许我们在保持原有类特性的基础上进⾏扩展&#xff0c;增加⽅法(成员函数)和属性(成员变量)&#xff0c;这样产⽣新的类&#xff0c;称派⽣类。2.定义 eg class Person{pub…

作者头像 李华
网站建设 2026/9/2 8:41:41

河道垃圾检测数据集解析与YOLOv8实战:从数据标注到模型部署

简介&#xff1a;本资源是面向计算机视觉初学者与环保智能监测项目开发者的河道垃圾检测专用数据集&#xff0c;聚焦水体环境下的常见废弃物识别任务&#xff0c;适用于目标检测模型训练、算法验证及智慧水务应用原型开发。压缩包共2000个文件&#xff0c;含1999个Pascal VOC格…

作者头像 李华
网站建设 2026/9/2 8:41:30

Python LSTM实战:量化投资中的时间序列预测与模型调优

简介&#xff1a;本资源是一份面向计算机及相关专业本科生的毕业设计实战项目&#xff0c;聚焦使用Python与LSTM&#xff08;长短期记忆网络&#xff09;构建股票及基金价格预测模型&#xff0c;解决金融时间序列建模与预测这一典型机器学习应用场景。资源包共11个文件&#xf…

作者头像 李华
网站建设 2026/9/2 8:40:11

2026年AI影视制作平台推荐排行,谁在构建系统化壁垒

2026年以来&#xff0c;AI影视制作行业迎来一个明显的分水岭——底层模型能力差距正在收窄&#xff0c;纯“生成效果”已不再是评判平台的唯一标准。创作者和团队真正需要的&#xff0c;是一套能从剧本走到成片、从创作走到变现的完整系统。蛙蛙写作是轻量化短剧工具的代表&…

作者头像 李华
网站建设 2026/9/2 8:40:09

自动化测试学习笔记

元素的定位cssSelector查找一个元素查找多个相同的元素By.by 通过_选择器Xpath 定位格式&#xff1a;//*[ ]获取HTML界面所有的节点获取HTML页面指定的节点——//_获取一个节点中的子节点——//span/input获取一个节点中的父节点——//intput/..实现节点属性的匹配——[...]//b…

作者头像 李华