「手把手」 Spring Boot 实现 TODO 项目

2024-02-21 11:10:30 浏览数 (2)

我们使用 Spring Boot 从零开始实现一个 TODO 项目,实现的项目,不包含真实上线的流程。

开发环境

  • MacBook Air - Sonoma 14.2
  • IntelliJ IDEA 2021.2.2
  • Google Chrome - 版本 120.0.6099.129(正式版本) (arm64)
  • Navicat Premium - 16.0.12
  • Postman - Version 8.12.1

项目搭建

我们先创建项目。

New Project
  • Atifact 填写 - todo-service
  • Group 填写 - com.jimmy
  • Java 选择版本 17

Go next.

  • Spring Boot 选择 3.2.1

Dependencies 选择如下:

  • Spring Web
  • Lombok
  • Spring Data JPA
  • MySQL Driver
添加 mysql

Navicat Premium 中新建数据库:

  • 数据库名:todo_service
  • 字符集:utf8mb4
  • 排序规则:utf8mb4_general_ci

And then.

在生成的项目中 src/main/resources/application.properties 文件中,添加下面的内容:

代码语言:javascript复制
spring.datasource.url=jdbc:mysql://localhost:3306/todo_service
spring.datasource.username=root
spring.datasource.password=

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
运行项目

执行项目运行,控制台没有报错,项目运行成功。

创建 Controller

我们简单创建一个 Controller 的案例 Demo,来了解过程。

创建 controller 的 Demo

在项目 src/main/java/com/jimmy.todoservice 下,创建包,其名为 controller

And then.

在包 controller 下,创建类,其名为 Demo,写入如下的代码:

代码语言:javascript复制
package com.jimmy.todoservice.controller;

@RestController
@RequestMapping("api")
public class Demo {
  @GetMapping("/hello")
  public String sayHello() {
    return "Hello World!";
  }
}
验证

运行项目后,在 postman 上执行接口:

代码语言:javascript复制
method [GET]
url [http://localhost:8080/api/hello]

能够正确输出 Hello World! 的信息。

数据写入 MySql

真实的项目,我们需要有自己的数据库来存储数据。这里,我们选择了 MySql

添加数据库表映射

项目进入 src/main/java/com.jimmy.todoservice 下创建包 entity

And then.

然后在包 entity 下创建类 Demo,然后填写下面的内容:

代码语言:javascript复制
package com.jimmy.todoservice.entity;

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "demo")
public class Demo {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(name = "name", nullable = false)
  private String name;
}

此时,如果我们执行项目,则会创建名为 demo 的数据表。我们可以进入 Navicat Premium 中数据库 todo_service 下查看表 demo,该表内有两个字段,分别为 idname

添加对应的 TDO

我们创建了 entity,下面我们创建相关的 tdo,方便前端数据的写入。

我们在 src/main/java/com.jimmy.todoservice 下创建包,名为 dto

And then.

在包 dto 下面创建类,名为 DemoDto,添加下面的内容:

代码语言:javascript复制
package com.jimmy.todoservice.dto;

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class DemoDto {
  private Long id;
  private String name;
}
添加对应的 Repository 来与数据库建立联系

src/main/java/com.jimmy.todoservice 下创建包,名为 repository

And then.

在包 repository 下创建接口类文件,名为 DemoRepository,添加下面的内容:

代码语言:javascript复制
package com.jimmy.todoservice.repository;

public interface DemoRepository extends JpaRepository<Demo, Long> {
}
创建对应的服务 service

src/main/java/com.jimmy.todoservice 下创建包,名为 service

And then.

在包 service 中创建接口类 DemoService,并添加下面的内容:

代码语言:javascript复制
package com.jimmy.todoservice.service;

public interface DemoService {
  // Add demo item
  DemoDto addDemoItem(DemoDto demoDto);
}

在包 service 下面创建包 impl

And then.

service/impl 下创建类 DemoServiceImpl,并添加下面的内容:

代码语言:javascript复制
package com.jimmy.todoservice.service.impl;

@Service
@AllArgsConstructor
public class DemoServiceImpl implements DemoService {

  private DemoRepository demoRepository;

  @Override
  public DemoDto addDemoItem(DemoDto demoDto) {
    return null;
  }
}

在完善上面 DemoServiceImpl.java 文件之前,我们先添加 modelMapper,用于 dtoentity 数据的转换。

添加 modelMapper

我们在 pom.xml 中,添加下面的依赖:

代码语言:javascript复制
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>3.2.0</version>
</dependency>

安装上面的依赖后,在入口文件 TodoServiceApplication 中添加下面的内容:

代码语言:javascript复制
@Bean
public ModelMapper modelMapper() {
  ModelMapper modelMapper = new ModelMapper();
  modelMapper.getConfiguration()
          .setMatchingStrategy(MatchingStrategies.STRICT); // https://stackoverflow.com/questions/58838964/modelmapper-failed-to-convert-java-lang-string-to-java-lang-long
  return modelMapper;
}

整个文件 TodoServiceApplication.java 的内容(移除了 import 引入)如下:

代码语言:javascript复制
package com.jimmy.todoservice;

@SpringBootApplication
public class TodoServiceApplication {

  // 使用 model mapper
  @Bean
  public ModelMapper modelMapper() {
    ModelMapper modelMapper = new ModelMapper();
    modelMapper.getConfiguration()
            .setMatchingStrategy(MatchingStrategies.STRICT); // https://stackoverflow.com/questions/58838964/modelmapper-failed-to-convert-java-lang-string-to-java-lang-long
    return modelMapper;
  }

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

}

OK,我们返回上面的 service/impl/DemoServiceImpl.java 文件。

完善 DemoSeriveImpl

我们引入 modelMapper,整个文件的内容(移除了 import 引入)如下:

代码语言:javascript复制
package com.jimmy.todoservice.service.impl;

@Service
@AllArgsConstructor
public class DemoServiceImpl implements DemoService {

  private DemoRepository demoRepository;

  private ModelMapper modelMapper;

  @Override
  public DemoDto addDemoItem(DemoDto demoDto) {

    Demo demo = modelMapper.map(demoDto, Demo.class);

    Demo savedDemo = demoRepository.save(demo);

    DemoDto savedDemoDto = modelMapper.map(savedDemo, DemoDto.class);

    return savedDemoDto;
  }
}
添加对应的 controller 操作

我们在之前的 src/main/java/com.jimmy.totoservice/controller/Demo.java 文件内,添加下面的 add 接口操作。整个文件的内容(移除了 import 引入)如下:

代码语言:javascript复制
package com.jimmy.todoservice.controller;

@RestController
@RequestMapping("api")
@AllArgsConstructor
public class Demo {
  private DemoService demoService;

  @GetMapping("/hello")
  public String sayHello() {
    return "Hello World!";
  }

  @PostMapping("/add")
  public ResponseEntity<DemoDto> addName(@RequestBody DemoDto demoDto) {
    DemoDto savedDemoDto = demoService.addDemoItem(demoDto);
    return new ResponseEntity<>(savedDemoDto, HttpStatus.OK);
  }
}
验证

我们运行项目起来。在 postman 上执行下面的接口:

代码语言:javascript复制
method [POST]
url [http://localhost:8080/api/add]
body -> {"name": "jimmy"}

查看返回的写入数据库的结果。

我们打开 Navicat Premium 查看 todo_service 数据库中表 demo 写入了新数据。

信息返回

我们统一处理返回的信息。

公共返回文件

我们在 src/main/java/com.jimmy.todoservice 下新建包 common,然后在其下面新建类 ResultData,内容如下:

代码语言:javascript复制
package com.jimmy.todoservice.common;

@Data
public class ResultData<T> {
  private String code;
  private String message;
  private T data;
  // 扩展字段,比如接口的请求时间
  private Long accessTimestamp;
//  private String path; // TODO: 获取请求的路径

  // 构造函数
  public ResultData() {
    this.accessTimestamp = System.currentTimeMillis();
  }

  // 成功返回
  public static <T> ResultData<T> success(T data) {
    ResultData<T> resultData = new ResultData<>();
    resultData.setCode("10000");
    resultData.setMessage(("请求成功!"));
    resultData.setData(data);
    return resultData;
  }

  // 失败返回
  public static <T> ResultData<T> fail(String code, String message) {
    ResultData<T> resultData = new ResultData<>();
    resultData.setCode(code);
    resultData.setMessage(message);
    return resultData;
  }
}
Demo

然后,我们更改 Get 接口请求,在文件 controller/Demo 下更改:

代码语言:javascript复制
@GetMapping("/get/{id}")
public ResultData<DemoDto> getItem(@PathVariable("id") Long id) {
  DemoDto demoDto = demoService.getDemoItem(id);
  return ResultData.success(demoDto);
}
验证

启动项目,在 postman 上进行验证:

代码语言:javascript复制
method [GET]
url [http://localhost:8080/api/get/1]

添加 security - 注册和登录

我们引入 security 进行验证。

安装依赖

在项目根目录的 pom.xml 文件中添加下面的依赖引用:

代码语言:javascript复制
<!-- security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

此时,运行项目,在浏览器中打开链接 http://localhost:8080 会自动跳转到登陆的页面。账号为默认 user,密码是随机生成的,可见于控制台 Using generated security password: 后的一串字符串。

自定义用户名和密码

当然,我们也可以自定义用户名和密码,我们在文件 src/main/resources/application.properties 中添加:

代码语言:javascript复制
spring.security.user.name=jimmy 
spring.security.user.password=123456

重新启动项目后,我们可以通过用户名/密码 jimmy/123456 来登陆。

用户注册

下面,我们实现一个系统用户注册。

首先,我们先配置 spring security config 配置类。在 com.jimmy.todoservice/config 下添加下面的内容:

代码语言:javascript复制
package com.jimmy.todoservice.config;

@Configuration
@EnableMethodSecurity
@AllArgsConstructor
public class SpringSecurityConfig {

  @Bean
  public static PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Bean
  SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.csrf((csrf) -> csrf.disable())
            .authorizeHttpRequests((authorize) -> {
              authorize.requestMatchers("/api/auth/**").permitAll();
              authorize.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll();
              authorize.anyRequest().authenticated();
            }).httpBasic(Customizer.withDefaults());

    return httpSecurity.build();
  }

  @Bean
  public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
    return authenticationConfiguration.getAuthenticationManager();
  }

}

我们在 com.jimmy.todoservice/entity 下添加用户类 User,内容如下:

代码语言:javascript复制
package com.jimmy.todoservice.entity;

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users")
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  private String name;
  @Column(nullable = false, unique = true)
  private String username;
  @Column(nullable = false, unique = true)
  private String email;
  @Column(nullable = false)
  private String password;
}

然后在 com.jimmy.todoservice/dto 下添加 RegisterDto 类:

代码语言:javascript复制
package com.jimmy.todoservice.dto;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class RegisterDto {
  private String name;
  private String username;
  private String email;
  private String password;
}

com.jimmy.todoservice/repository 下添加类 UserRepository,内容如下:

代码语言:javascript复制
package com.jimmy.todoservice.repository;

public interface UserRepository extends JpaRepository<User, Long> {

  Optional<User> findByUsernameOrEmail(String username, String email);

}

然后在 com.jimmy.todoservice/service 下添加接口类 AuthService ,内容如下:

代码语言:javascript复制
package com.jimmy.todoservice.service;

public interface AuthService {

  String register(RegisterDto registerDto);

  String login(LoginDto loginDto);
}

这里我把登陆的接口也罗列出来了。

下面是注册用户重点

0 人点赞