SpringBoot 仿B站后端项目实战
1. 搭建环境
1)项目架构
业务(功能)架构
用户服务: 注册登录 /大会员权限/ 查找视频
在线视频流播放+实时弹幕
管理后台 视频上传/数据统计/系统消息推送
技术架构
SpringBoot + MySql + Mybatis + Maven
部署架构
前端 : 服务转发 + 负载均衡
后端: 业务处理 + 功能实现
缓存 + 队列
2)开发环境搭建
Windows系统
IDEA软件
JDK1.8 / Maven
创建多环境、多模块项目
配置JDKMaven
IDEA 中 配置JDK
设置本地Maven setting文件
重写IDEA默认配置
配置模块间的依赖
添加SpringBoot依赖 pom依赖
启动入口
搭建数据库MySql和持久层框架MyBatis
mysql
mysql-connector-java
8.0.28
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.2.2
3)热部署
org.springframework.boot
spring-boot-devtools
2.6.4
2.用户模块开发
Restful风格 *幂等性
POST (不具有幂等性)
1. 文章组发送消息
2. 注册用户
3. 提交表单
GET
DELETE
PUT 用于请求中的负载创建或者替换资源 具有幂等性(一次调用或多次调用等价,不会有副作用)
package com.mao.bilibili.api;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@RestController
public class RestfulApi {
private final Map> datamap;
public RestfulApi(){
datamap = new HashMap<>();
for (int i = 1; i < 3; i++) {
HashMap data = new HashMap<>();
data.put("id",i);
data.put("name","name"+i);
datamap.put(i,data);
}
}
@GetMapping("/objects/{id}")
public Map getData(@PathVariable("id") Integer id){
return datamap.get(id);
}
@DeleteMapping("/objects/{id}")
public String deleteData(@PathVariable("id") Integer id){
datamap.remove(id);
return "delete success";
}
@PostMapping("/objects") //添加数据
public String postData(@RequestBody Map data){
//先获取最后的id值
Integer[] ts = datamap.keySet().toArray(new Integer[0]);
Arrays.sort(ts);
int id = ts[ts.length - 1] + 1; //要添加的id值
datamap.put(id,data);
return "post success";
}
@PutMapping("/objects")
public String putData(@RequestBody Map data){
//先看datamap中是否有相同id值的
Integer id = Integer.valueOf(String.valueOf(data.get("id")));
Map containedData = datamap.get(id);
if (containedData == null){
Integer[] ts = datamap.keySet().toArray(new Integer[0]);
Arrays.sort(ts);
int nextId = ts[ts.length - 1] + 1; //要添加的id值
datamap.put(nextId,data);
}else {
datamap.put(id,data);
}
return "put success";
}
}
一些规范
通用功能与配置
Utils
Json数据返回类
package com.mao.bilibili.domain;
import lombok.Data;
@Data
public class JsonResponse {
private String code;
private String msg;
private T data;
public JsonResponse(String code,String msg){
this.code =code;
this.msg = msg;
}
public JsonResponse(T data){
this.data = data;
this.code = "0";
this.msg = "success";
}
// 成功返回前端无数据
public static JsonResponse success(){
return new JsonResponse(null);
}
// 返回前端有数据
public static JsonResponse success(String data){
return new JsonResponse<>(data);
}
// 失败
public static JsonResponse fail(){
return new JsonResponse("1","fail");
}
// 返回特定状态码
public static JsonResponse fail(String code,String msg){
return new JsonResponse<>(code, msg);
}
}
Json信息转换配置
@Configuration
public class JsonHttpMessageConverterConfig {
@Bean
@Primary //提高优先级
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter messageConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
fastJsonConfig.setSerializerFeatures(
SerializerFeature.PrettyFormat,
//List字段如果为null,输出为[],而非null
SerializerFeature.WriteNullListAsEmpty,
//字符类型字段如果为null,输出为"",而非null
SerializerFeature.WriteNullStringAsEmpty,
//Boolean字段如果为null,输出为false,而非null
SerializerFeature.WriteNullBooleanAsFalse,
//消除对同一对象循环引用的问题,默认为false(如果不配置有可能会进入死循环)
SerializerFeature.DisableCircularReferenceDetect,
//是否输出值为null的字段,默认为false。
SerializerFeature.WriteMapNullValue,
SerializerFeature.MapSortField
);
messageConverter.setFastJsonConfig(fastJsonConfig);
return new HttpMessageConverters(messageConverter);
}
}
全局异常处理
@ControllerAdvice //Controller增强器
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CommonGlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public JsonResponse commonExceptionHandler(HttpServletRequest request,Exception e){
String errorMsg = e.getMessage();
if (e instanceof ConditionException){
String errorCode = ((ConditionException) e).getCode();
return new JsonResponse<>(errorCode,errorMsg);
}else {
return new JsonResponse<>("500",errorMsg);
}
}
}
public class ConditionException extends RuntimeException{
private static final long serialVersionUID = 1L;
private String code;
public ConditionException(String code,String name){
super(name);
this.code = code;
}
public ConditionException(String name){
super(name);
this.code = "500"; //通用异常
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
```java
{
super(name);
this.code = code;
}
public ConditionException(String name){
super(name);
this.code = "500"; //通用异常
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}