目录
背景
- 入职萌新写入职demo,记录一下。
- 创建一个提供者微服务,提供用户的增删改查接口;
- 技术栈:springboot,mybatis-plus,mysql数据库
- 目的:了解如何构建一个spring boot项目,如何在springboot应用中实现rest接口,如何使用mybatis-plus操作数据库
- 创建一个消费者微服务,提供用户的增删改查接口,接口通过调用提供者微服务提供的接口实现;
- 技术栈:springboot,RestTemplate
- 目的:了解如何在一个微服务中使用RestTemplate调用http接口
- 用postman测试
1、创建两个微服务工程
- 先创建一个父工程,用Spring Initializr快速创建,创建成功后,删除src目录
- 再创建子工程,(在父工程中new module),用Spring Initializr快速创建
- 父子工程的groupId相同
2、生产者
(1)目录
(2)开始编写父子pom.xml
父工程pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.prlp</groupId>
<artifactId>provider-service-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<modules>
<module>provicer-service</module>
</modules>
<name>provider-service-example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>-->
<!--简化冗余编码工具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--web依赖,提供web服务器等功能-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
子工程pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent>
<groupId>com.prlp</groupId>
<artifactId>provider-service-example</artifactId>
<version>0.0.1-SNAPSHOT</version><!-- lookup parent from repository -->
</parent>
<groupId>com.prlp</groupId>
<artifactId>provicer-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>provicer-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(3)编写代码
实体类
User.java
@Data
:不必再写实体类的get
/set
/equals
/hashCode
/toString
等方法,因为我们的IDEA安装了lombok插件,pom.xml中也导入了lombok依赖坐标,@Data
标注在类上,为类提供读写属性以及其他方法@JsonInclude(Include.NON_NULL)
:当实体类与json互转的时候 属性值为null的不参与序列化,因为一般传给前端的值尽量不要有null,可有为空串“” 或者 0 或者 [], 但尽量不要null。@TableName("数据库的表名")
:指明实体类对应的数据库表名@TableId
:标识此字段为数据库表中的主键
package com.prlp.provicerservice.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import java.util.Date;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
/**
* <pre>
* classname User
* description
* </pre>
*
* @author pengruolan
* @date 2021/4/9 19:09
**/
@Data
@TableName("user")
@JsonInclude(Include.NON_NULL)
public class User {
@TableId
private Integer id;
private String nickname;
//@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
编写mapper接口
-UserMapper.java
- 编写接口即可,mybatis-plus会利用反射生产实现类
package com.prlp.provicerservice.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.prlp.provicerservice.entity.User;
import org.springframework.stereotype.Component;
/**
* <pre>
* classname UserMapper
* description
* </pre>
*
* @author pengruolan
* @date 2021/4/9 19:11
**/
@Component
public interface UserMapper extends BaseMapper<User> {
}
逻辑层(这里我直接写实现类了)
UserService.java
package com.prlp.provicerservice.service;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.prlp.provicerservice.entity.User;
import com.prlp.provicerservice.mapper.UserMapper;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <pre>
* classname UserService
* description
* </pre>
*
* @author pengruolan
* @date 2021/4/9 19:21
**/
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public List<User> queryAllUser() {
return userMapper.selectList(Wrappers.emptyWrapper()); /*UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件*/
}
public User queryById(Integer id) {
return userMapper.selectById(id);
}
public void createUser(User user) {
userMapper.insert(user);
}
public void updateUserById(Integer id, User user) {
user.setId(id);
userMapper.updateById(user);
}
public void deleteUserById(Integer id) {
userMapper.deleteById(id);
}
}
控制层
UserController.java
package com.prlp.provicerservice.controller;
import com.prlp.provicerservice.entity.User;
import com.prlp.provicerservice.service.UserService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* <pre>
* classname UserController
* description
* </pre>
*
* @author pengruolan
* @date 2021/4/9 19:21
**/
@RestController
//@Validated
public class UserController {
@Autowired
UserService userService;
@RequestMapping(value = "/provider/users", method = RequestMethod.GET, produces = "application/json")
public List<User> queryAllUser() {
return userService.queryAllUser();
}
@RequestMapping(value = "/provider/user/{id}", method = RequestMethod.GET, produces = "application/json")
public User queryById(@PathVariable("id") Integer id) {
return userService.queryById(id);
}
@RequestMapping(value = "/provider/user/c", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
public void createUser(@RequestBody User user) {
userService.createUser(user);
}
@RequestMapping(value = "/provider/user/u/{id}", method = RequestMethod.PUT, produces = "application/json", consumes = "application/json")
public void updateUser(@PathVariable("id") Integer id, @RequestBody User user) {
userService.updateUserById(id, user);
}
@RequestMapping(value = "/provider/user/d/{id}", method = RequestMethod.DELETE, produces = "application/json")
public void deleteUser(@PathVariable("id") Integer id) {
userService.deleteUserById(id);
}
}
启动类
ProvicerServiceApplication.java
- 在 Spring Boot 启动类中添加
@MapperScan
注解,扫描 Mapper 文件夹
package com.prlp.provicerservice;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.prlp.provicerservice.mapper")
public class ProvicerServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProvicerServiceApplication.class, args);
}
}
测试类
ProvicerServiceApplicationTests.java
package com.prlp.provicerservice;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.prlp.provicerservice.entity.User;
import com.prlp.provicerservice.mapper.UserMapper;
import java.util.List;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
class ProvicerServiceApplicationTests {
@Autowired
UserMapper userMapper;
@Test
void testSelect() {
System.out.println("------ prl ------");
List<User> users = userMapper.selectList(Wrappers.emptyWrapper());
Assert.assertEquals(5, users.size());
users.forEach(System.out::println);
System.out.println(users.get(0).getNickname());
}
}
配置
application.yml
spring:
datasource:
driver-class=name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/provider
username: root
password: root
schema: classpath:db/schema-mysql.sql
data: classpath:db/data-mysql.sql
server:
port: 10003
3、消费者
(1)目录
(2)父子pom.xml
父工程pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.prlc</groupId>
<artifactId>comsumer-service-example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<modules>
<module>comsumer-service</module>
</modules>
<name>comsumer-service-example</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>-->
<!--简化冗余编码工具-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--web依赖,提供web服务器等功能-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-to-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
子工程pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<parent>
<groupId>com.prlc</groupId>
<artifactId>comsumer-service-example</artifactId>
<version>0.0.1-SNAPSHOT</version> <!-- lookup parent from repository -->
</parent>
<groupId>com.prlc</groupId>
<artifactId>comsumer-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>comsumer-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(3)开始编写代码
实体类
User.java
- 从数据库获取时间传到前端进行展示的时候,我们需要设置一个统一的格式,避免从数据库中取出的时间戳,在前端展示时很丑
@JsonFormat(pattern = "格式", timezone = "时区")
:Jackson时间格式化注解,使得后台到前台时间转换格式保持一致,添加此注解后,用对应的实体类来接收数据库查询出来的结果时就完成了时间格式的转换,再返回给前端时就是一个符合我们设置的时间格式
- 在使用WEB服务的时,可能会需要用到,传入时间给后台,如注册新用户需要填写出生日期,此时前台传递给后台的时间格式同样是不一致的
@DataFormat(pattern = "时间格式)
:在对应的接收前台数据的对象的属性上添加,使得前台到后台时间转换格式保持一致
package com.prlc.comsumerservice.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
/**
* <pre>
* classname User
* description
* </pre>
*
* @author pengruolan
* @date 2021/4/9 20:06
**/
@Data
public class User { /*由于我们使用RestTemplate调用Restful服务后,需要将对应的json串转换成User对象,所以需要将这个类拷贝到该工程中*/
private Integer id;
private String nickname;
//@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH-mm-ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
}
控制层
ConsumerController.java
package com.prlc.comsumerservice.controller;
import com.prlc.comsumerservice.entity.User;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* <pre>
* classname ConsumerController
* description
* </pre>
*
* @author pengruolan
* @date 2021/4/9 20:08
**/
@RestController
//@Validated
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/consumer/users")
public List<User> query() {
String url = "http://localhost:10003/provider/users";
User[] users = restTemplate.getForObject(url, User[].class); /*返回一个List集合*/
return Arrays.asList(users);
}
@RequestMapping("/consumer/user/{id}")
public User queryById(@PathVariable("id") Integer id) {
String url = "http://localhost:10003/provider/user/" + id;
return restTemplate.getForObject(url, User.class);
}
@RequestMapping(value = "/consumer/user/c", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
public void createUser(@RequestBody User user) {
String url = "http://localhost:10003/provider/user/c";
restTemplate.postForObject(url, user, User.class);
}
@RequestMapping(value = "/consumer/user/u/{id}", method = RequestMethod.PUT, produces = "application/json", consumes = "application/json")
public void updateUser(@PathVariable("id") Integer id, @RequestBody User user) {
String url = "http://localhost:10003/provider/user/u/{id}";
restTemplate.put(url, user, id); //id可以替换url中的占位符{id}
}
@RequestMapping(value = "consumer/user/d/{id}", method = RequestMethod.DELETE, produces = "application/json")
public void deleteUser(@PathVariable("id") Integer id) {
String url = "http://localhost:10003/provider/user/d/" + id;
restTemplate.delete(url, id);
}
}
启动类
ComsumerServiceApplication .java
package com.prlc.comsumerservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class ComsumerServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ComsumerServiceApplication.class, args);
}
//@Autowired
//private RestTemplateBuilder builder; //spring默认已经注入了RestTemplateBuilder实例
/*@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
测试类
ComsumerServiceApplicationTests .java
package com.prlc.comsumerservice;
import com.prlc.comsumerservice.entity.User;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
@RunWith(SpringRunner.class)
@SpringBootTest
class ComsumerServiceApplicationTests {
@Autowired
public RestTemplate restTemplate;
@Test
void post() {
User user = restTemplate.getForObject("http://localhost:10003/provider/user/1", User.class);
System.out.println(user);
}
}
配置
application.yml
server:
port: 10004
4、结果
生产者 能够通过postman调用 接口 进行用户的增删改查操作
消费者 能够通过postman调用 消费者微服务接口 进行用户的增删改查操作
测试生产者的增删改查
-
查所有用户
-
查单个用户
-
增加用户
-
更改用户
-
删除用户
测试消费者的增删改查
(此时生产者和消费者都要启动)
(调用了生产者接口,利用RestTemplate
类进行通信)
-
查询所有用户
-
查询单个用户
-
增加用户
-
更改用户
RestTemplate
工厂类的默认实现中,不支持使用PATCH
方法进行更改,需要将RestTemplate
配置类的工厂对象修改为HttpComponentsClientHttpRequestFactory
;
因此这里用PUT
来更改用户
-
删除用户
数据库表
建好一个数据库,取名provider
schema-mysql.sql
drop table if exists person;
create table user
(
id int(11) auto_increment,
nickname varchar(20) not null,
create_time timestamp default CURRENT_TIMESTAMP,
primary key (id)
) auto_increment = 1;
data-mysql.sql
DELETE
FROM user;
INSERT INTO user (id, nickname)
VALUES (1, 'Jone'),
(2, 'Jack'),
(3, 'Tom'),
(4, 'Sandy'),
(5, 'Billie');
踩到的坑
- 传输过程中数据类型转换出错:在createTime字段上加上注解,规定格式
- Consumer项目中,创建对象的方法,RestTemplate要用postForObject方法
基本概念
@PathVariable("xxx")
:- 通过
@PathVariable
可以将URL中占位符参数{xxx}
绑定到处理器类的方法形参中@PathVariable(“xxx“)
- URL访问路径:
localhost:10003/user/c/1
- 通过
@RequestBody
:主要用来接收前端传递给后端的json
字符串中的数据的(请求体中的数据);GET
方式无请求体,所以使用@RequestBody
接收数据时,前端不能使用GET
方式提交数据,而是用POST
方式进行提交。(因此我们在“增”时,采用的是POST
请求);- 在后端的同一个接收方法里,
@RequestBody
与@RequestParam()
可以同时使用,@RequestBody
最多只能有一个,而@RequestParam()
可以有多个; - 一个请求,只有一个
RequestBody
;一个请求,可以有多个RequestParam
;
@RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)
:将URL中请求参数绑定到控制器的方法参数上(是springmvc中接收普通参数的注解)- URL访问路径:
localhost:10003/user/c?name=xxx&password=xxx
value
:参数名required
:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。defaultValue
:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值
- URL访问路径: