1 SpringBoot常用开发技能
1.1 项目架构
- 创建项⽬
- 配置启动类
- 建⽴对应的包,先建⽴这些包,其他⽤到再补充
controller
service
mapper
domain
utils
1.2 开发HTTP接⼝GET请求
GET请求
场景:⼀般的查询接⼝就是get请求
注解:@GetMapping = @RequestMapping(method = RequestMethod.GET)
⼀个顶两的注解
@GetMapping = @RequestMapping(method = RequestMethod.GET)
@PostMapping = @RequestMapping(method = RequestMethod.POST)
@PutMapping = @RequestMapping(method = RequestMethod.PUT)
@DeleteMapping = @RequestMapping(method = RequestMethod.DELETE)
示例:视频列表查询
- 实体类:Video
public class Vedio implements Serializable {
private Integer id;
private String title;
private String summary;
private Integer price;
private String coverImg;
private Date createTime;
public Vedio() {
}
public Vedio(Integer id, String title) {
this.id = id;
this.title = title;
this.createTime = new Date();
}
// 省get、set、tostring方法
- mapper类:VideoMapper
@Repository
public class VideoMapper {
private static Map<Integer, Vedio> videoMap = new HashMap<>();
/**
* 模拟数据
*/
static {
videoMap.put(1, new Vedio(1 , "东邪西毒"));
videoMap.put(2, new Vedio(2 , "精武英雄"));
videoMap.put(3, new Vedio(3 , "霸王别姬"));
videoMap.put(4, new Vedio(4 , "南极大冒险"));
videoMap.put(5, new Vedio(5 , "变形金刚"));
videoMap.put(6, new Vedio(6 , "流浪地球"));
}
// 获取视频列表
public List<Vedio> listVideo() {
List<Vedio> list = new LinkedList<>();
list.addAll(videoMap.values());
return list;
}
}
- service类,包括接口和实现类
接口类: VideoService
public interface VideoService {
List<Vedio> listVideo();
}
实现类:VideoServiceImpl
@Service
public class VideoServiceImpl implements VideoService {
@Autowired // 自动注入VideoMapper
private VideoMapper videoMapper;
@Override
public List<Vedio> listVideo() {
return videoMapper.listVideo();
}
}
- Controller类:VideoController
@RestController
@RequestMapping("/app/v1/video")
public class VideoController {
@Autowired // 自动注入VideoService实现类
private VideoService videoService;
@RequestMapping("list")
public Object getList() {
return videoService.listVideo();
}
}
测试:
- 启动程序
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.2)
2022-01-12 14:11:05.388 INFO 20464 --- [ main] c.j.s.Springbootdemo2Application : No active profile set, falling back to default profiles: default
2022-01-12 14:11:06.005 INFO 20464 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-01-12 14:11:06.011 INFO 20464 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-01-12 14:11:06.011 INFO 20464 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.56]
2022-01-12 14:11:06.068 INFO 20464 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-01-12 14:11:06.068 INFO 20464 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 646 ms
2022-01-12 14:11:06.315 INFO 20464 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-01-12 14:11:06.321 INFO 20464 --- [ main] c.j.s.Springbootdemo2Application : Started Springbootdemo2Application in 1.198 seconds (JVM running for 1.959)
- 访问接口
1.3 制定统⼀接⼝返回协议
实际开发中通过⾃定义返回对象,统⼀协议,能够有效的避免开发中协议不统一导致的许多问题,而且还能提供工作的效率。
- 自定义返回信息对象(务必生成get、set方法)
/**
* 业务返回信息类
*/
public class RetData {
/**
* 业务返回码,默认:0:成功,-1:失败
*/
private int code; // 业务返回码
/**
* 业务数据
*/
private Object data; // 业务数据
/**
* 返回信息
*/
private String message; // 状态信息
public RetData(){}
public RetData(int code, Object data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
/**
* 无参返回成功,默认返回码
*/
public static RetData RetSuccess() {
return new RetData(0, null, "success");
}
/**
* 有参返回成功,默认返回码
* @param data
* @return
*/
public static RetData RetSuccess(Object data) {
return new RetData(0, data, "success");
}
/**
* 无参返回错误,默认返回码
* @return
*/
public static RetData RetError() {
return new RetData(-1, null, "unknow error");
}
/**
* 默认返回码带错误信息
* @param msg
* @return
*/
public static RetData RetError(String msg) {
return new RetData(-1, null, msg);
}
/**
* 自定义返回码带错误信息
* @param code
* @param msg
* @return
*/
public static RetData RetError(int code, String msg) {
return new RetData(code, null, msg);
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
- 在VideoController类中使用RetData返回
@RequestMapping("list")
public Object getList() {
return RetData.RetSuccess(videoService.listVideo()); // 使用RetData返回
}
- 重启程序并访问接口,返回结果如下:
code:业务返回码
data:业务数据
message:返回信息
{
"code": 0,
"data": [
{
"id": 1,
"title": "东邪西毒",
"summary": null,
"price": null,
"coverImg": null,
"createTime": "2022-01-12T06:43:36.907+00:00"
},
{
"id": 2,
"title": "精武英雄",
"summary": null,
"price": null,
"coverImg": null,
"createTime": "2022-01-12T06:43:36.907+00:00"
},
{
"id": 3,
"title": "霸王别姬",
"summary": null,
"price": null,
"coverImg": null,
"createTime": "2022-01-12T06:43:36.907+00:00"
},
{
"id": 4,
"title": "南极大冒险",
"summary": null,
"price": null,
"coverImg": null,
"createTime": "2022-01-12T06:43:36.907+00:00"
},
{
"id": 5,
"title": "变形金刚",
"summary": null,
"price": null,
"coverImg": null,
"createTime": "2022-01-12T06:43:36.907+00:00"
},
{
"id": 6,
"title": "流浪地球",
"summary": null,
"price": null,
"coverImg": null,
"createTime": "2022-01-12T06:43:36.907+00:00"
}
],
"message": "success"
}
1.4 开发HTTP接⼝POST请求
POST请求-form表单
场景:表单提交,如果不⽤细分PUT/DELETE⽅法,则都可以⽤POST
注解:@PostMapping = @RequestMapping(method = RequestMethod.POST)
测试:
- 编写UserController类
@RestController
@RequestMapping("/app/v1/user")
public class UserController {
@PostMapping("login")
public RetData login(String username, String pwd) {
System.out.println("username:" + username + ",pwd:" + pwd);
return RetData.RetSuccess("");
}
}
- 启动程序
- 采用参数形式
能够正常接收post提交的参数
- 采用form表单形式
也能够正常接收post提交的参数
实际开发中,参数数量比较多,通常我们会用对象接收参数,修改UserController如下:
@RestController
@RequestMapping("/app/v1/user")
public class UserController {
@PostMapping("login")
public RetData login(User user) {
System.out.println("user:" + user.toString());
return RetData.RetSuccess("");
}
}
再次使用form表达形式提交
程序依然能够正常接收post提交的参数
user:User{id=null, username='tom', password='8888'}
但实际开发中,接口一般都是以json形式提交参数,提交方式如下:
此时程序无法正常接收数据
user:User{id=null, username='null', password='null'}
为了正常接收json形式的参数,需要在对象参数前面加注解:@RequestBody
注意:参数名字与对象属性名称保持一致
@PostMapping("login")
public RetData login(@RequestBody User user) {
System.out.println("user:" + user.toString());
return RetData.RetSuccess("");
}
这样才能正常的接收
user:User{id=null, username='fanny', password='123456'}
开发示例:账号密码提交登录接⼝,form表单形式
- Mapper类:UserMapper
@Repository
public class UserMapper {
private static Map<String, User> userMap = new HashMap<>();
static {
userMap.put("jackyan", new User(1, "jackyan", "123456"));
userMap.put("lucy", new User(2, "lucy", "888888"));
userMap.put("tom", new User(3, "tom", "666666"));
userMap.put("fancy", new User(4, "fancy", "555555"));
}
public User login(String username, String password) {
User user = userMap.get(username);
if (user == null) {
return null;
} else if (user.getPassword().equals(password)) {
return user;
}
return null;
}
}
- Service类
接口类:UserService
public interface UserService {
public String login(String username, String password);
}
实现类:
@Service
public class UserServiceImpl implements UserService {
// 模拟保存用户session
private static Map<String, User> sessionMap = new HashMap<>();
@Autowired
private UserMapper userMapper;
@Override
public String login(String username, String password) {
User user = userMapper.login(username, password);
if (user == null) {
return null;
}
String token = UUID.randomUUID().toString();
// 保存用户session
sessionMap.put(token, user);
return token;
}
}
- Controller类:UserController
@RestController
@RequestMapping("/app/v1/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("login")
public RetData login(@RequestBody User user) {
String token = userService.login(user.getUsername(), user.getPassword());
if (token == null) {
return RetData.RetError("用户名或密码错误!");
}
return RetData.RetSuccess(token);
}
}
- 测试
登录成功
登录失败
1.5 RequestBody对象数组提交接⼝
POST请求-RequestBody⽅式
开发示例:新增视频json对象,章数组提交
- Video类增加章列表信息
private List<Chapter> chapterList;
- 新增Chapter类
/**
* 视频章信息
*/
public class Chapter {
private Integer id;
private Integer videoId;
private String title;
public Chapter() {
}
public Chapter(Integer id, Integer videoId, String title) {
this.id = id;
this.videoId = videoId;
this.title = title;
}
// 省略set、get、tostring方法
- 编写Controller类测试
@PostMapping("save_video")
public RetData saveVideo(@RequestBody Video video) {
return RetData.RetSuccess(video);
}
- 测试,发送数据
{
"id":1001,
"title":"精武英雄",
"chapterList":[
{
"id":10001,
"title": "日本求学"
},
{
"id":10002,
"title": "回国探师"
},
{
"id":10003,
"title": "比武切磋"
}
]
}
- 测试结果
{
"code": 0,
"data": {
"id": 1001,
"title": "精武英雄",
"summary": null,
"price": null,
"coverImg": null,
"createTime": null,
"chapterList": [
{
"id": 10001,
"videoId": null,
"title": "日本求学"
},
{
"id": 10002,
"videoId": null,
"title": "回国探师"
},
{
"id": 10003,
"videoId": null,
"title": "比武切磋"
}
]
},
"message": "success"
}
1.6 定制JSON字段
springboot中自带jackson相关包,可以用来定制化处理实体类字段相关信息:
指定字段不返回:@JsonIgnore
指定⽇期格式:@JsonFormat(pattern="yyyy-MM-dd hh:mm:ss",locale="zh",timezone="GMT+8")
空字段不返回:@JsonInclude(Include.NON_NULL)
指定别名:@JsonProperty
@JsonProperty("cover_img") // 给字段取别名
private String coverImg;
@JsonFormat(pattern="yyyy-MM-dd hh:mm:ss",locale="zh",timezone="GMT+8")
@JsonProperty("create_time")
private Date createTime;
@JsonProperty("chapter_list")
@JsonIgnore // 该字段不返回
private String password;