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)
    @ResponseBody
    public 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 {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public 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 {


    @Autowired
    private IUserService userService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public 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

核心代码


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

}

完整代码

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private AccessLimitInterceptor accessLimitInterceptor;


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

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

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

public class PhoneJsonSerializer extends JsonSerializer {
    @Override
    public 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;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入了解Java 8 新特性:Stream流的实践应用(二)

阅读建议 嗨&#xff0c;伙计&#xff01;刷到这篇文章咱们就是有缘人&#xff0c;在阅读这篇文章前我有一些建议&#xff1a; 本篇文章大概8000多字&#xff0c;预计阅读时间长需要10分钟&#xff08;不要害怕字数过多&#xff0c;其中有一大部分是示例代码&#xff0c;读起…

FFmpeg常用命令行讲解及实战一

文章目录 前言一、学习资料参考二、FFmpeg 选项1、主要选项①、主要命令选项②、举例 2、视频选项①、主要命令选项②、举例1&#xff09;提取固定帧2&#xff09;禁止输出视频3&#xff09;指定视频的纵横比 3、音频选项①、主要命令选项②、举例 4、字幕选项①、主要命令选项…

redis的高可用

1.redis的高可用 在集群当中有一个非常重要的指标&#xff0c;提供正常服务的时间的百分比&#xff08;365天&#xff09;99% redis的高可用含义更宽泛&#xff0c;正常服务是指标之一&#xff0c;数据容量的扩展&#xff0c;数据的安全性。 在redis中实现高可用技术&#x…

循环链表3

插入函数——插入数据&#xff0c;在链表plsit的pos位置插入val数据元素 位置pos&#xff08;在无特别说明的情况下&#xff09;是从0开始计数的 要改变链表结构&#xff0c;就要依赖前驱&#xff0c;每个前驱的next存储着下一个数据结点的地址&#xff0c;也就是依靠前驱的ne…

数据类型扩展02

1、字符串拓展 所有的字符本质还是数字。 char c1 a;char c2 中;System.out.println("c1:"c1);System.out.println("c1转换:"(int)c1);System.out.println("c2:"c2);System.out.println("c2转换:"(int)c2); 执行结果 c1:a c1转换:…

Java修仙记之记录一次与前端女修士论道的经历

文章开始之前&#xff0c;想跟我念一句&#xff1a;福生无量天尊&#xff0c;无量寿佛&#xff0c;阿弥陀佛 第一场论道&#xff1a;id更新之争 一个天气明朗的下午&#xff0c;前端的小美女长发姐告诉我&#xff1a;嘿&#xff0c;小后端&#xff0c;你的代码报错了 我答道&am…

3.6 Windows驱动开发:内核进程汇编与反汇编

在笔者上一篇文章《内核MDL读写进程内存》简单介绍了如何通过MDL映射的方式实现进程读写操作&#xff0c;本章将通过如上案例实现远程进程反汇编功能&#xff0c;此类功能也是ARK工具中最常见的功能之一&#xff0c;通常此类功能的实现分为两部分&#xff0c;内核部分只负责读写…

通过AppLink把拼多多热门榜单商品同步至小红书

上篇说到AppLink当中定时调度方式如何配置&#xff0c;这次来演示一下&#xff0c;如何把热门榜单信息同步至小红书 1.拉取一个定时器作为触发动作&#xff0c;通过配置定时器调度时间将定时策略配置为每天执行一次 2.触发动作完成后通过好单库获取拼多多每日热门榜单&#xf…

单元测试实战(四)MyBatis-Plus 的测试

为鼓励单元测试&#xff0c;特分门别类示例各种组件的测试代码并进行解说&#xff0c;供开发人员参考。 本文中的测试均基于JUnit5。 单元测试实战&#xff08;一&#xff09;Controller 的测试 单元测试实战&#xff08;二&#xff09;Service 的测试 单元测试实战&am…

人机交互复习专题

第一章概述 1.1人机交互的概念与理解 人机交互的概念与理解 人机交互是人与机器进行交互的操作方式&#xff0c;即用户与机器互相传递信息的媒介。好的人机交互界面美观且通俗易懂、操作简单有引导功能&#xff0c;使用户感受到愉快、有兴趣&#xff0c;从而提升使用效率。 美…

【阿里云】图像识别

一、阿里云官网资料及配置本地 二、配置环境变量 三、C语言调用阿里云Python接口 一、阿里云官网资料及配置本地 阿里云官网 垃圾识别分类 sudo apt install python3-pip pip3 install alibabacloud_imagerecog20190930可能出现的网络问题 二、配置环境变量 配置环境变量A…

猫罐头牌子哪个好一点?精选5款口碑好的猫罐头推荐!

猫罐头牌子哪个好一点&#xff1f;选择猫罐头是十分重要的事情&#xff0c;千万不能将就。因为&#xff0c;好的猫罐头不仅可以营养丰富&#xff0c;水分充足&#xff0c;适口性好&#xff0c;还能易吸收。而一旦选择错误&#xff0c;不仅无法达到上述效果&#xff0c;还可能产…

在Python中调用imageJ开发

文章目录 一、在ImageJ中进行Python开发二、在Python中调用imageJ开发2.1、简介2.2、环境配置2.3、测试一2.4、测试二 Python imageJ 解决方案&#xff0c;采坑记录 一、在ImageJ中进行Python开发 原生ImageJ仅支持JS脚本&#xff08;JAVAScript&#xff09;&#xff0c;而Im…

虾皮网同行数据丨虾皮数据工具-知虾:监控竞争对手数据的利器

在如今的电商竞争激烈的市场中&#xff0c;了解竞争对手的销售情况和策略对于制定自己的营销策略至关重要。虾皮网作为一家知名的电商平台&#xff0c;提供了一款强大的同行数据工具-知虾&#xff0c;可以帮助卖家监控竞争对手的数据&#xff0c;为自己的业务发展提供有力支持。…

请收藏!2023年全栈开发人员实战进阶指南终极版

全栈工程师在过去十年中越来越受到欢迎&#xff0c;而且在国内的就业环境下&#xff0c;它是更适合从技术转管理的职位。 但究竟什么是全栈工程师&#xff1f;他需要哪些技术能力&#xff1f;如何才能成为一名优秀的全栈工程师&#xff1f;今天这篇文章就给大家全方位分享一下…

去除IDEA中代码的波浪线(黄色警示线)

去除IDEA中代码的波浪线 首先是点击File—>Settings 操作如下图所示: 然后点击Editor—>Inspections—>General—>Duplicated code fragment(去掉勾选)—>Apply—>OK 即可,详情请看下图所示:

构建和应用卡尔曼滤波器 (KF)--扩展卡尔曼滤波器 (EKF)

作为一名数据科学家&#xff0c;我们偶尔会遇到需要对趋势进行建模以预测未来值的情况。虽然人们倾向于关注基于统计或机器学习的算法&#xff0c;但我在这里提出一个不同的选择&#xff1a;卡尔曼滤波器&#xff08;KF&#xff09;。 1960 年代初期&#xff0c;Rudolf E. Kal…

华为云cce中环境变量的使用

如上图&#xff0c;cce中的环境变量可配置。 配置后的这些参数怎么用呢&#xff1f; 我们可以在docker打包前在springboot的配置文件中配置&#xff0c;cce在启动的时候会调用环境变量中的设置。 如上图&#xff0c;配置的东西以key值标记&#xff0c;冒号后面的是默认配置项…

jenkins-2.426.1-1.1.noarch.rpm 的公钥没有安装

执行命令 yum install jenkins 报错 jenkins-2.426.1-1.1.noarch.rpm 的公钥没有安装 下载的软件包保存在缓存中&#xff0c;直到下次成功执行事务。 您可以通过执行 yum clean packages 删除软件包缓存。 错误&#xff1a;GPG 检查失败 解决办法&#xff1a; 1、安装新的公…

CentOS8部署Skywalking(非容器方式)

一、官网下载安装包 二、安装 #tar -zxf apache-skywalking-apm-9.6.0.tar.gz #mv apache-skywalking-apm-9.6.0 skywalking #cd /opt/skywalking 修改配置文件 #vi /opt/skywalking/config/application.yml #vi vi /opt/skywalking/webapp/application.yml 三、运行 ./bin…
最新文章