苍穹外卖心得体会

1 登录认证

技术点:JWT令牌技术(JSON Web Token)

JWT(JSON Web Token)是一种令牌技术,主要由三部分组成:Header头部、Payload载荷和Signature签名。Header头部存储令牌的类型(如JWT)和使用的加密算法(如HS256)。Payload载荷包含具体信息,如用户身份、权限、过期时间等声明(Claims)。Signature签名通过加密算法对Header和Payload进行签名,用于验证数据完整性和发行者身份。

在实际业务中,用户登录时,后端服务器接收客户端请求并解析传递的登录信息,验证用户名和密码是否正确。若验证成功,服务器生成JWT令牌返回给前端。后端无需存储Token,只需保存密钥(Secret Key)。后续请求时,服务器通过拦截器在请求前拦截,提取JWT并进行解析与验证:首先检查签名是否有效(防止篡改),再校验Payload中的声明(如是否过期、权限是否有效)。验证通过后,放行请求并执行业务逻辑。

Session与Token的对比

2 分页查询

PageHelper是一个基于MyBatis的分页插件,通过拦截MyBatis的执行器实现分页功能。

当调用 PageHelper.startPage() 设置分页参数后,MyBatis 会通过其拦截器机制自动触发分页逻辑,动态修改后续的 SQL 语句以实现分页。

分页参数通过 ThreadLocal 存储到当前线程的上下文中(PageContext),确保同一线程内的后续操作可获取这些参数。PageHelper 在处理完当前 SQL 后,自动清除 ThreadLocal 中的分页参数,因此同一线程后续的查询不会被分页,除非再次调用 startPage()

    /*** 分页查询套餐** @param setmealPageQueryDTO* @return*/@Overridepublic PageResult pageQuery(SetmealPageQueryDTO setmealPageQueryDTO) {int pageNum = setmealPageQueryDTO.getPage();int pageSize = setmealPageQueryDTO.getPageSize();PageHelper.startPage(pageNum, pageSize);Page<SetmealVO> page = setmealMapper.pageQuery(setmealPageQueryDTO);return new PageResult(page.getTotal(), page.getResult());}
-- 原始SQL
SELECT * FROM table;
-- 重写后(MySQL示例)
SELECT * FROM table LIMIT offset, pageSize;

3 MVC当中的参数注解

1 @RequestBoby:绑定HTTP请求体,反序列化为java对象。-(JSON,XML)

2 @RequestParam:绑定查询参数。(URL后-问号传参,参数用 ? 分隔,参数间用 & 连接。)

3 @PathVariable:绑定URL路径变量。(URL中-路径传参,参数用用 {} 包裹,/连接。)

4 @RequestHeader :绑定HTTP请求头。

5 @CookieValue:绑定Cookie

4 ThreadLocal

ThreadLocal是Java中的一个线程变量,它可以为每个线程提供一个独立的变量副本。ThreadLocal实例是共享的,但每个线程通过它访问的是自己的ThreadLocalMap中的值。

ThreadLocal的主要作用是在多线程的环境下提供线程安全的变量访问。它常用于解决线程间数据共享的问题,特别是在并发编程中,当多个线程需要使用同一个变量时,可以使用ThreadLocal确保每个线程访问的都是自己的变量副本,从而避免了线程安全问题。

ThreadLocal底层是通过ThreadLocalMap来实现的,每一个Thread(线程)对象中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值。

static修饰的ThreadLocal对象属于类级别,在JVM的整个生命周期中仅初始化一次,后续所有的线程通过BaseContext.threadLocal访问同一个ThreadLocal实例,但每个线程的变量副本独立存储,避免重复创建对象。

内存泄漏问题:当ThreadLocal对象使用完之后,应该将Entry对象(即key和value)回收。而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用指向Entry对象。在Entry中,key是弱引用,会触发自动回收机制,但value是强引用不会自动回收,最终导致Entry整体无法被回收机制回收。最终导致线程池中的线程因ThreadLocalMap未清理而出现内存泄漏。解决方法是手动调用ThreadLocal的remove()方法,清除Entry对象。

package com.sky.context;public class BaseContext {public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();public static void setCurrentId(Long id) {threadLocal.set(id);}public static Long getCurrentId() {return threadLocal.get();}public static void removeCurrentId() {threadLocal.remove();}}

示例:

public class ThreadLocalExample {// 定义一个ThreadLocal变量private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 线程A设置值Thread threadA = new Thread(() -> {threadLocal.set(100); // 线程A的值为100System.out.println("线程A的值:" + threadLocal.get()); // 输出100});// 线程B尝试获取值Thread threadB = new Thread(() -> {System.out.println("线程B的值:" + threadLocal.get()); // 输出null(未设置时默认值)threadLocal.set(200); // 线程B的值为200System.out.println("线程B的值:" + threadLocal.get()); // 输出200});threadA.start();threadB.start();}
}

项目当中便可使用这个来存储用户的id值其可全局获取,并且不需要多次实例化对象,在jwt校验结束便可设置。

5 @JSONFormat

在业务需求当中可能会出现前端给我们传递过来的时间参数,其格式不一定符合我们变量的格式。

因此我们需要对前端的时间参数进行格式化,将前端传递的参数指定为pattern当中的参数格式。

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime createTime;

6 基于注解和AOP的公共字段填充

在业务开发中,由于存在大量数据表且字段重叠较多,我们可以使用AOP技术结合注解技术对公共字段进行填充,从而减少代码的冗杂性。

首先,我们需要创建一个自定义注解,用于标记需要自动填充公共字段的方法。

注解:AutoFill

注解的目的作用在Mapper业务层,对那些需要对数据库操作的进行字段填充。

package com.sky.annotation;import com.sky.enumeration.OperationType;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义注解,用于标识需要自动填充的字段*/// 标识在方法上
@Target(ElementType.METHOD)
// 标识在运行时
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {//数据库操作类型 UPDATE INSERTOperationType value();}

然后,AOP切面拦截这些注解的方法

切面:AutoFillAspect

使用的是前置通知,目标方法执行前自动调用,拦截带有@AutoFill的方法,业务当中通过反射的思想进行字段赋值。

package com.sky.aspect;import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.time.LocalDateTime;/*** 自定义切面,用于自动填充公共字段处理逻辑*/@Aspect
@Component
@Slf4j
public class AutoFillAspect {/*** 切入点表达式 com.sky.mapper 包下的所有类中的所有方法并且有 @AutoFill 注解的方法*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut() {}/*** 前置通知,在目标方法执行前执行*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint) {log.info("开始进行公共字段自动填充...");// 通过方法签名获取方法上的注解MethodSignature signature = (MethodSignature) joinPoint.getSignature();AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); // 关键修改if (autoFill == null) {log.info("当前方法没有 @AutoFill 注解,不需要自动填充");return;}OperationType operationType = autoFill.value();//数据库操作类型//获取当前杯拦截的方法的参数--实体对象Object[] args = joinPoint.getArgs();if (args == null || args.length == 0) {log.info("当前方法没有参数,不需要自动填充");return;}Object entity = args[0];log.info("当前自动填充的实体对象:{}", entity.toString());//准备赋值的数据LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();log.info("当前操作的用户id:{}", currentId);//根据当前不同的操作类型,为对应的实体对象通过反射来赋值if (operationType == OperationType.INSERT) {//四个公共字段:createTime、createUser、updateTime、updateUser赋值try {//获取当前实体类中的对应方法(使用本地的常量方法名)Method createTimeMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method createUserMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method updateTimeMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method updateUserMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通过反射为实体对象赋值createTimeMethod.invoke(entity, now);createUserMethod.invoke(entity, currentId);updateTimeMethod.invoke(entity, now);updateUserMethod.invoke(entity, currentId);log.info("为实体类 {} 赋值成功", entity);} catch (Exception e) {e.printStackTrace();}} else if (operationType == OperationType.UPDATE) {try {//两个公共字段:updateTime、updateUser赋值Method updateTimeMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method updateUserMethod = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);updateTimeMethod.invoke(entity, now);updateUserMethod.invoke(entity, currentId);log.info("为实体类 {} 赋值成功", entity);} catch (Exception e) {e.printStackTrace();}}}
}

示例:(这段代码就实现了对公共字段的填充)

    /*** 新增套餐** @param setmeal*/@AutoFill(OperationType.INSERT)void insert(Setmeal setmeal);

7 个人感悟

在初次接触项目时第一感觉就是觉得太复杂了,当时看见那么多的类文件,觉得自己肯定学不好也学不会,但是不断的接触才发现,是有其自己的一套方法,也是可以接受的,也能跟着照葫芦画瓢,其三层架构十分具有条理性的将代码进行分割将业务代码进行拆分,在这个项目当中解除了后端方面对基础业务CRUD的实现,同时也有一些常见开发规范的学习,以及小程序端的开发,实现前后端的联调实现业务的完整性,但是学习的过程也是有不足的很多实现的过程都是跟着老师进行开发的很多都没有由自己开发实现,自己单独分析接口文档进行接口编写的能力还是有所欠缺。在学习过程中也发现自己之前的遗漏点,也是一种学习的方法,遇到不会的再向前学习,再进行运用,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/903504.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

车载功能测试-车载域控/BCM控制器测试用例开发流程【用例导出方法+优先级划分原则】

目录 1 摘要2 位置灯手动控制简述2.1 位置灯手动控制需求简述2.2 位置灯手动控制逻辑交互图 3 用例导出方法以及优先级原则3.1 用例导出方法3.1.1 用例导出方法介绍3.1.2 用例导出方法关键差异分析 3.2 优先级规则3.2.1 优先级划分的核心原则3.2.2 具体等级定义与判定标准 3.3 …

Linux系统基础:基础指令简介(网络概念部分)

简介&#xff1a;Linux 是一种开源的类 Unix 操作系统内核&#xff0c;由 Linus Torvalds 于 1991 年首次发布。经过多年发展&#xff0c;它已成为服务器、嵌入式设备和个人计算机领域的重要操作系统。 网络基础概念 初始协议 简单来说&#xff0c;协议是一种约定&#xff0…

多模态(3):实战 GPT-4o 视频理解

最近&#xff0c;OpenAI 团队的 GPT-4o 模型&#xff0c;在多模态方面的能力有了大幅提升&#xff0c;这次我们就使用 GPT-4o 完成一个视频理解的实战。 1. 环境搭建 1.1 安装 FFmpeg 做视频处理&#xff0c;我们需要用到 FFmpeg 这款功能强大的开源多媒体处理工具。FFmpeg…

(27)VTK C++开发示例 ---将点坐标写入 STL文件

文章目录 1. 概述2. CMake链接VTK3. main.cpp文件4. 演示效果 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;VTK开发 &#x1f448; 1. 概述 此示例使用 vtkSTLWriter 将存储在 vtkPolyData 对象中的 3D 几何数据保存到 STL 文件&#xff0c;并读取stl文件显示…

2. python协程/异步编程详解

目录 1. 简单的异步程序 2. 协程函数和协程对象 3. 事件循环 4. 任务对象Task及Future对象 4.1 Task与Future的关系 4.2 Future对象 4.3 全局对象和循环事件对象 5. await关键字 6. 异步上下文管理 7.异步迭代器 8. asyncio的常用函数 8.1 asyncio.run 8.2 asyncio.get…

智慧园区IOT项目与AI时代下的机遇 - Java架构师面试实战

在互联网大厂的Java求职者面试中&#xff0c;面试官通常会针对实际业务场景提出一系列问题。以下是关于智慧园区IOT项目及AI时代下的机遇的面试模拟对话。 第一轮提问 面试官&#xff1a;马架构&#xff0c;请简要介绍下智慧园区IOT项目的整体架构设计。 马架构&#xff1a;…

论文导读 - 基于特征融合的电子鼻多任务深度学习模型研究

基于特征融合的电子鼻多任务深度学习模型研究 原论文地址&#xff1a;https://www.sciencedirect.com/science/article/pii/S0925400524009365 引用此论文&#xff08;GB/T 7714-2015&#xff09;&#xff1a; NI W, WANG T, WU Y, et al. Multi-task deep learning model f…

AI超级智能体项目教程(二)---后端项目初始化(设计knif4j接口文档的使用)

文章目录 1.选择JDK的版本和相关配置2.添加依赖信息2.1指定lombok版本信息2.2引入hutool工具类2.3了解knif4j依赖2.4引入knif4j依赖 3.contrller测试3.1完成yml文件配置3.2修改默认扫描路径3.3controller具体的内容3.4配置接口和访问路径3.5如何访问3.6调试接口3.6调试接口 1.选…

linux blueZ 第四篇:BLE GATT 编程与自动化——Python 与 C/C++ 实战

本篇聚焦 BLE(Bluetooth Low Energy)GATT 协议层的编程与自动化实践,涵盖 GATT 基础、DBus API 原理、Python(dbus-next/bleak)示例、C/C++ (BlueZ GATT API)示例,以及自动发现、读写特征、订阅通知、安全配对与脚本化测试。 目录 BLE GATT 基础概念 BlueZ DBus GATT 模…

kafka与flume的整合、spark-streaming

kafka与flume的整合 前期配置完毕&#xff0c;开启集群 需求1&#xff1a; 利用flume监控某目录中新生成的文件&#xff0c;将监控到的变更数据发送给kafka&#xff0c;kafka将收到的数据打印到控制台&#xff08;三个node01中运行&#xff09; 1.在kafka中建立topic kafka…

redis高级进阶

1.redis主从复制 redis主从复制1 2.redis哨兵模式 哔哩哔哩视频 redis哨兵模式1 redis哨兵模式2 redis哨兵模式3 3.redis分片集群 redis分片集群1 redis分片集群2 redis分片集群3

uniapp: 低功耗蓝牙(BLE)的使用

在微信小程序中实现蓝牙对接蓝牙秤的重量功能&#xff0c;主要依赖微信小程序提供的低功耗蓝牙&#xff08;BLE&#xff09;API。以下是一个清晰的步骤指南&#xff0c;帮助你完成从连接蓝牙秤到获取重量数据的开发流程。需要注意的是&#xff0c;具体实现可能因蓝牙秤的协议和…

3D架构图软件 iCraft Editor 正式发布 @icraft/player-react 前端组件, 轻松嵌入3D架构图到您的项目

安装 pnpm install icraft/player-react --saveimport { ICraftPlayer } from "icraft/player-react";export default function MyScene() {return <ICraftPlayer srcyour-scene.iplayer />; }icraft/player-react 为开发者提供了一站式的3D数字孪生可视化解决…

云数据中心整体规划方案PPT(113页)

1. 引言 概述&#xff1a;云数据中心整体规划方案旨在构建弹性、高效的云计算基础设施&#xff0c;通过软件定义数据中心&#xff08;SDDC&#xff09;实现资源虚拟化与管理自动化。 2. 技术趋势与背景 技术革新&#xff1a;随着云计算、虚拟化及自动化技术的发展&#xff0c…

(六)机器学习---聚类与K-means

到本篇文章&#xff0c;我们先对前几篇所学习的算法进行一个回顾&#xff1a; 而本篇文章我们将会介绍聚类以及K-means算法。 分类问题回归问题聚类问题各种复杂问题决策树√线性回归√K-means√神经网络√逻辑回归√岭回归密度聚类深度学习√集成学习√Lasso回归谱聚类条件随机…

在html中如何创建vue自定义组件(以自定义文件上传组件为例,vue2+elementUI)

1、先上代码&#xff1a;vueUpload.js var dom <div class"upload-file"><el-upload :action"uploadFileUrl" :before-upload"handleBeforeUpload" :file-list"fileList" :limit"limit":on-error"handleUpl…

计算机基础:二进制基础14,二进制加法

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;计算机基础&#xff1a;二进制基础13&#xff0c;十六进制与二进…

可视化图解算法: 判断是不是二叉搜索树(验证二叉搜索树)

1. 题目 描述 给定一个二叉树根节点&#xff0c;请你判断这棵树是不是二叉搜索树。 二叉搜索树满足每个节点的左子树上的所有节点的值均严格小于当前节点的值&#xff1b;并且右子树上的所有节点的值均严格大于当前节点的值。 数据范围&#xff1a;节点数量满足 1≤n≤10^4…

Markdown转WPS office工具pandoc实践笔记

随着DeepSeek、文心一言、讯飞星火等AI工具快速发展&#xff0c;其输出网页内容拷贝到WPS Office过程中&#xff0c;文档编排规整的格式很难快速复制。 注&#xff1a;WPS Office不支持Markdown格式&#xff0c;无法识别式样。 在这里推荐个免费开源工具Pandoc&#xff0c;实现…

python的turtle库实现四叶草

实现代码&#xff1a; import turtle turtle.pencolor(‘green’) turtle.fillcolor(‘green’) turtle.begin_fill() turtle.circle(100,90) turtle.left(90) turtle.circle(100,90) turtle.right(180) turtle.circle(100, 90) turtle.left(90) turtle.circle(100,90) tu…