苍穹外卖技术栈

重难点详解

1、定义全局异常

在这里插入图片描述

2、ThreadLocal

  • ThreadLocal 并不是一个Thread,而是Thread的一个局部变量
  • ThreadLocal 为每一个线程提供独立的存储空间,具有线程隔离的效果,只有在线程内才能取到值,线程外则不能访问
public void set(T value) 设置当前线程的线程局部变量的值public T get() 返回当前线程所对应的线程局部变量的值public void remove() 移除当前线程的线程局部变量

注意:客户端每次发送http请求,对应的服务端都会分配一个新的线程,在处理过程中涉及到系列方法属于同一个线程:
【1】LoginCheckFilter的doFilter方法
【2】EmployeeController的update方法
【3】MyMetaObjectHandler的updateFill方法

3、BaseContext上下文

BaseContext是基于ThreadLocal类封装的工具类,用于在同一线程中的封装数据和获取数据

> BaseContext工具类用于存放和取出当前登录的用户的id
> 
public class BaseContext {public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();/*** 设置当前线程的线程局部变量的值* @param id*/public static void setCurrentId(Long id) {threadLocal.set(id);}public static Long getCurrentId() {return threadLocal.get();}public static void removeCurrentId() {threadLocal.remove();}

4、PageHelper分页

pagehelper是mybatis 提供的分页插件

开始分页

PageHelper.startPage(1, 10)// selectlist查询数据库的时候会自动加上limit 1,10。

在CategoryServiceImpl声明pageQuery方法,及其父类接口:

    public PageResult pageQuery(CategoryPageQueryDTO categoryPageQueryDTO) {// 分页器对象Page<Category> page = new Page<>();List<Category> categoryList  = categoryMapper.pageQuery(categoryPageQueryDTO);Long total = page.getTotal();// 分页return new PageResult(total,categoryList);}

在EmployeeServiceImpl声明pageQuery方法,及其父类接口

public PageResult pageQuery(EmployeePageQueryDTO dto) {PageHelper.startPage(dto.getPage(),dto.getPageSize());Page<Employee> page = employeeMapper.pageQuery(dto);return new PageResult(page.getTotal(),page.getResult());}

原理:

  • Mybatis内置了分页拦截器PageInterceptor,即在执行相关sql之前会拦截一些操作
  • 通过`setLocalPage()方法,将分页信息保存在当前线程中。分页查询方法与之处在同一个线程中,共享ThreadLocal中的数据
  • selectlist查询之后赋值给的List list。这个list是Page 类型
  • 再将list放到PageInfo<>中即可。

查询

List<Employee> list = employeeMapper.selectByExample(Example);  

分页结果展示

 PageInfo<Employee> pageInfo = new PageInfo<>(list);  System.out.println("总记录数:"+pageInfo.getTotal());  System.out.println("总页数:"+pageInfo.getPages());  System.out.println("一页的大小:"+pageInfo.getSize());  

5、日期按年月日显示

  • WebMvcConfiguration 中扩展Spring MVC的消息转换器,统一对日期类型进行格式化处理
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {log.info("Spring MVC扩展消息转化器...");// 创建消息转换器对象MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();// 设置该消息转换器使用 JacksonObjectMapper 进行转换messageConverter.setObjectMapper(new JacksonObjectMapper());// 将消息转换器对象追加到 mvc 框架的转换器集合中(加到最前面)converters.add(0,messageConverter);}

在这里插入图片描述

6、启用/禁用账号

/**
* 启用禁用员工账号
*/
@Override
public void startOrStop(Integer status, Long id) {Employee employee = Employee.builder().status(status).id(id).build();employeeMapper.update(employee);
}

Employee.builder().build()建造者模式,builder构造对象;@Builder 注解

6、公共字段填充

mybatis-plus提供了公共字段自动填充功能

// 在EmployeeMapper
@AutoFill(value = OperationType.INSERT)
void insert(Employee employee);@AutoFill(value = OperationType.UPDATE)
void update(Employee employee);// CategoryMapper
@AutoFill(value = OperationType.INSERT)
void insert(Category category);@AutoFill(value = OperationType.UPDATE)
void update(Category category);

实现步骤
【1】实体类的属性上加入注解@TableField

create_time,create_user 使用 Insert
update_time,update_user 使用 Insert/update
FieldFill.DEFAULT  //默认不自动填充@ApiModelProperty(value = "创建时间")@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@ApiModelProperty(value = "更新时间")@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;

【2】元数据对象处理器,实现MetaObjectHandler接口,

@Slf4j
@Component // 交给spring管理
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("start insert fill...");metaObject.setValue("createTime", LocalDateTime.now());metaObject.setValue("updateTime",LocalDateTime.now());metaObject.setValue("createUser", BaseContext.getCurrentId());metaObject.setValue("updateUser",BaseContext.getCurrentId());}@Overridepublic void updateFill(MetaObject metaObject) {log.info("start update fill...");long id = Thread.currentThread().getId();log.info("当前线程id:{}",id);metaObject.setValue("updateTime",LocalDateTime.now());metaObject.setValue("updateUser",BaseContext.getCurrentId());}

7、文件上传

 @PostMapping("/upload")@ApiOperation("文件上传")public Result<String> upload(MultipartFile file){log.info("文件上传: {}", file);try {// 原始文件名String originalFilename = file.getOriginalFilename();// 截取原始文件名的后缀String suffix= originalFilename.substring(originalFilename.lastIndexOf("."));// 构造新文件名称String objectName = UUID.randomUUID().toString() + extension;
// 文件的请求路径File dir = new File(basePath);// 创建一个目录对象//	将临时文件转存到指定位置
file.transferTo(new File(basePath+fileName));

7、请求参数requestparam与requestbody

@ResquestBody要搭配@PostMapping使用,@ResquestParam要搭配@GetMapping使用
区别equestparam与requestbody

  • 【@RequestParam】接收的参数是来自HTTP请求体或请求url的QueryString中
public void aliReceive(@RequestParam("message") String message)

@RequestParam用来处理 Content-Type 为 application/x-www-form-urlencoded 编码的内容

@RequestParam 接受JSON的字符串

@RequestParam注解无法读取application/json格式数据

  • 【@RequestBody】一般用来处理application/json、application/xml等类型的数据。

POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用

  • 【@PathVariable】
// 参数名和url变量名不一致
@RequestMapping("/user/{id}")
public String testPathVariable(@PathVariable("id") String userId){System.out.println("路径上的占位符的值="+id);
}// 参数名和url变量名一致
@RequestMapping("/getUser/{name}")public User getUser(@PathVariable String name){return userService.selectUser(name);}通过@PathVariable,例如/blogs/1
通过@RequestParam,例如blogs?blogId=1

8、HttpClient

【发送请求步骤】

  • 创建HttpClient对象
  • 创建请求对象HttpGet、HttpPost等
  • 请求对象调用HttpClient的execute方法发送请求
/*** 测试通过httpclient发送GET方式的请求*/
@Test
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();
}

9、微信小程序登录

在application-dev.yml中写入具体的配置属性

skywechat:appid: ${sky.wechat.appid}secret: ${sky.wechat.secret}

创建user/UserController,创建微信登录的方法

@PostMapping("/login")@ApiOperation("微信登录")public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){log.info("微信用户登录:{}",userLoginDTO);// 微信登录User user = userService.wxLogin(userLoginDTO);// 为微信用户生成jwt令牌Map<String, Object> claims = new HashMap<>();claims.put(JwtClaimsConstant.USER_ID, user.getId());String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);UserLoginVO userLoginVO = UserLoginVO.builder().id(user.getId()).openid(user.getOpenid()).token(token).build();return Result.success(userLoginVO);

在UserServiceImpl中,实现wxLogin的方法,及其父类接口

 // 微信服务接口地址public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
public User wxLogin(UserLoginDTO userLoginDTO) {String openid = getOpenid(userLoginDTO.getCode());// 判断openid是否为空,如果为空表示登录失败,抛出业务异常if (openid == null){throw new LoginFailedException(MessageConstant.LOGIN_FAILED);}// 判断当前用户是否为新用户User user = userMapper.getByOpenId(openid);// 如果是新用户,自动完成注册if (user == null){user = User.builder().openid(openid).createTime(LocalDateTime.now()).build();userMapper.insert(user);}private String getOpenid(String code){// 调用微信接口服务,获取当前微信用户的openidMap<String, String> map = new HashMap<>();map.put("appid", weChatProperties.getAppid());map.put("secret", weChatProperties.getSecret());map.put("js_code", code);map.put("grant_type", "authorization_code");String json = HttpClientUtil.doGet(WX_LOGIN, map);JSONObject jsonObject = JSON.parseObject(json);String openid = jsonObject.getString("openid");return openid;}

编写JwtTokenUserInterceptor拦截器,校验用户端token是否是合法的

public class JwtTokenUserInterceptor implements HandlerInterceptor {...}

在WebMvcConfiguration中,注册拦截器


@Autowired
private JwtTokenUserInterceptor jwtTokenUserInterceptor;protected void addInterceptors(InterceptorRegistry registry) {log.info("开始注册自定义拦截器...");registry.addInterceptor(jwtTokenAdminInterceptor).addPathPatterns("/admin/**").excludePathPatterns("/admin/employee/login");registry.addInterceptor(jwtTokenUserInterceptor).addPathPatterns("/user/**").excludePathPatterns("/user/user/login").excludePathPatterns("/user/shop/status");

10、Redis缓存

RedisTemplate是Spring Data Redis提供给用户的最高级的抽象客户端,用户可直接通过RedisTemplate进行多种操作、异常处理、序列化、发布订阅等
————————————————————
spring-data-redis的提供了如下功能:

  1. 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
  2. 进行了归类封装,将同一类型操作封装为operation接口

ValueOperations:简单K-V操作 redisTemplate.opsForValue().
SetOperations:set类型数据操作 redisTemplate.opsForSet().
ZSetOperations:zset类型数据操作 redisTemplate.opsForZSet().
HashOperations:针对map类型的数据操作 redisTemplate.opsForHash()
ListOperations:针对list类型的数据操作 redisTemplate.opsForList().

  1. 针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)
  1. JdkSerializationRedisSerializer:POJO对象的存取场景,使用JDK本身序列化机制,将pojo类通过ObjectInputStream/ObjectOutputStream进行序列化操作,最终redis-server中将存储字节序列。是目前最常用的序列化策略。
  1. StringRedisSerializer:Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是“newString(bytes,charset)”和“string.getBytes(charset)”的直接封装。是最轻量级和高效的策略。
  1. JacksonJsonRedisSerializer:jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。因为jackson工具在序列化和反序列化时,需要明确指定Class类型,因此此策略封装起来稍微复杂。
  1. OxmSerializer:提供了将javabean与xml之间的转换能力,目前可用的三方支持包括jaxb,apache-xmlbeans;redis存储的数据将是xml工具。不过使用此策略,编程将会有些难度,而且效率最低;不建议使用。【需要spring-oxm模块的支持】

11.BeanUtils

BeanUtils.copyProperties的用法

常见场景:

  • 接口中将前端请求参数xxVo,转化为xxQuery对象,查询数据库条件对象
BeanUtils.copyProperties(source,target);BeanUtils.copyProperties("要转换的类", "转换后的类");
  1. 源对象source的属性拷贝值赋给目标对象target的过程中,属性名和属性类型都相同的属性才能被成功拷贝赋值
  2. 做赋值的属性一定要有对应的setter/getter才能成功赋值
  3. 1.对于类型为Boolean/Short/Integer/Float/Double的属性,它会转换为0
  4. java.util.Date/BigDecimal/java.sql.Date/java.sql.Timestamp/java.sql.Time这几个类,如果值为null,则在copy时会抛异常,需要使用对应的Conveter:

org.springframework.beans.BeanUtils BeanUtils.copyProperties(a, b):a复制到b
org.apache.commons.beanutils.BeanUtilsBeanUtils.copyProperties(a, b):b复制到a

12、Spring Task定时处理

Spring Task 是Spring框架提供的任务调度工具

【Spring Task实现步骤】

  1. 导入maven左边
  2. 启动类添加@EnableScheduling注解,开启任务调度
  3. 自定义定时任务 @Scheduled(cron=" ")
    @Scheduled(cron = "0 * * * * ? *") // 每分钟触发一次public void processTimeoutOrder(){log.info("定时处理超时订单:{}", LocalDateTime.now());

13、WebSocket

WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接, 并进行双向数据传输

创建websocket/WebSocketServer,实现WebSocket服务

@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {// 存放会话对象
private static Map<String, Session> sessionMap = new HashMap();//连接建立成功@OnOpenpublic void onOpen(Session session, @PathParam("sid") String sid) {System.out.println("客户端:" + sid + "建立连接");sessionMap.put(sid, session);}
//收到客户端消息@OnMessagepublic void onMessage(String message, @PathParam("sid") String sid) {System.out.println("收到来自客户端:" + sid + "的信息:" + message);}
// 关闭连接@OnClosepublic void onClose(@PathParam("sid") String sid) {System.out.println("连接断开:" + sid);sessionMap.remove(sid);}
// public void sendToAllClient(String message) {Collection<Session> sessions = sessionMap.values();for (Session session : sessions) {try {//服务器向客户端发送消息session.getBasicRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}

创建WebSocketTask中,通过WebSocket向客户端发送消息

    public void sendMessageToClient() {webSocketServer.sendToAllClient("这是来自服务端的消息:" + DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now()));}

14、LambdaQueryWrapper

LambdaQueryWrapper是Mybatis-Plus框架中的一个查询条件构造器

LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getName, "张三") // 等于eq.ne(User::getAge, 18) // 不等于ne.gt(User::getSalary, 5000) // 大于gt.like(User::getEmail, "abc") //模糊查询.orderByDesc(User::getCreateTime);// 降序
List<User> userList = userMapper.selectList(queryWrapper);

15、Nginx

  • 反向代理:允许Nginx服务器代表客户端将请求转发给后端的多个应用服务器
    在这里插入图片描述

16、Spring Cache

Spring Cache 是一个框架,实现了基于注解的缓存功能;底层可以切换不同的缓存实现,如:Redis

    @PostMapping@ApiOperation(value = "新增套餐")@CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")public Result save(@RequestBody SetmealDTO setmealDTO){setmealService.save(setmealDTO);}// 修改信息@CacheEvict(cacheNames = "setmealCache",allEntries = true)public Result update(@RequestBody SetmealDTO setmealDTO){setmealService.update(setmealDTO);return Result.success();}

常用注解:

@EnableCaching // 开启缓存注解功能,启动类上@Cacheable //在方法执行前查询缓存中是否有数据@CachePut //将方法的返回值加入缓存中@CacheEvict //删除缓存

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

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

相关文章

前端自动化部署,Devops,CI/CD

DevOps 提到 Jenkins&#xff0c;想到的第一个概念就是 CI/CD 在这之前应该再了解一个概念。 DevOps Development 和 Operations 的组合&#xff0c;是一种方法论&#xff0c;并不特指某种技术或者工具。DevOps 是一种重视 Dev 开发人员和 Ops 运维人员之间沟通、协作的流程。…

【0904作业】QT 完成登陆界面跳转到聊天室+完成学生管理系统的查找和删除功能

一、完成登陆界面跳转到聊天室 1> 项目结构 2> 源码 ① .pro ②main #include "mywnd.h" #include"chatCli.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MyWnd w;w.show();Form f;QObject::co…

无涯教程-JavaScript - EDATE函数

描述 EDATE函数返回表示日期的序列号,该序列号是在指定日期(start_date)之前或之后的月份数。 使用EDATE来计算到期日或到期日,该到期日或到期日与发行日期在当月的同一天。 语法 EDATE (start_date, months)争论 Argument描述Required/OptionalStart_date 代表开始日期的…

【Java 基础篇】Java 数组使用详解:从零基础到数组专家

如果你正在学习编程&#xff0c;那么数组是一个不可或缺的重要概念。数组是一种数据结构&#xff0c;用于存储一组相同类型的数据。在 Java 编程中&#xff0c;数组扮演着非常重要的角色&#xff0c;可以帮助你组织、访问和操作数据。在本篇博客中&#xff0c;我们将从零基础开…

GPT引领前沿与应用突破之GPT4科研实践技术与AI绘图教程

详情点击链接&#xff1a;GPT引领前沿与应用突破之GPT4科研实践技术与AI绘图教程 前沿 GPT对于每个科研人员已经成为不可或缺的辅助工具&#xff0c;不同的研究领域和项目具有不同的需求。 如在科研编程、绘图领域&#xff1a; 1、编程建议和示例代码: 无论你使用的编程语言是…

基于单片机的太阳能热水器控制器设计

一、项目介绍 随着环保意识的逐渐增强&#xff0c;太阳能热水器作为一种清洁能源应用得越来越广泛。然而&#xff0c;传统的太阳能热水器控制器通常采用机械式或电子式温控器&#xff0c;存在精度低、控制不稳定等问题。为了解决这些问题&#xff0c;本项目基于单片机技术设计…

Ansible之playbook详解和应用实例

目录 一、playbook简介 1.什么是playbook 2.playbook组成 二、应用实例 1.使用playbook安装启用httpd服务 2.使用playbook安装启用nginx服务 三、ansible-playbook其他用法 1.检查yaml文件的语法是否正确 2.检查tasks任务 3.检查指定的主机 4.指定从某个task开始运行…

mac下配置JDK环境

一、下载安装 下载地址&#xff1a;Java Downloads | Oracle&#xff0c;选择适用于Mac OS的JDK版本&#xff0c;点击下载即可。 下载完之后&#xff0c;直接安装&#xff1a; 安装过程非常简单&#xff0c;按“继续”按钮一直下一步即可。 二、配置环境变量 上一步骤&#x…

虚拟现实vr元宇宙井下危险隐患排查模拟实训稳固企业生产

数字化时代&#xff0c;职业教育正面临着前所未有的挑战和机遇&#xff0c;元宇宙的兴起&#xff0c;借助元宇宙平台进行钻井虚拟教学实验&#xff0c;基于元宇宙数字空间搭建更丰富、逼真、安全、灵活的实验环境&#xff0c;成为石油行业教育创新的催化剂。 一、降低实验成本 …

LinkedList(3):并发异常

1 LinkedList并发异常 package com.example.demo;import java.util.Iterator; import java.util.LinkedList;public class TestLinkedList {public static void main(String[] args) {LinkedList linkedList new LinkedList(); //双向链表linkedList.add(11);linkedList.add(…

Linux--进程概念

1.什么是程序&#xff1f;什么是进程&#xff1f;有什么区别&#xff1f; 程序&#xff1a;是静态的概念&#xff0c;gcc xxx.c -o pro 磁盘中生成的pro文件&#xff0c;叫做程序。 进程&#xff1a;是程序的一种与运行活动&#xff0c;通俗的意思是程序跑起来了&#xff0c;系…

OpenCV实现Photoshop曲线调整

《QT 插件化图像算法研究平台》有仿Photoshop曲线调整图像的功能&#xff0c;包括RGB曲线调整和HSV曲线调整。 Photoshop曲线调整原理&#xff1a;RGB、HSV各通道曲线&#xff0c;可以理解为一个值映射&#xff08;值转换&#xff09;函数。X轴是输入&#xff0c;Y轴是输出。x0…