我们使用 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);
}
这里我把登陆的接口也罗列出来了。
下面是注册用户重点