**前言:**大家跟着黑马的视频做完了项目以后,是不是或多或少都有些头脑混乱呢,不知道看过学过的知识是否记住了,也不清楚自己到底学会了多少,这篇文章将带着你重温项目中涉及的重要知识点(只是简要总结了知识点,帮助你回想所学的知识,不会涉及很多业务模块的代码和逻辑),包括SpringBoot, Mybatis ,httpclient,Spring Cache,JWT令牌,Swagger,POI,WebSocket,MySQL,Redis,pagehelper,git等技术的讲解,希望对你有所帮助,祝大家都能尽快拿到自己想要的offer。
一.熟悉开发环境
1.项目介绍
本项目(苍穹外卖)是专门为餐饮企业(餐厅、饭店)定制的一款软件产品,包括 系统管理后台 和小程序端应用 两部分。其中系统管理后台主要提供给餐饮企业内部员工使用,可以对餐厅的分类、菜品、套餐、订单、员工等进行管理维护,对餐厅的各类数据进行统计,同时也可进行来单语音播报功能。小程序端主要提供给消费者使用,可以在线浏览菜品、添加购物车、下单、支付、催单等。
1.1前端环境认识
前端发送的请求,两个地址不一致,是如何请求到后端服务的?这就用到了nginx反向代理技术
(1)nginx反向代理
nginx 反向代理,就是将前端发送的动态请求由 nginx 转发到后端服务器
那为什么不直接通过浏览器直接请求后台服务端,需要通过nginx反向代理呢?
好处:
-
提高访问速度:因为nginx本身可以进行缓存,如果访问的同一接口,并且做了数据缓存,nginx就直接可把数据返回,不需要真正地访问服务端,从而提高访问速度。
-
进行负载均衡:所谓负载均衡,就是把大量的请求按照我们指定的方式均衡的分配给集群中的每台服务器。
-
保证后端服务安全:因为一般后台服务地址不会暴露,所以使用浏览器不能直接访问,可以把nginx作为请求访问的入口,请求到达nginx后转发到具体的服务中,从而保证后端服务的安全。
(2)nginx负载均衡
当如果服务以集群的方式进行部署时,那nginx在转发请求到服务器时就需要做相应的负载均衡。其实,负载均衡从本质上来说也是基于反向代理来实现的,最终都是转发请求。
nginx 负载均衡策略:
名称 | 说明 |
---|---|
轮询 | 默认方式 |
weight | 权重方式,默认为1,权重越高,被分配的客户端请求就越多 |
ip_hash | 依据ip分配方式,这样每个访客可以固定访问一个后端服务 |
least_conn | 依据最少连接方式,把请求优先分配给连接数少的后端服务 |
url_hash | 依据url分配方式,这样相同的url会被分配到同一个后端服务 |
fair | 依据响应时间方式,响应时间短的服务将会被优先分配 |
(3)http请求报文和响应报文
http报文的三个组成部分 http报文是一个格式化数据块。报文类型包括客户端请求,服务器响应。
http请求报文组成:
请求行(start line):由请求方法(GET/POST/PUT)、请求URL(不包括域名 | 、HTTP协议版本组成
请求头(header):请求头部由关键字/值对组成,每行一对;主要包含Content-Length标头:实体的长度,Content-Tyep标头:实体的媒体类型
请求体(body) :GET(从服务器获取数据)请求体为空,POST(向服务器发送要处理的数据)有数据
http响应报文组成:
1.状态行:服务器HTTP协议版本,响应状态码,状态码的文本描述
状态码由3位数字组成,第一个数字定义了响应的类别:
1xx:指示信息,表示请求已接收,继续处理
2xx:成功,表示请求已被成功接受,处理。
3xx:重定向
4xx:客户端错误
5xx:服务器端错误,服务器未能实现合法的请求。
2.首部行:主要包含Content-Length标头:实体的长度,Content-Tyep标头:实体的媒体类型
3.实体:实体包含了Web客户端请求的对象
1.2 后端环境认识
(1)模块说明
对工程的每个模块作用说明:
序号 | 名称 | 说明 |
---|---|---|
1 | sky-take-out | maven父工程,统一管理依赖版本,聚合其他子模块 |
2 | sky-common | 子模块,存放公共类,例如:工具类、常量类、异常类等 |
3 | sky-pojo | 子模块,存放实体类、VO、DTO等 |
4 | sky-server | 子模块,后端服务,存放配置文件、Controller、Service、Mapper等 |
分析sky-common模块的每个包的作用:
名称 | 说明 |
---|---|
constant | 存放相关常量类 |
context | 存放上下文类 |
enumeration | 项目的枚举类存储 |
exception | 存放自定义异常类 |
json | 处理json转换的类 |
properties | 存放SpringBoot相关的配置属性类 |
result | 返回结果类的封装 |
utils | 常用工具类 |
分析sky-pojo模块的每个包的作用:
名称 | 说明 |
---|---|
Entity | 实体,通常和数据库中的表对应(数据库字段一般是下划线命名,实体类属性一般是驼峰命名) |
DTO | 数据传输对象,通常用于程序中各层之间传递数据 |
VO | 视图对象,为前端展示数据提供的对象,一般是controller层把VO传给前端,然后前端展示 |
POJO | 普通Java对象,只有属性和对应的getter和setter |
分析sky-server模块的每个包的作用:
名称 | 说明 |
---|---|
config | 存放配置类 |
controller | 存放controller类 |
interceptor | 存放拦截器类 |
mapper | 存放mapper接口 |
service | 存放service类 |
SkyApplication | 启动类 |
(2)三层架构思想
在我们进行程序设计以及程序开发时,尽可能让每一个接口、类、方法的职责更单一些(单一职责原则)
表现层(Presentation Layer)--- 控制器(Controller): 这是Spring MVC的入口点,负责处理用户的HTTP请求。控制器接收请求,调用业务逻辑,然后返回一个模型和视图。控制器通常注解为@Controller或@RestController(后者用于RESTful Web服务,自动将返回值作为响应体)。
业务逻辑层(Service Layer)--- 服务(Service): 这一层包含了应用程序的业务逻辑。服务层会调用数据访问层(DAO)来获取或保存数据,并处理这些数据以满足业务需求。服务层通常注解为@Service。
**数据访问层(Data Access Layer)--- 数据访问对象(DAO):**这一层负责与数据库交互,执行CRUD操作(创建、读取、更新、删除)。DAO层通常注解为@Repository。
优点:
解耦:每一层只关注自己的职责,降低了各层之间的依赖。
可维护性:每一层可以独立开发和测试,提高了代码的可维护性。
可扩展性:随着业务逻辑的增长,可以更容易地扩展服务层和数据访问层。
可测试性:每一层可以被单独测试,提高了测试的覆盖率和质量。
(3)常用注解
- @SpringBootApplication:用于标注主应用程序类,标识一个Spring Boot应用程序的入口点
- @Controller:标识控制器类,处理Http请求
- @GetMapping、@PostMapping、@PutMapping、@DeleteMapping:用于简化@RequestMapping注解,分别用于处理GET、POST、PUT、DELETE请求。
- @RequestBody:用于读取Http请求的内容,将其绑定到Controller方法的参数上。
- @Value:用于将配置文件中的值注入到Bean的字段中。
- @Autowired:用于自动注入依赖的Bean。
- @Service:用于标注服务层的组件,通常用于标记业务逻辑层。
- @Repository:用于标注数据访问层的组件,继承自@Component。
- @Component:通用的Spring组件注解,表示一个受Spring管理的组件。
- RequestMapping:用于映射HTTP请求路径到Controller的处理方法
另外一个与配置相关的重要注解:
- @Configuration:用于制定一个类为配置类,其中定义的bean会被Spring容器管理
二. 重要技术解析
上述已讲述了nginx的反向代理技术,下面介绍Swagger,JWT令牌等技术
1.Swagger
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(https://swagger.io/)。使用Swagger只需要按照规范去定义接口及接口相关的信息,就可以做到生成接口文档,以及在线接口调试页面。Swagger可以帮助后端生成接口文档、进行在线接口测试。 knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。
通过注解可以控制生成的接口文档,使接口文档拥有更好的可读性,常用注解如下:
注解 | 说明 |
---|---|
@Api | 用在类上,例如Controller,表示对类的说明 |
@ApiModel | 用在类上,例如entity、DTO、VO |
@ApiModelProperty | 用在属性上,描述属性信息 |
@ApiOperation | 用在方法上,例如Controller的方法,说明方法的用途、作用 |
2. JWT令牌
2.1 组成
JWT令牌由三个部分组成,三个部分之间使用英文的点来分割
-
第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}
-
第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}
-
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
2.2 校验流程(登录认证)
-
在浏览器发起请求来执行登录操作,此时会访问登录的接口,如果登录成功之后,我们需要生成一个jwt令牌,将生成的 jwt令牌返回给前端。
-
前端拿到jwt令牌之后,会将jwt令牌存储起来。在后续的每一次请求中都会将jwt令牌携带到服务端。
-
服务端统一拦截请求之后,先来判断一下这次请求有没有把令牌带过来,如果没有带过来,直接拒绝访问,如果带过来了,还要校验一下令牌是否是有效。如果有效,就直接放行进行请求的处理。
3. ThreadLocal
3.1 介绍
ThreadLocal 是 Java 提供的一种线程局部变量机制,它允许我们创建线程局部变量,每个使用该变量的线程都有其自己的变量副本,副本之间互不影响。这种机制特别适用于需要在多线程环境中保持状态的情况,比如数据库连接、事务管理等。
常用方法:
-
public void set(T value) 设置当前线程的线程局部变量的值
-
public T get() 返回当前线程所对应的线程局部变量的值
-
public void remove() 移除当前线程的线程局部变量
3.2 在项目中的使用
在新增员工时,创建人id和修改人id设置为固定值。
在拦截请求验证的时候可以获得JWT令牌,问题:在解析出登录员工id后如何传递给Service的save方法?
答:通过ThreadLocal,它是Thread的局部变量,为每个线程提供单独一份的存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,在线程外则不能访问。
4. 公共字段自动填充
在新增员工 或者新增菜品分类 时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工 或者编辑菜品分类时需要设置修改时间、修改人等字段。这些字段属于公共字段,也就是在我们的系统中很多表中都会有这些字段,而在每一个业务方法中进行操作, 编码相对冗余、繁琐,那能不能对于这些公共字段在某个地方统一处理,来简化开发呢?
答: 使用AOP切面编程,实现功能增强,来完成公共字段自动填充功能。
实现步骤:
1). 自定义注解 AutoFill,用于标识需要进行公共字段自动填充的方法
2). 自定义切面类 AutoFillAspect,统一拦截加入了 AutoFill 注解的方法,通过反射为公共字段赋值
3). 在 Mapper 的方法上加入 AutoFill 注解
**技术点:**枚举、注解、AOP、反射
5. Redis部分
这部分可以看我发的文章,点击连接即可跳转
6.HttpClient
6.1 介绍
HttpClient 是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
6.2 HttpClient的核心API:
-
HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。
-
HttpClients:可认为是构建器,可创建HttpClient对象。
-
CloseableHttpClient:实现类,实现了HttpClient接口。
-
HttpGet:Get方式请求类型。
-
HttpPost:Post方式请求类型。
6.3 HttpClient发送请求步骤:
-
创建HttpClient对象
-
创建Http请求对象
-
调用HttpClient的execute方法发送请求
6.4 和@GetMapping、@PostMapping 等注解的区别
- HttpClient 发出的 GET、POST 请求是客户端行为,用于主动向服务器发送请求。提供了更细粒度的控制和灵活性,适用于需要直接与 HTTP 通信的任何客户端应用程序。
- @GetMapping、@PostMapping 等注解是服务器端行为,用于定义如何处理进入的 HTTP 请求。提供了简化的服务器端路由和请求处理,适用于构建 Web 应用程序和服务。
在实际开发中,两者经常一起使用:使用 HttpClient 在客户端应用程序中发送请求,使用 @GetMapping、@PostMapping 在服务器端处理这些请求。
7. 微信小程序
小程序的开发本质上属于前端开发,主要使用JavaScript开发,咱们现在的定位主要还是在后端,所以,对于小程序开发简单了解即可。
7.1 介绍
小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:
文件说明:
**app.js:**必须存在,主要存放小程序的逻辑代码
**app.json:**必须存在,小程序配置文件,主要存放小程序的公共配置
app.wxss: 非必须存在,主要存放小程序公共样式表,类似于前端的CSS样式。
对小程序主体三个文件了解后,其实一个小程序又有多个页面。比如说,有商品浏览页面、购物车的页面、订单支付的页面、商品的详情页面等等。那这些页面会放在哪呢?
答:会存放在pages目录。
每个小程序页面主要由四个文件组成:
文件说明:
**js文件:**必须存在,存放页面业务逻辑代码,编写的js代码。
**wxml文件:**必须存在,存放页面结构,主要是做页面布局,页面效果展示的,类似于HTML页面。
**json文件:**非必须,存放页面相关的配置。
**wxss文件:**非必须,存放页面样式表,相当于CSS文件。
7.2 微信登录流程:
步骤分析:
-
小程序端,调用wx.login()获取code,就是授权码。
-
小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。
-
开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。
-
开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。
-
开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。
-
小程序端,收到自定义登录态,存储storage。
-
小程序端,后绪通过wx.request()发起业务请求时,携带token。
-
开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。
-
开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。
这里需要**编写拦截器JwtTokenUserInterceptor:**统一拦截用户端发送的请求并进行jwt校验,在WebMvcConfiguration配置类中注册拦截器。
到此上部基本结束......