Spring Boot 初体验:满足基本增删改查

262 阅读9分钟

引言

Spring Boot 是一个用于简化 Spring 应用程序开发的框架。它提供了自动配置和约定优于配置的原则,使得开发者能够快速创建独立的、生产级别的 Spring 应用程序。本文将详细介绍如何使用 Spring Boot 创建一个包含基本增删改查(CRUD)功能的项目。

一、创建 Spring Boot 项目

首先,我们需要创建一个新的 Spring Boot 项目。可以通过 Spring Initializr(start.spring.io/)来快速生成项目结构。

1.1 配置项目基本信息

Project:Maven Project • Language:Java • Spring Boot:选择最新稳定版本 • Project Metadata: • Group:com.example.demo • Artifact:demo • Name:demo • Description:Demo project for Spring Boot CRUD operations • Package name:com.example.demo

1.2 添加依赖

pom.xml 文件中添加以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

1.3 创建项目结构

创建以下目录结构:

src/main/java/com/example/demo
  ├── DemoApplication.java
  ├── model
  │   └── User.java
  ├── repository
  │   └── UserRepository.java
  ├── service
  │   └── UserService.java
  └── controller
      └── UserController.java

二、实现基本增删改查功能

2.1 创建实体类

User.java

package com.example.demo.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
    private String email;

    // Getters and setters
}

2.2 创建数据访问层

UserRepository.java

package com.example.demo.repository;

import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

2.3 创建服务层

UserService.java

package com.example.demo.service;

import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> findAll() {
        return userRepository.findAll();
    }

    public Optional<User> findById(Long id) {
        return userRepository.findById(id);
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public void deleteById(Long id) {
        userRepository.deleteById(id);
    }
}

2.4 创建控制层

UserController.java

package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        List<User> users = userService.findAll();
        return new ResponseEntity<>(users, HttpStatus.OK);
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        User user = userService.findById(id).orElseThrow(() -> new ResourceNotFoundException("User not found with ID: " + id));
        return new ResponseEntity<>(user, HttpStatus.OK);
    }

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody User user) {
        User savedUser = userService.save(user);
        return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
        User updatedUser = userService.findById(id).map(existingUser -> {
            existingUser.setName(user.getName());
            existingUser.setEmail(user.getEmail());
            return userService.save(existingUser);
        }).orElseThrow(() -> new ResourceNotFoundException("User not found with ID: " + id));
        return new ResponseEntity<>(updatedUser, HttpStatus.OK);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.deleteById(id);
        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
    }
}

2.5 异常处理

ResourceNotFoundException.java

package com.example.demo.exceptions;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

三、运行项目

3.1 启动应用

在 IntelliJ IDEA 中,右键点击 DemoApplication.java 文件,选择 Run 'DemoApplication'。或者使用命令行:

./mvnw spring-boot:run

3.2 测试 API

启动应用后,你可以使用以下 URL 来测试增删改查功能:

• 获取所有用户:http://localhost:8080/users • 获取指定 ID 的用户:http://localhost:8080/users/{id} • 创建新用户:http://localhost:8080/users(提交 POST 请求,包含 JSON 数据) • 更新指定 ID 的用户:http://localhost:8080/users/{id}(提交 PUT 请求,包含 JSON 数据) • 删除指定 ID 的用户:http://localhost:8080/users/{id}(提交 DELETE 请求)

四.疑问

4.1 项目的启动入口是哪里?

DemoApplication.java 文件是 Spring Boot 项目的入口文件。当你启动 Spring Boot 应用时,执行的入口点就是这个文件中的 main 方法。

详细解释

DemoApplication.java 文件

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

关键点

  1. @SpringBootApplication 注解: • 这个注解是一个组合注解,包含了 @Configuration@EnableAutoConfiguration@ComponentScan。 • 它告诉 Spring Boot 这是一个 Spring Boot 应用,并启用自动配置和组件扫描。

  2. main 方法: • public static void main(String[] args) 是 Java 应用的标准入口点。 • SpringApplication.run(DemoApplication.class, args); 这行代码启动 Spring Boot 应用,初始化 Spring 应用上下文,并加载所有配置的 Bean。

执行流程

  1. 启动应用: • 当你运行 DemoApplication.java 文件时,Java 虚拟机(JVM)会执行 main 方法中的代码。 • SpringApplication.run(DemoApplication.class, args); 这行代码会启动 Spring Boot 应用。

  2. 初始化 Spring 应用上下文: • Spring Boot 会初始化 Spring 应用上下文,加载所有配置的 Bean,并进行自动配置。 • 组件扫描会找到所有带有 @Component@Service@Repository@Controller 等注解的类,并将它们注册为 Spring 管理的 Bean。

  3. 处理请求: • 当应用启动并运行时,Spring Boot 会启动嵌入式的 Web 服务器(默认是 Tomcat)。 • 你可以访问应用的 URL(例如 http://localhost:8080/users),Spring Boot 会根据请求的 URL 和 HTTP 方法调用相应的控制器方法来处理请求。

DemoApplication.java 文件是 Spring Boot 应用的入口文件,main 方法中的 SpringApplication.run(DemoApplication.class, args); 这行代码启动 Spring Boot 应用,初始化 Spring 应用上下文,并加载所有配置的 Bean。通过这个入口文件,Spring Boot 应用开始运行并处理请求。

4.2访问http://localhost:8080/users,为什么可以获取所有用户?

当然可以!结合你之前的提问,下面我将详细补充访问链接时的实现逻辑,特别是从请求到达控制器到最终返回响应的整个过程。

访问链接时的实现逻辑

1. 请求到达 Web 服务器

• 当你在浏览器或其他客户端中访问 http://localhost:8080/users,HTTP 请求首先到达 Spring Boot 应用程序的嵌入式 Web 服务器(默认是 Tomcat)。

2. Web 服务器转发请求

• Web 服务器将请求转发给 Spring MVC 的前端控制器(DispatcherServlet)。

3. 前端控制器处理请求

DispatcherServlet 接收到请求后,会根据请求的 URL 和 HTTP 方法来查找匹配的处理器方法。 • 在这个例子中,/users URL 与 UserController 类中的 getAllUsers 方法匹配。

4. 控制器处理请求

UserController 类中的 getAllUsers 方法被调用。 • 这个方法通过依赖注入(DI)机制访问 UserService 类的一个实例(Bean)。 • UserService 是一个服务层组件,负责处理业务逻辑。

5. 服务层处理业务逻辑

UserService 类中的 findAll 方法被调用。 • 这个方法通过依赖注入(DI)机制访问 UserRepository 接口的一个实例(Bean)。 • UserRepository 是一个数据访问层接口,扩展了 JpaRepository

6. 数据访问层执行数据库操作

UserRepository 接口继承自 JpaRepository,它提供了基本的 CRUD 操作方法,如 findAll。 • 当调用 findAll 方法时,JpaRepository 在后台执行相应的 SQL 查询,从数据库中检索所有用户记录。

7. 结果返回给前端

• 查询到的用户列表被返回给 UserService,然后传递给 UserController。 • UserController 将用户列表包装在一个 ResponseEntity 对象中,并设置适当的 HTTP 状态码(通常是 200 OK)。 • DispatcherServlet 接收到 ResponseEntity 对象后,将其转换为 HTTP 响应并发送回客户端。

具体实现细节

控制器(Controller)

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        List<User> users = userService.findAll();
        return new ResponseEntity<>(users, HttpStatus.OK);
    }

    // 其他 CRUD 方法...
}

服务层(Service)

@Service
public class UserService {

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public List<User> findAll() {
        return userRepository.findAll();
    }

    // 其他 CRUD 方法...
}

数据访问层(Repository)

public interface UserRepository extends JpaRepository<User, Long> {
    // 可以添加自定义查询方法
}

总结

  1. 请求到达 Web 服务器:HTTP 请求首先到达嵌入式 Web 服务器。
  2. Web 服务器转发请求:请求被转发给 Spring MVC 的前端控制器。
  3. 前端控制器处理请求:根据 URL 和 HTTP 方法查找匹配的处理器方法。
  4. 控制器处理请求:调用服务层方法来处理业务逻辑。
  5. 服务层处理业务逻辑:调用数据访问层方法来获取或操作数据。
  6. 数据访问层执行数据库操作:执行 SQL 查询或其他数据库操作。
  7. 结果返回给前端:将结果包装在响应实体中并发送回客户端。

4.3 其它核心知识点

1.@Autowired

@Autowired 是 Spring 框架中的一个注解,用于自动装配(自动注入)Bean。在你的 UserController 类中,@Autowired 注解的作用是将 UserService 类型的 Bean 自动注入到 UserController 中。

具体作用

  1. 依赖注入: • @Autowired 注解告诉 Spring 容器,UserController 类需要一个 UserService 类型的 Bean。 • Spring 容器会在启动时扫描所有的 Bean,并找到匹配的 UserService Bean,然后将其注入到 UserController 中。

  2. 减少样板代码: • 使用 @Autowired 注解可以避免手动编写繁琐的依赖注入代码,如使用 new 关键字创建对象或使用 ApplicationContext 手动获取 Bean。 • 这使得代码更加简洁和易读。

  3. 提高可测试性: • 通过依赖注入,可以轻松地使用 Mock 对象进行单元测试,而不需要依赖实际的实现。 • 这有助于编写更独立和可维护的测试代码。

示例代码解析

在你的 UserController 类中:

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    // 其他方法...
}

构造函数注入: • 这里使用了构造函数注入的方式,通过 @Autowired 注解,Spring 容器会自动将 UserService Bean 注入到 UserController 的构造函数中。 • 使用构造函数注入的好处是可以确保所有的依赖在对象创建时就已注入,并且不可变(final)。

其他注入方式

除了构造函数注入,@Autowired 还可以用于字段注入和方法注入:

字段注入

@Autowired
private UserService userService;

方法注入

private UserService userService;

@Autowired
public void setUserService(UserService userService) {
    this.userService = userService;
}

注意事项

推荐使用构造函数注入:构造函数注入是 Spring 官方推荐的方式,因为它可以确保依赖在对象创建时就已注入,并且不可变。 • 避免循环依赖:在使用 @Autowired 注解时,需要注意避免循环依赖的情况,即 A 依赖 B,B 又依赖 A。

2.@RestController

@RestController 是 Spring MVC 框架中的一个注解,用于标识一个控制器类,并且表明该控制器类中的所有方法都返回 JSON 或 XML 响应,而不是视图(View)。它是 @Controller@ResponseBody 注解的组合,简化了 RESTful Web 服务的开发。

主要作用

  1. 标识控制器类: • @RestController 注解用于标识一个类作为 Spring MVC 的控制器。 • 这个类中的方法将处理 HTTP 请求,并返回数据而不是视图。

  2. 自动添加 @ResponseBody 注解: • @RestController 注解隐式地为类中的每个方法添加了 @ResponseBody 注解。 • @ResponseBody 注解告诉 Spring MVC,方法的返回值应该直接写入 HTTP 响应体中,而不是解析为视图。

使用场景

@RestController 主要用于构建 RESTful Web 服务,适用于以下场景:

返回 JSON 或 XML 数据: • 当你需要返回 JSON 或 XML 格式的数据时,使用 @RestController 注解。 • 这种情况下,方法的返回值会直接作为 HTTP 响应体返回给客户端。

简化代码: • 使用 @RestController 注解可以避免在每个方法上显式添加 @ResponseBody 注解,简化了代码。

示例代码

package com.example.demo.controller;

import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    // GET endpoint to retrieve all users
    @GetMapping
    public ResponseEntity<List<User>> getAllUsers() {
        List<User> users = userService.findAll();
        return new ResponseEntity<>(users, HttpStatus.OK);
    }

    // 其他 CRUD 方法...
}

对比 @Controller@RestController

@Controller: • 标识一个类作为 Spring MVC 的控制器。 • 默认情况下,方法的返回值会被解析为视图名称,需要配合视图解析器(如 Thymeleaf、JSP)来渲染页面。

@RestController: • 标识一个类作为 Spring MVC 的控制器,并且方法的返回值直接写入 HTTP 响应体中。 • 适用于构建 RESTful Web 服务,返回 JSON 或 XML 数据。

结语

通过本文的介绍,你应该能够使用 Spring Boot 创建一个包含基本增删改查功能的项目。Spring Boot 的自动配置和约定优于配置的原则大大简化了开发过程,使得开发者能够专注于业务逻辑的实现。希望这篇文档对你有所帮助!