欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot利用攔截器實(shí)現(xiàn)避免重復(fù)請(qǐng)求

 更新時(shí)間:2022年11月22日 09:52:39   作者:iicode  
Spring MVC中的攔截器(Interceptor)類似于Servlet中的過(guò)濾器(Filter),它主要用于攔截用戶請(qǐng)求并作相應(yīng)的處理。本文就將利用攔截器實(shí)現(xiàn)避免重復(fù)請(qǐng)求,感興趣的小伙伴可以了解一下

攔截器

什么是攔截器

Spring MVC中的攔截器(Interceptor)類似于Servlet中的過(guò)濾器(Filter),它主要用于攔截用戶請(qǐng)求并作相應(yīng)的處理。例如通過(guò)攔截器可以進(jìn)行權(quán)限驗(yàn)證、記錄請(qǐng)求信息的日志、判斷用戶是否登錄等。

如何自定義攔截器

自定義一個(gè)攔截器非常簡(jiǎn)單,只需要實(shí)現(xiàn)HandlerInterceptor這個(gè)接口即可,這個(gè)接口有三個(gè)可實(shí)現(xiàn)的方法

  • preHandle()方法:該方法會(huì)在控制器方法前執(zhí)行,其返回值表示是否知道如何寫(xiě)一個(gè)接口。中斷后續(xù)操作。當(dāng)其返回值為true時(shí),表示繼續(xù)向下執(zhí)行;當(dāng)其返回值為false時(shí),會(huì)中斷后續(xù)的所有操作(包括調(diào)用下一個(gè)攔截器和控制器類中的方法執(zhí)行等)。
  • postHandle()方法:該方法會(huì)在控制器方法調(diào)用之后,且解析視圖之前執(zhí)行??梢酝ㄟ^(guò)此方法對(duì)請(qǐng)求域中的模型和視圖做出進(jìn)一步的修改。
  • afterCompletion()方法:該方法會(huì)在整個(gè)請(qǐng)求完成,即視圖渲染結(jié)束之后執(zhí)行??梢酝ㄟ^(guò)此方法實(shí)現(xiàn)一些資源清理、記錄日志信息等工作。

如何讓攔截器在Spring Boot中生效

想要在Spring Boot生效其實(shí)很簡(jiǎn)單,只需要定義一個(gè)配置類,實(shí)現(xiàn)WebMvcConfigurer這個(gè)接口,并且實(shí)現(xiàn)其中的addInterceptors()方法即可,代碼如下:

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private XXX xxx;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 不攔截的uri
        final String[] commonExclude = {}};
        registry.addInterceptor(xxx).excludePathPatterns(commonExclude);
    }
}

用攔截器規(guī)避重復(fù)請(qǐng)求

需求

開(kāi)發(fā)中可能會(huì)經(jīng)常遇到短時(shí)間內(nèi)由于用戶的重復(fù)點(diǎn)擊導(dǎo)致幾秒之內(nèi)重復(fù)的請(qǐng)求,可能就是在這幾秒之內(nèi)由于各種問(wèn)題,比如網(wǎng)絡(luò),事務(wù)的隔離性等等問(wèn)題導(dǎo)致了數(shù)據(jù)的重復(fù)等問(wèn)題,因此在日常開(kāi)發(fā)中必須規(guī)避這類的重復(fù)請(qǐng)求操作,今天就用攔截器簡(jiǎn)單的處理一下這個(gè)問(wèn)題。

思路

在接口執(zhí)行之前先對(duì)指定接口(比如標(biāo)注某個(gè)注解的接口)進(jìn)行判斷,如果在指定的時(shí)間內(nèi)(比如5秒)已經(jīng)請(qǐng)求過(guò)一次了,則返回重復(fù)提交的信息給調(diào)用者。

根據(jù)什么判斷這個(gè)接口已經(jīng)請(qǐng)求了?

根據(jù)項(xiàng)目的架構(gòu)可能判斷的條件也是不同的,比如IP地址,用戶唯一標(biāo)識(shí)、請(qǐng)求參數(shù)、請(qǐng)求URI等等其中的某一個(gè)或者多個(gè)的組合。

這個(gè)具體的信息存放在哪?

由于是短時(shí)間內(nèi)甚至是瞬間并且要保證定時(shí)失效,肯定不能存在事務(wù)性數(shù)據(jù)庫(kù)中了,因此常用的幾種數(shù)據(jù)庫(kù)中只有Redis比較合適了。

實(shí)現(xiàn)

Docker啟動(dòng)一個(gè)Redis

docker pull redis:7.0.4

docker run -itd \
    --name redis \
    -p 6379:6379 \
    redis:7.0.4

創(chuàng)建一個(gè)Spring Boot項(xiàng)目

使用idea的Spring Initializr來(lái)創(chuàng)建一個(gè)Spring Boot項(xiàng)目,如下圖:

添加依賴

pom.xml文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>springboot_06</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_06</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--spring redis配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <!-- 1.5的版本默認(rèn)采用的連接池技術(shù)是jedis  2.0以上版本默認(rèn)連接池是lettuce, 在這里采用jedis,所以需要排除lettuce的jar -->
            <exclusions>
                <exclusion>
                    <groupId>redis.clients</groupId>
                    <artifactId>jedis</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置Redis

application.properties

spring.redis.host=127.0.0.1
spring.redis.database=1
spring.redis.port=6379

定義一個(gè)注解

package com.example.springboot_06.intercept;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {
    /**
     * 默認(rèn)失效時(shí)間5秒
     *
     * @return
     */
    long seconds() default 5;
}

創(chuàng)建一個(gè)攔截器

package com.example.springboot_06.intercept;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 重復(fù)請(qǐng)求的攔截器
 *
 * @Component:該注解將其注入到IOC容器中
 */
@Slf4j
@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {

    /**
     * Redis的API
     */
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * preHandler方法,在controller方法之前執(zhí)行
     * <p>
     * 判斷條件僅僅是用了uri,實(shí)際開(kāi)發(fā)中根據(jù)實(shí)際情況組合一個(gè)唯一識(shí)別的條件。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            // 只攔截標(biāo)注了@RepeatSubmit該注解
            HandlerMethod method = (HandlerMethod) handler;
            // 標(biāo)注在方法上的@RepeatSubmit
            RepeatSubmit repeatSubmitByMethod = AnnotationUtils.findAnnotation(method.getMethod(), RepeatSubmit.class);
            // 標(biāo)注在controler類上的@RepeatSubmit
            RepeatSubmit repeatSubmitByCls = AnnotationUtils.findAnnotation(method.getMethod().getDeclaringClass(), RepeatSubmit.class);
            // 沒(méi)有限制重復(fù)提交,直接跳過(guò)
            if (Objects.isNull(repeatSubmitByMethod) && Objects.isNull(repeatSubmitByCls)) {
                log.info("isNull");
                return true;
            }

            // todo: 組合判斷條件,這里僅僅是演示,實(shí)際項(xiàng)目中根據(jù)架構(gòu)組合條件
            //請(qǐng)求的URI
            String uri = request.getRequestURI();

            //存在即返回false,不存在即返回true
            Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(uri, "",
                    Objects.nonNull(repeatSubmitByMethod) ? repeatSubmitByMethod.seconds() : repeatSubmitByCls.seconds(), TimeUnit.SECONDS);

            //如果存在,表示已經(jīng)請(qǐng)求過(guò)了,直接拋出異常,由全局異常進(jìn)行處理返回指定信息
            if (ifAbsent != null && !ifAbsent) {
                String msg = String.format("url:[%s]重復(fù)請(qǐng)求", uri);
                log.warn(msg);
                // throw new RepeatSubmitException(msg);
                throw new Exception(msg);
            }
        }
        return true;
    }
}

配置攔截器

package com.example.springboot_06.config;
import com.example.springboot_06.intercept.RepeatSubmitInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private RepeatSubmitInterceptor repeatSubmitInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 不攔截的uri
        final String[] commonExclude = {"/error", "/files/**"};
        registry.addInterceptor(repeatSubmitInterceptor).excludePathPatterns(commonExclude);
    }
}

寫(xiě)個(gè)測(cè)試Controller

package com.example.springboot_06.controller;

import com.example.springboot_06.intercept.RepeatSubmit;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 標(biāo)注了@RepeatSubmit注解,全部的接口都需要攔截
 *
 */
@Slf4j
@RestController
@RequestMapping("/user")
@RepeatSubmit
public class UserController {

    @RequestMapping("/save")
    public ResponseEntity save() {
        log.info("/user/save");
        return ResponseEntity.ok("save success");
    }
}

測(cè)試

到此這篇關(guān)于SpringBoot利用攔截器實(shí)現(xiàn)避免重復(fù)請(qǐng)求的文章就介紹到這了,更多相關(guān)SpringBoot攔截器防重復(fù)請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談Java開(kāi)發(fā)中的安全編碼問(wèn)題

    淺談Java開(kāi)發(fā)中的安全編碼問(wèn)題

    這篇文章主要介紹了淺談Java開(kāi)發(fā)中的安全編碼問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-10-10
  • Mybatis-Plus中update()和updateById()將字段更新為null

    Mybatis-Plus中update()和updateById()將字段更新為null

    本文主要介紹了Mybatis-Plus中update()和updateById()將字段更新為null,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • Mybatis查詢返回Map<String,Object>類型的實(shí)現(xiàn)

    Mybatis查詢返回Map<String,Object>類型的實(shí)現(xiàn)

    本文主要介紹了Mybatis查詢返回Map<String,Object>類型的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Java如何修改JsonObject中的屬性值

    Java如何修改JsonObject中的屬性值

    這篇文章主要介紹了Java如何修改JsonObject中的屬性值問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • springboot結(jié)合mysql主從來(lái)實(shí)現(xiàn)讀寫(xiě)分離的方法示例

    springboot結(jié)合mysql主從來(lái)實(shí)現(xiàn)讀寫(xiě)分離的方法示例

    這篇文章主要介紹了springboot結(jié)合mysql主從來(lái)實(shí)現(xiàn)讀寫(xiě)分離的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Mybatis-plus多租戶項(xiàng)目實(shí)戰(zhàn)進(jìn)階指南

    Mybatis-plus多租戶項(xiàng)目實(shí)戰(zhàn)進(jìn)階指南

    多租戶是一種軟件架構(gòu)技術(shù),在多用戶的環(huán)境下共有同一套系統(tǒng),并且要注意數(shù)據(jù)之間的隔離性,下面這篇文章主要給大家介紹了關(guān)于Mybatis-plus多租戶項(xiàng)目實(shí)戰(zhàn)進(jìn)階的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • Java Quartz觸發(fā)器CronTriggerBean配置用法詳解

    Java Quartz觸發(fā)器CronTriggerBean配置用法詳解

    這篇文章主要介紹了Java Quartz觸發(fā)器CronTriggerBean配置用法詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Spring Boot通過(guò)Junit實(shí)現(xiàn)單元測(cè)試過(guò)程解析

    Spring Boot通過(guò)Junit實(shí)現(xiàn)單元測(cè)試過(guò)程解析

    這篇文章主要介紹了Spring Boot通過(guò)Junit實(shí)現(xiàn)單元測(cè)試過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 解析MapStruct轉(zhuǎn)換javaBean時(shí)出現(xiàn)的詭異事件

    解析MapStruct轉(zhuǎn)換javaBean時(shí)出現(xiàn)的詭異事件

    在項(xiàng)目中用到了MapStruct,對(duì)其可以轉(zhuǎn)換JavaBean特別好奇,今天小編給大家分享一個(gè)demo給大家講解MapStruct轉(zhuǎn)換javaBean時(shí)出現(xiàn)的詭異事件,感興趣的朋友一起看看吧
    2021-09-09
  • Spring三級(jí)緩存思想解決循環(huán)依賴總結(jié)分析

    Spring三級(jí)緩存思想解決循環(huán)依賴總結(jié)分析

    這篇文章主要為大家介紹了Spring三級(jí)緩存思想解決循環(huán)依賴總結(jié)分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08

最新評(píng)論