前言:
前端的网页,微信小程序,nginx在苍穹外卖的配套资料已经给出,学习项目时主要是了解前端的基本知识和实现与前端交互的接口。
一、分模块开发
概念:
继承:
概念:继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。
作用:简化依赖配置、统一管理依赖。
实现步骤:先创建父工程,设置打包方式为pom,并继承spring-boot-starter-parent;再在子工程中配置继承关系;再在父工程中配置各个工程的共有依赖。
聚合:
将多个模块组织成一个整体,同时进行项目的构建。
聚合工程:一个不具有业务的"空"工程(有且仅有一个pom文件)
作用:快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即可)。
实现:maven中可以通过<modules>设置当前聚合工程的子模块名称。
聚合工程中所包含的模块,在构建时,会自动根据模块间的依赖关系设置构建顺序,与聚合工程中的模块的配置书写位置无关。
本项目:
1、sky-take-out
作为maven父工程,统一管理依赖版本,聚合其他子模块
2、sky-common
作为存放公共类的子模块,其中有:
constant:管理常量,方便常量的维护,增加代码可读性。
context:使用ThreadLocal存储用户id,封装方法便于调用。
enumeration:封装枚举类。
exception:封装异常类,可自定义抛出便于查错。
json:处理json和java对象之间转换的类。
properties:存放配置的类。
result:封装返回至前端(客户端)数据的类。
utils:封装工具类。
3、sky-pojo
存放实体类的子模块,其中有:
dto:封装前端发送给后端的数据的类,便于接收前端数据。
entity:封装数据库中对应表数据结构的类,便于mysql的相关操作。
vo:封装后端返回给前端数据的类,便于前端处理数据。
4、sky-server
进行后端服务和存放配置文件,其中有:
annotation:存放自定义注解,标识方法需要进行aop操作
aspect:存放切面,进行各种公共字段的填充或记录方法消耗时间。
config:存放配置类。
handler:处理特定的请求,事件或异常。此处用于全局异常捕获及处理。
interceptor:拦截器,可自定义拦截条件,此处用于校验jwt令牌。
task:任务类,用于定时使用方法。
websocket:webSocket服务的使用。
controller:控制层,用于接受前端请求。
service:服务层,用于存放业务方法。
mapper:持久层,存放操作mysql的方法。
二、使用到的技术
1、nginx
nginx反向代理:将前端发送的动态请求由nginx转发到后端服务器。
作用:
1、缓存数据,提高访问速度。
2、进行负载均衡。
3、隐藏后端服务器接口,保证后端服务安全。
负载均衡策略:
轮询:默认方式,平均分配。
weight:权重方式,权重越高分配的客户端请求越多。
ip_hash:依据ip分配方式,每个访客固定访问一个后端服务。
least_conn:请求优先分配给连接数较少的后端服务。
url_hash:依据url分配,相同的url会被分配到一个后端服务。
fair:依据响应时间分配,响应时间短的服务被优先分配。
配置信息:
upstream webservers{
server 127.0.0.1:8080 weight=90 ;
server 127.0.0.1:8088 weight=10 ;
}
//配置权重,意思十个服务九个转发到8080的端口,一个到8088端口
server {
listen 80;
server_name localhost;
location / {
root html/sky;
index index.html index.htm;
}
# 反向代理,处理管理端发送的请求
location /api/ {
proxy_pass http://localhost:8080/admin/;
#proxy_pass http://webservers/admin/;
}
# 反向代理,处理用户端发送的请求
location /user/ {
proxy_pass http://webservers/user/;
}
# WebSocket
location /ws/ {
proxy_pass http://webservers/ws/;
proxy_http_version 1.1;
proxy_read_timeout 3600s;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "$connection_upgrade";
}
2、git
介绍:
Git是一个分布式的版本控制工具,通常用来对软件开发过程中的源代码文件进行管理。通过git仓库来存储和管理这些文件,Git仓库分两种:
本地仓库:开发人员自己电脑上的Git仓库。
远程仓库:远程服务器上的Git仓库。
commit:提交,将本地文件和版本信息保存到本地仓库。
push:推送,将本地仓库文件和版本信息上传到远程仓库。
pull:拉取,将远程仓库文件和版本信息下载到本地仓库。
使用:
基础配置:
先在gitee创建账号和仓库,再进行git全局设置(安装git后右键可以选择git命令行--git bash here)。
设置用户信息:git config --global user.name "用户名称"
git config --global user.email "email地址"
git config --list可以查看配置信息。
常用命令:
git status 查看文件状态
git add 将文件的修改加入暂存区
git reset 将暂存区的文件取消暂存或切换到指定版本
git commit 将暂存区文件修改提交到版本库
git log 查看日志
git tag 列出已有标签
git tag [name] 将标签推送至远程仓库
git checkout -b[branch][name]
远程仓库操作:
git remote 查看远程仓库
git remote add 添加远程仓库
git clone 从远程仓库克隆
git pull 从远程仓库拉取
git push 推送到远程仓库
分支命令:
同一仓库可以有多个分支,各个分支相互独立,互不影响。
git branch 查看分支
git branch [name] 创建分支
git checkout [name] 切换分支
git push [shortName] [name] 推送至远程仓库分支
git merge [name] 合并分支
在IDEA中使用Git:
先在Settings中配置git
Commit表示提交到本地仓库,Push为推送到远程仓库,Pull为从远程仓库拉取。(仓库需自己配置地址)
左下角的git可以选择切换分支
3、MD5
工作原理:
MD5 算法接收任意长度的输入消息,经过一系列处理后,生成一个 128 位(16 字节)的哈希值,通常以 32 位十六进制数字的形式表示。
用法:将前端传来的注册登录密码进行MD5加密存入数据库中,以后每次前端传来登录请求时,校验的登录密码使用MD5加密再与数据库存储的密码比对。
优点:提高安全性。
例子
String password="";
password=DigestUtils.md5DigestAsHex(password.getBytes());//MD5加密
4、Swagger
作用:生成接口文档,以及在线接口调试,便于后端开发时测试接口。
使用方法:
1、导入knife4j的maven2、配置3、设置静态资源映射
访问localhost:8080/doc.html即可访问文档
常用注解:
@Api:用在类上。
@ApiModel:用在实体类上。
@ApiModelProperty:用在属性上
@ApiOperation:用在方法上
注解效果:
@Api(tags="员工相关接口")
public class EmployeeController {
@ApiOperation(value = "员工登录")
public Result<EmployeeLoginVO> login(...){...}
@ApiOperation(value = "员工退出")
public Result<String> logout(...) {...}
}
页面展示:
5、JWT令牌
JWT 令牌由三部分组成,各部分之间用点(.
)分隔,格式为 Header.Payload.Signature
。
Header(头部):包含两部分信息,令牌的类型(通常是 JWT)和使用的签名算法,如 HMAC SHA256 或 RSA。它是一个 JSON 对象,经过 Base64Url 编码后形成 JWT 的第一部分。
Payload(负载):包含声明(Claims),声明是关于实体(通常是用户)和其他数据的声明。
Signature(签名):为了创建签名部分,需要使用编码后的 Header、编码后的 Payload、一个密钥(secret)和 Header 中指定的签名算法。例如使用 HMAC SHA256 算法,签名将按以下方式创建:
使用方法:需要自己编写JWT令牌的生成和解密方法。封装到JWT工具类进行使用。
JWT令牌的生成:
JWT令牌的解密
6、ThreadLocal
基本概念:
ThreadLocal
提供了线程局部变量,也就是说,你创建的一个 ThreadLocal
变量,每个使用到它的线程都会有这个变量的独立副本,每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。
使用方法:
定义设置,返回,移除当前线程的线程局部变量的方法并将其封装到context类,便于调用。
此项目用于获取用户 id,在校验令牌时候将令牌解析出来的id存到线程局部变量。
7、PageHelper
基本概念
PageHelper 是一个非常流行的 MyBatis 分页插件,它通过拦截 MyBatis 的 SQL 语句来自动添加分页参数,从而实现分页查询。
使用方法
前提:先引入依赖,再在类前引入@Slf4j注解。
在调用Mapper的查询方法前使用PageHelper.startPage(x1,x2)开启分页查询;
//x1表示页码,x2表示每页数据数量
此后查询方法返回结果将被自动封装到Page<T>中,通过调用Page的getTotal()方法可以得到页码总数,调用Page.getResult()方法可以得到查询到的数据列表。
8.公共字段自动填充(AOP)
基本概念
在软件开发中,有些功能会贯穿于应用程序的多个模块,如日志记录、事务管理、安全验证等,这些功能被称为横切关注点。传统的面向对象编程会导致这些横切关注点的代码分散在各个模块中,造成代码重复、可维护性差等问题。AOP 的核心思想就是将这些横切关注点从业务逻辑中分离出来,形成独立的模块,即切面(Aspect),从而提高代码的可维护性和复用性。
相关术语
-
切面(Aspect):封装横切关注点的模块,包含了通知和切点。例如,日志记录功能可以封装成一个切面。
-
连接点(Join Point):程序执行过程中的某个特定位置,如方法调用、异常抛出等。在 AOP 中,连接点通常指方法的执行。
-
切点(Pointcut):用于定义哪些连接点会被增强,即确定哪些方法会被切面影响。切点可以通过表达式来指定,如匹配特定的类、方法名等。
-
通知(Advice):切面在特定连接点执行的代码,根据执行时机的不同,通知可以分为以下几种类型:
-
前置通知(Before Advice):在目标方法执行之前执行。
-
后置通知(After Advice):在目标方法执行之后执行,无论目标方法是否抛出异常。
-
返回通知(After Returning Advice):在目标方法正常返回后执行。
-
异常通知(After Throwing Advice):在目标方法抛出异常后执行。
-
环绕通知(Around Advice):环绕在目标方法执行前后,相当于前置通知和后置通知的结合,可以控制目标方法的执行。
-
使用方法:
1、自定义注解AutoFill,用于标识需要进行公共字段自动填充的方法。
@Target定义注解的使用范围只能应用于方法上
@Retention定义注解的声明周期
RetentionPolicy.RUNTIME:表示注解在运行时可以通过反射机制访问。
其他保留策略:
RetentionPolicy.SOURCE:注解仅保留在源代码级别,编译时会被丢弃。
RetentionPolicy.CLASS:注解保留在编译后的字节码文件中,但运行时无法通过反射访问 (默认策略)。
2、自定义切面AutoFillAspect,统一拦截加入了AutoFill注解的方法,通过反射为公共字段赋值。
@Aspect表明这是一个切面
@Pointcut指定切入点,其中execution指定路径,@annotation指定切入点需引用什么注解。
3、在Mapper的方法上加入AutoFill注解(注解要赋值类型)
9、阿里OSS
作用:
存储菜品的图片于阿里OSS,前端可以直接访问图片url获得资源数据。数据库只需要存储url即可,减小数据库负担。
使用方法:
配置Oss工具类
(以下需自行注册获取)
endpoint:阿里云 OSS 服务的访问域名或端点。
accessKeyId:阿里云账号的访问密钥 ID
accessKeySecret:阿里云账号的访问密钥密钥。
bucketName:阿里云 OSS 中的存储桶名称
创建OSSClient实例和putObject请求传输数据,最后关闭OSS。
拼接文件访问路径并返回,返回值自行接收并存入数据库。
通过oss配置类将aliOss的属性导入工具类并通过@Bean交给容器,应用时只需要注入即可直接调用工具类方法。
在传文件名称时候可以用UUID的随机数+原文件后缀的方式
10、事务管理
四个特性:
- 原子性(Atomicity)
- 含义:事务是一个不可分割的最小工作单元,事务中的所有操作要么全部成功执行,要么全部不执行。就像一个原子一样,是一个不可再分的整体。
- 示例:在银行转账事务中,从账户 A 扣除金额和向账户 B 添加金额这两个操作必须同时成功或者同时失败。如果只完成了其中一个操作,就会导致数据不一致,违反了原子性。
- 一致性(Consistency)
- 含义:事务执行前后,数据库的完整性约束没有被破坏,数据处于一致的状态。也就是说,事务必须使数据库从一个一致性状态转换到另一个一致性状态。
- 示例:在一个库存管理系统中,当进行商品出库操作时,不仅要更新库存表中商品的数量,还要确保相关的统计信息(如总库存价值、库存周转率等)也得到正确的更新。这样才能保证数据库在出库事务执行前后,数据在整体上是一致的,符合业务规则和完整性约束。
- 隔离性(Isolation)
- 含义:多个事务并发执行时,每个事务都应该感觉不到其他事务的存在,就好像它们是在独立运行一样。隔离性确保了事务之间不会相互干扰,避免数据不一致的情况发生。
- 示例:假设有两个并发的事务,一个事务在更新用户的余额,另一个事务在查询该用户的余额。如果没有隔离性,查询事务可能会读到更新事务尚未提交的数据,导致查询结果不准确。通过设置合适的隔离级别,可以确保查询事务只能读到已提交的数据,从而避免数据不一致的问题。
- 持久性(Durability)
- 含义:一旦事务被提交,它对数据库所做的修改就会永久保存下来,即使系统出现故障(如断电、服务器崩溃等),这些修改也不会丢失。
- 示例:当用户完成一笔订单并提交事务后,订单信息、库存变化等相关数据会被持久化到数据库中。即使在事务提交后系统立即出现故障,在系统恢复后,这些数据仍然能够被正确地恢复和查询到,保证了数据的持久性。
作用:
当事务中有一操作未执行成功则回退至开始时的状态,保证数据的一致性,避免数据库混乱。
使用方法:
先在启动类方法上加@EnableTransactionManagement
再在需要事务管理的方法前加@Transactional注解
扩展
事务的隔离级别
未提交读(Read Uncommitted)
一个事务能够读取到别的事务中没有提交的更新数据。事务中的修改,即使没有提交,其他事务也可以看得到。在这种隔离级别下有可能发生脏读,不可重复读和幻读。
提交读(Read Committed)
事务中的修改只有提交以后才能被其它事务看到。在这种隔离级别下解决了脏读,但是有可能发生不可重复读和幻读。
可重复读(Repeated Read)
保证了在同一事务中先后执行的多次查询将返回同一结果,看到的每行的记录的结果是一致的,不受其他事务的影响。但是这种级别下有可能发生幻读。
可串行化(Serializable)
不允许事务并发执行,强制事务串行执行。就是在读取的每一行数据上都加上了锁,读写相互都会阻塞,所以效率很低下。这种隔离级别最高,是最安全的,但是性能最低,不会出现脏读,不可重复读,幻读。(性能差,不推荐使用,Mysql的InnoDB引擎默认隔离级别是可重复读加上next-key lock锁来避免幻读)
@Transactional
@Transactional(rollbackFor = Exception.class //回滚事务
,propagation = Propagation.REQUIRED, //事务传播行为
isolation = Isolation.DEFAULT, //事务隔离级别
readOnly = true //是否有事务
11、redis
简介
Redis是一个基于内存的key-value结构数据库
Redis的特点包括:
1、数据持久化:通过快照和日志两种方式,实现数据的持久化存储和恢复。
2、高性能:Redis基于内存,读写速度非常快,且支持多种数据结构和操作命令,适合高并发场景。
3、多功能性:除了作为缓存,Redis还可以用作数据库、消息队列、分布式锁等。
4、高可用性:Redis支持主从复制、哨兵和集群等机制,提供了高可用性和容错能力。
字符串操作命令:
SET key value 设置指定key的值
Get key 获取指定key的值
SETEX key seconds value 设置指定key的值,并将过期时间设为seconds秒
SETNX key value 只有在key不存在时设置key的值
哈希操作命令:
HSET key field value 将哈希表key中的字段field的值设为value
HGET key field 获取存储在哈希表中指定字段的值
HDEL key field 删除存储在哈希表中的指定字段
HKEYS key 获取哈希表中所有字段
HVALS key 获取哈希表中所有的值
列表操作命令:
LPUSH key value1 [value2] 将一个或多个值插入到列表头部
LRANGE key start stop 获取列表指定范围的元素
RPOP key 移除并获取列表最后一个元素
LLEN key 获取列表长度
集合操作命令:
SADD key member1 [member2] 向集合多添加一个或多个成员
SMEMBERS key 返回结合中的所有成员
SCARD key 获取集合的成员数
SINTER key1 [key2] 返回给定所有集合的交集
SUNION key1 [key2] 返回所有给定集合的并集
SREM key member1 [member2] 删除集合中一个或多个成员
有序集合操作命令:
ZADD key score1 member1 [score2 member2] 向有序集合添加一个或多个成员
ZRANGE key start stop [WITHSCORES] 通过索引区间返回有序集合中指定区间的成员
ZINCRBY key increment member 有序集合中对指定成员的分数上增量increment
ZREM key member [member...] 移除有序集合中的一个或多个成员
通用命令:
KEYS pattern 查找所有符合给定模式的key
EXISTS key 检查给定key是否存在
TYPE key 返回key所储存的值的类型
DEL key 该命令用于在key存在时删除key
IDEA使用方法:
配置数据源
spring:
redis:
host: 地址
port: 端口号
password: 密码
database: 数据库号
编写配置类,将redisTemplate交给容器,注入使用。
在使用时候注入,调用函数的opsForValue(),posForHash(),opsForList(),opsForSet(),opsForZSet()获得对应的Operations再来调用操作方法(可导览选)
例子:操作字符串类型,redis存储key="SHOP_STATUS",value=status对应的值
(redis序列化器在将数据存入redis时会转化格式,故在idea操作redis时,不推荐手动改redis数据库)
12、HttpClient
介绍:
HttpClient是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。
作用:
在java程序中通过编码的方式发送HTTP请求。
例子
public void testGET () throws Exception {
// 创建 httpClient 对象
CloseableHttpClient httpClient = HttpClients.createDefault ();
// 创建请求对象
HttpGet httpGet = new HttpGet ("http://localhost:8080/user/shop/status");
// 发送请求,接受响应结果
CloseableHttpResponse response = httpClient.execute (httpGet);
// 获取服务端返回的状态码
int statusCode = response.getStatusLine ().getStatusCode ();
System.out.println ("服务端返回的状态码为:" + statusCode);
HttpEntity entity = response.getEntity ();
String body = EntityUtils.toString (entity);
System.out.println ("服务端返回的数据为:" + body);
// 关闭资源
response.close ();
httpClient.close ();
}
13、微信登录
根据视频注册小程序并且导入代码。
支付功能跳过可以参考《苍穹外卖》电商实战项目实操笔记系列(P66~P122)【中】-优快云博客
14、SpringCache
介绍
SpringCache 是 Spring 框架提供的缓存抽象机制,用于简化应用中的缓存操作,实现了基于注解的缓存功能
SpringCache提供了一层抽象,底层可以切换不同的缓存实现
常用注解
注解 | 说明 |
---|---|
@EnableCaching | 开启缓存注解功能,通常加在启动类上 |
@Cacheable | 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中 |
@CachePut | 将方法的返回值放到缓存中 |
@CacheEvict | 将一条或多条数据从缓存中删除 |
例子
@GetMapping
@Cacheable (cacheNames = "userCache",key = "#id") //key 的生成: userCache::10
public User getById (long id){ //存储的值为user
User user = userMapper.getById (id);
return user;
}
@DeleteMapping
@CacheEvict (cacheNames = "userCache", key = "#id")
public void deleteById (long id){
userMapper.deleteById (id);
}
@DeleteMapping("/delAll")
@CacheEvict(cacheNames = "userCache",allEntries = true) //删除userCache下的所有键值对
public void deleteAll(){
userMapper.deleteAll();
}
15、Spring Task
Spring Task是Sping框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。属于spring-context的附属依赖。
作用:
可以用于简化定时任务如缓存清理,数据备份等操作。
使用方法:
使用@Scheduled注解,其中cron= "秒 分 时 日 月 周" 表示每多少时间出现一次,其中天和周不能同时出现,且周表示周几的意思。
16、WebSocket
定义:
WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信 —— 浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。
HTTP 协议和 WebSocket 协议对比:
HTTP 是短连接
WebSocket 是长连接
HTTP 通信是单向的,基于请求响应模式
WebSocket 支持双向通信
HTTP 和 WebSocket 底层都是 TCP 连接
作用和应用场景:
实现全双工通信:WebSocket 允许客户端和服务器在单个 TCP 连接上,同时进行双向数据传输 。区别于 HTTP 的请求 - 响应单向模式,它能让双方随时主动发送消息,提升通信效率 。
创建持久连接:浏览器和服务器完成一次握手后,即可建立持久性连接 。避免了 HTTP 短连接中频繁建立和断开连接的开销,适合需要持续交互的场景 。
降低资源消耗:相比传统轮询(浏览器定时向服务器发送请求获取数据 ),WebSocket 由服务器主动推送数据,减少了不必要的请求,节省带宽和服务器资源 。
实时数据传输:能及时传输数据,满足对实时性要求高的应用场景,减少数据延迟 。
应用场景:弹幕,在线聊天,实时客服,金融股票等。
使用方法:
1、配置websocket.html2、导入WebSocket的maven3、导入WebSocket服务端组件
4、导入WebSocketConfiguration配置类,注册WebSocket的服务端组件
5、导入方法使用
配置WebSocket服务端组件WebSocketServer
通过配置类注册WebSocket的服务端组件
在任务类中导入使用
17、Apache ECharts
介绍:
是一款基于javascript的数据可视化图标库,提供直观,生动,可交互,可个性化定制的数据可视化图表。
后端只需要提供符合格式的动态数据响应给前端来进行展示。
18、Apache POI
介绍
Apache POI 是一个处理 Microsoft Office 各种文件格式的开源项目。简单来说就是,我们可以使用 POI 在 Java 程序中对 Microsoft Office 各种文件进行读写操作。一般情况下,POI 都是用于操作 Excel 文件。
应用场景:
可以用来批量处理文档提高效率,也可以用来业务系统导出excel报表。
使用方法:
先设计excel模板文件(模板文件放在resources下的自定义文件夹内),再将查询到的数据写入模板文件,最后通过输出流将excel文件下载到客户端浏览器。