我们使用 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 文件中,添加下面的内容:
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,写入如下的代码:
package com.jimmy.todoservice.controller;
@RestController
@RequestMapping("api")
public class Demo {
@GetMapping("/hello")
public String sayHello() {
return "Hello World!";
}
}验证
运行项目后,在 postman 上执行接口:
method [GET]
url [http://localhost:8080/api/hello]能够正确输出 Hello World! 的信息。
数据写入 MySql
真实的项目,我们需要有自己的数据库来存储数据。这里,我们选择了 MySql。
添加数据库表映射
项目进入 src/main/java/com.jimmy.todoservice 下创建包 entity。
And then.
然后在包 entity 下创建类 Demo,然后填写下面的内容:
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,该表内有两个字段,分别为 id 和 name。
添加对应的 TDO
我们创建了 entity,下面我们创建相关的 tdo,方便前端数据的写入。
我们在 src/main/java/com.jimmy.todoservice 下创建包,名为 dto。
And then.
在包 dto 下面创建类,名为 DemoDto,添加下面的内容:
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,添加下面的内容:
package com.jimmy.todoservice.repository;
public interface DemoRepository extends JpaRepository<Demo, Long> {
}创建对应的服务 service
在 src/main/java/com.jimmy.todoservice 下创建包,名为 service。
And then.
在包 service 中创建接口类 DemoService,并添加下面的内容:
package com.jimmy.todoservice.service;
public interface DemoService {
// Add demo item
DemoDto addDemoItem(DemoDto demoDto);
}在包 service 下面创建包 impl。
And then.
在 service/impl 下创建类 DemoServiceImpl,并添加下面的内容:
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,用于 dto 和 entity 数据的转换。
添加 modelMapper
我们在 pom.xml 中,添加下面的依赖:
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.2.0</version>
</dependency>安装上面的依赖后,在入口文件 TodoServiceApplication 中添加下面的内容:
@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 引入)如下:
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 引入)如下:
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 引入)如下:
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 上执行下面的接口:
method [POST]
url [http://localhost:8080/api/add]
body -> {"name": "jimmy"}查看返回的写入数据库的结果。
我们打开 Navicat Premium 查看 todo_service 数据库中表 demo 写入了新数据。
信息返回
我们统一处理返回的信息。
公共返回文件
我们在 src/main/java/com.jimmy.todoservice 下新建包 common,然后在其下面新建类 ResultData,内容如下:
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 下更改:
@GetMapping("/get/{id}")
public ResultData<DemoDto> getItem(@PathVariable("id") Long id) {
DemoDto demoDto = demoService.getDemoItem(id);
return ResultData.success(demoDto);
}验证
启动项目,在 postman 上进行验证:
method [GET]
url [http://localhost:8080/api/get/1]添加 security - 注册和登录
我们引入 security 进行验证。
安装依赖
在项目根目录的 pom.xml 文件中添加下面的依赖引用:
<!-- 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 中添加:
spring.security.user.name=jimmy
spring.security.user.password=123456重新启动项目后,我们可以通过用户名/密码 jimmy/123456 来登陆。
用户注册
下面,我们实现一个系统用户注册。
首先,我们先配置 spring security config 配置类。在 com.jimmy.todoservice/config 下添加下面的内容:
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,内容如下:
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 类:
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,内容如下:
package com.jimmy.todoservice.repository;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsernameOrEmail(String username, String email);
}然后在 com.jimmy.todoservice/service 下添加接口类 AuthService ,内容如下:
package com.jimmy.todoservice.service;
public interface AuthService {
String register(RegisterDto registerDto);
String login(LoginDto loginDto);
}这里我把登陆的接口也罗列出来了。
下面是注册用户重点


