当前位置: 首页 > news >正文

SpringBoot 注解开发

利用自定义注解,解决问题

例1 自定义注解限制请求

场景:前端发起的频繁的请求,导致服务器压力过大。需要对后端接口进行限流处理,每个接口都需要做限流处理的话就会导致代码冗余,此时就可以利用注解进行解决

非注解形式

这里是用于 某个用户点击的次数(结合radis)进行判断,也可以用请求的ip进行判断

核心代码:

        //限流,60秒5次ValueOperations valueOperations = redisTemplate.opsForValue();String uri = request.getRequestURI();//captcha = "0"; 这个用于测试Integer count =(Integer) valueOperations.get(uri+":"+user.getId());if(count == null){valueOperations.set(uri+":"+user.getId(),1,60,TimeUnit.SECONDS);} else if (count<5) {valueOperations.increment(uri+":"+user.getId());}else {return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REAHCED);}

完整代码:

    @RequestMapping(value = "/path",method = RequestMethod.GET)@ResponseBodypublic RespBean getPath(User user,Long goodsId,String captcha,HttpServletRequest request){if(user == null){return RespBean.error(RespBeanEnum.SESSION_ERROR);}//限流,60秒5次ValueOperations valueOperations = redisTemplate.opsForValue();String uri = request.getRequestURI();log.info("uri====:{}",uri);log.info("qequestIp===={}",request.getRemoteAddr());log.info("LocalIp===={}",request.getLocalAddr());//captcha = "0";Integer count =(Integer) valueOperations.get(uri+":"+user.getId());if(count == null){valueOperations.set(uri+":"+user.getId(),1,60,TimeUnit.SECONDS);} else if (count<5) {valueOperations.increment(uri+":"+user.getId());}else {return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REAHCED);}boolean check= orderService.checkCaptcha(user,goodsId,captcha);if(!check){return RespBean.error(RespBeanEnum.ERROR_CAPTCHA);}String str = orderService.createPath(user,goodsId);return RespBean.success(str);}

注解形式

注解定义方式,以AcessLimit为例

  1. 定义注解  AccessLimit

  2. 创建拦截器 AccessLimitInterceptor

  3. 在WebConfig 中添加拦截器 addInterceptors

定义注解AccessLimit
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {//时间int second();//次数int maxCount();//这个用于判断是否需要登录,后续可用SpringSecurity + JWT 实现。boolean needLogin() default true;
}
创建拦截器 AccessLimitInterceptor

核心代码:

@Component
public class AccessLimitInterceptor implements HandlerInterceptor {@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if(handler instanceof HandlerMethod){HandlerMethod hm = (HandlerMethod) handler;AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);if( accessLimit ==null){return true;}int second = accessLimit.second();int maxCount = accessLimit.maxCount();boolean needLogin = accessLimit.needLogin();String key = request.getRequestURI();ValueOperations valueOperations =redisTemplate.opsForValue();Integer count = (Integer) valueOperations.get(key);if(count == null){valueOperations.set(key,1,second, TimeUnit.SECONDS);} else if (count<maxCount) {valueOperations.increment(key);} else {render(response,RespBeanEnum.ACCESS_LIMIT_REAHCED);return false;}}return true;}

完整代码:

package com.xxx.seckilldemo.annotation;import com.fasterxml.jackson.databind.ObjectMapper;
import com.xxx.seckilldemo.config.UserContext;
import com.xxx.seckilldemo.pojo.User;
import com.xxx.seckilldemo.service.IUserService;
import com.xxx.seckilldemo.utils.CookieUtil;
import com.xxx.seckilldemo.vo.RespBean;
import com.xxx.seckilldemo.vo.RespBeanEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;/** 接口限流拦截器* @author Adzc* @date 2023/11/15 15:40*/
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {@Autowiredprivate IUserService userService;@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if(handler instanceof HandlerMethod){User user = getUser(request,response);UserContext.setUser(user);HandlerMethod hm = (HandlerMethod) handler;AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);if( accessLimit ==null){return true;}int second = accessLimit.second();int maxCount = accessLimit.maxCount();//此处是登录相关boolean needLogin = accessLimit.needLogin();String key = request.getRequestURI();if(needLogin){if (user == null){render(response, RespBeanEnum.SESSION_ERROR);return false;}key+=":"+user.getId();}//在有效时间内,次数的判断ValueOperations valueOperations =redisTemplate.opsForValue();Integer count = (Integer) valueOperations.get(key);if(count == null){valueOperations.set(key,1,second, TimeUnit.SECONDS);} else if (count<maxCount) {valueOperations.increment(key);} else {//返回对象的定义render(response,RespBeanEnum.ACCESS_LIMIT_REAHCED);return false;}}return true;}/*** 获取当前用户* @param request* @param response* @return*/private User getUser(HttpServletRequest request,HttpServletResponse response){String ticket = CookieUtil.getCookieValue(request,"userTicket");if (StringUtils.isEmpty(ticket)){System.out.println("没有获取到用户");return null;}return userService.getUserByCookie(ticket,request,response);}/*** 构建返回对象* @param response* @param respBeanEnum*/private void render(HttpServletResponse response,RespBeanEnum respBeanEnum) throws IOException {response.setContentType("application/json");response.setCharacterEncoding("utf-8");PrintWriter out = response.getWriter();RespBean respBean = RespBean.error(respBeanEnum);out.write(new ObjectMapper().writeValueAsString(respBean));out.flush();out.close();}
}

返回对象用到了枚举

/*** 公共返回对象* @author Adzc* @date 2023/9/25 23:19*/@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class RespBean {private long code;private String message;private Object obj;/*** 成功返回结果* @return*/public static RespBean success(){return new RespBean(RespBeanEnum.SUCCESS.getCode(), RespBeanEnum.SUCCESS.getMessage(),null);}public static RespBean success(Object obj){return new RespBean(RespBeanEnum.SUCCESS.getCode(), RespBeanEnum.SUCCESS.getMessage(),obj);}/*** 失败返回结果* @param respBeanEnum* @return*/public static RespBean error(RespBeanEnum respBeanEnum){return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(),null);}public static RespBean error(RespBeanEnum respBeanEnum,Object obj){return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(),obj);}
}
@Getter
@ToString
@AllArgsConstructor
public enum RespBeanEnum {SUCCESS(200,"SUCCESS"),ERROR(500,"服务端异常"),//登录模块LOGIN_ERROR(500210,"用户名或密码不正确"),MOBILE_ERROR(500211,"非法手机号"),BIND_ERROR(500212,"参数校验异常"),MOBILE_NOT_EXIST(500213, "手机号不存在"),PASSWORD_UPDATE_FAIL(500214,"密码更新失败"),SESSION_ERROR(500215,"用户不存在"),ACCESS_LIMIT_REAHCED(500504,"访问过于频繁请稍后在尝试");private final Integer code;private final String message;
}
在WebConfig 中添加拦截器 addInterceptors

核心代码

@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(accessLimitInterceptor);}}

完整代码

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate AccessLimitInterceptor accessLimitInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(accessLimitInterceptor);}
}

例2 利用@JsonSerialize注解+自定义类进行手机号脱敏处理

场景:很多手机号之类的信息需要在前端页面需要进行脱敏处理

public class PhoneJsonSerializer extends JsonSerializer {@Overridepublic void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {if (value != null && value instanceof String){if (!Objects.equals(value,"") && value.toString().length() > 5){String phone = value.toString();phone = phone.substring(0, 3) + "******" + phone.substring(phone.length() - 2);gen.writeString(phone);} else {gen.writeObject(value);}} else {gen.writeObject(value);}}}

在返回的对象中使用

@JsonSerialize(using = PhoneJsonSerializer.class)private String phone;

相关文章:

SpringBoot 注解开发

利用自定义注解&#xff0c;解决问题 例1 自定义注解限制请求 场景&#xff1a;前端发起的频繁的请求&#xff0c;导致服务器压力过大。需要对后端接口进行限流处理&#xff0c;每个接口都需要做限流处理的话就会导致代码冗余&#xff0c;此时就可以利用注解进行解决 非注解形…...

使用持久卷部署 WordPress 和 MySQL

&#x1f5d3;️实验环境 OS名称Microsoft Windows 11 家庭中文版系统类型x64-based PCDocker版本Docker version 24.0.6, build ed223bcminikube版本v1.32.0 &#x1f587;️创建 kustomization.yaml 你可以通过 kustomization.yaml 中的生成器创建一个 Secret存储密码或密…...

2024年csdn最新最全的Postman接口测试: postman实现参数化

什么时候会用到参数化 比如&#xff1a;一个模块要用多组不同数据进行测试 验证业务的正确性 Login模块&#xff1a;正确的用户名&#xff0c;密码 成功&#xff1b;错误的用户名&#xff0c;正确的密码 失败 postman实现参数化 在实际的接口测试中&#xff0c;部分参数…...

开发知识点-uniapp微信小程序-开发指南

uniapp Vue的原型链生命周期函数onLoaduni.chooseLocationgetCurrentPages美团外卖微信小程序开发uniapp-美团外卖微信小程序开发P1 成果展示P2外卖小程序后端&#xff0c;学习给小程序写http接口P3 主界面配置P4 首页组件拆分P13 外卖列表布局筛选组件商家 布局测试数据创建样…...

Vue3+Vite实现工程化,事件绑定以及修饰符

我们可以使用v-on来监听DOM事件&#xff0c;并在事件触发时执行对应的Vue的Javascript代码。 用法&#xff1a;v-on:click "handler" 或简写为 click "handler"vue中的事件名原生事件名去掉 on 前缀 如:onClick --> clickhandler的值可以是方法事件…...

20、动态路由_下滑线为前缀的目录

创建文件 pages_question\index.vue pages_question\detail.vue 生成的对应路由&#xff1a; const _6bf6ece8 () > interopDefault(import(..\\pages\\_question\\index.vue /* webpackChunkName: "pages/_question/index" */)) const _a98c80aa () > in…...

中间件安全: Apache 远程代码执行 (CVE-2021-42013)

中间件安全&#xff1a; Apache 远程代码执行 &#xff08;CVE-2021-42013&#xff09; Apache HTTP Server是美国阿帕奇&#xff08;Apache&#xff09;基金会的一款开源网页服务器。该服务器具有快速、可靠且可通过简单的API进行扩充的特点&#xff0c;发现 Apache HTTP Ser…...

YOLOv8优化与量化(1000+ FPS性能)

YOLO家族又添新成员了&#xff01;作为目标检测领域著名的模型家族&#xff0c;you only look once (YOLO) 推 出新模型的速度可谓是越来越快。就在刚刚过去的1月份&#xff0c;YOLO又推出了最新的YOLOv8模型&#xff0c;其模型结构和架构上的创新以及所提供的性能提升&#xf…...

python urllib open 头部信息错误

header 有些字符在 lighttpd server 中无法正常解析,需要转换 quteo 可以转换 就跨平台而言,Rust 和 python 一样优秀,看了在stm32 上使用 Rust 进行编程,从一定程度上,而言&#xff0c;稳定和安全性要比C 开发的好的多,说出来可能不信&#xff0c;在单片机上是可以对空指针进行…...

nn.KLDivLoss,nn.CrossEntropyLoss,nn.MSELoss,Focal_Loss

KL loss&#xff1a;https://blog.csdn.net/qq_50001789/article/details/128974654 https://pytorch.org/docs/stable/nn.html 1. nn.L1Loss 1.1 公式 L1Loss: 计算预测 x和 目标y之间的平均绝对值误差MAE, 即L1损失&#xff1a; l o s s 1 n ∑ i 1 , . . . n ∣ x i…...

HTTP Error 500.31 - Failed to load ASP.NET Core runtime

在winserver服务器上部署net6应用后&#xff0c;访问接口得到以下提示&#xff1a; 原因是因为没有安装net6的运行时和环境&#xff0c;我们可以在windows自带的 “事件查看器” 查看原因。 可以直接根据给出的地址去官网下载sdk环境&#xff0c;安装即可 下载对应的net版本…...

2023.11.17 关于 Spring Boot 日志文件

目录 日志文件作用 常见的日志框架说明 门面模式 日志的使用 日志的级别 六种级别 日志级别的设置 日志的持久化 使用 Lombok 输出日志 实现原理 普通打印和日志的区别 日志文件作用 记录 错误日志 和 警告日志&#xff08;发现和定位问题&#xff09;记录 用户登录…...

【框架整合】Redis限流方案

1、Redis实现限流方案的核心原理&#xff1a; redis实现限流的核心原理在于redis 的key 过期时间&#xff0c;当我们设置一个key到redis中时&#xff0c;会将key设置上过期时间&#xff0c;这里的实现是采用lua脚本来实现原子性的。2、准备 引入相关依赖 <dependency>…...

NSS [鹤城杯 2021]Middle magic

NSS [鹤城杯 2021]Middle magic 源码直接给了。 粗略一看&#xff0c;一共三个关卡 先看第一关&#xff1a; if(isset($_GET[aaa]) && strlen($_GET[aaa]) < 20){$aaa preg_replace(/^(.*)level(.*)$/, ${1}<!-- filtered -->${2}, $_GET[aaa]);if(preg_m…...

Sqlite安装配置及使用

一、下载SQLite Sqlite官网 我下载的是3370000版本:sqlite-dll-win64-x64-3370000.zip 和 sqlite-tools-win32-x86-3370000.zip 二、解压下载的两个压缩包 三、配置环境 四、检查是否安装配置成功 winR&#xff1a;输入cmd调出命令窗口&#xff0c;输入sqlite3后回车查看s…...

参数估计(一)(点估计)

文章目录 点估计和估计量的求法点估计概念矩估计法极大似然估计法 参考文献 参数估计是数理统计中重要的基本问题之一。通常&#xff0c;称参数的可容许值的全体为参数空间&#xff0c;并记为 Θ \Theta Θ。所谓参数估计就是由样本对总体分布所含的未知参数做出估计。另外&am…...

kubenetes-服务发现和负载均衡

一、服务发布 kubenetes把服务发布至集群内部或者外部&#xff0c;服务的三种不同类型&#xff1a; ClusterlPNodePortLoadBalancer ClusterIP是发布至集群内部的一个虚拟IP,通过负载均衡技术转发到不同的pod中。 NodePort解决的是集群外部访问的问题&#xff0c;用户可能不…...

docker的基本使用以及使用Docker 运行D435i

1.一些基本的指令 1.1 容器 要查看正在运行的容器&#xff1a; sudo docker ps 查看所有的容器&#xff08;包括停止状态的容器&#xff09; sudo docker ps -a 重新命名容器 sudo docker rename <old_name> <new_name> <old_name> 替换为你的容器名称…...

如何看待人工智能行业发展

随着人工智能技术的飞速发展&#xff0c;这个领域的就业前景也日益广阔。人工智能在各行各业都有广泛的应用&#xff0c;包括医疗、金融、制造业、教育等。因此&#xff0c;对于想要追求高薪、高技能职业的人来说&#xff0c;学习人工智能是一个非常有前景的选择。 首先&#x…...

linux中实现自己的bash

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C &#x1f525;座右铭&#xff1a;“不要等到什么都没有了&#xff0c;才下…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...