最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

攔截器

什么是攔截器

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

如何自定義攔截器

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

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

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

想要在Spring Boot生效其實(shí)很簡單,只需要定義一個(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ù)請求

需求

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

思路

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

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

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

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

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

實(shí)現(xiàn)

Docker啟動一個(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來創(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ù)請求的攔截器
 *
 * @Component:該注解將其注入到IOC容器中
 */
@Slf4j
@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {

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

    /**
     * preHandler方法,在controller方法之前執(zhí)行
     * <p>
     * 判斷條件僅僅是用了uri,實(shí)際開發(fā)中根據(jù)實(shí)際情況組合一個(gè)唯一識別的條件。
     */
    @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);
            // 沒有限制重復(fù)提交,直接跳過
            if (Objects.isNull(repeatSubmitByMethod) && Objects.isNull(repeatSubmitByCls)) {
                log.info("isNull");
                return true;
            }

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

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

            //如果存在,表示已經(jīng)請求過了,直接拋出異常,由全局異常進(jìn)行處理返回指定信息
            if (ifAbsent != null && !ifAbsent) {
                String msg = String.format("url:[%s]重復(fù)請求", 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);
    }
}

寫個(gè)測試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");
    }
}

測試

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

相關(guān)文章

  • Java代碼規(guī)范與質(zhì)量檢測插件SonarLint的使用

    Java代碼規(guī)范與質(zhì)量檢測插件SonarLint的使用

    本文主要介紹了Java代碼規(guī)范與質(zhì)量檢測插件SonarLint的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • 淺析java修飾符訪問權(quán)限(動力節(jié)點(diǎn)Java學(xué)院整理)

    淺析java修飾符訪問權(quán)限(動力節(jié)點(diǎn)Java學(xué)院整理)

    Java有四種訪問權(quán)限,其中三種有訪問權(quán)限修飾符,分別為private,public和protected,還有一種不帶任何修飾符,下面通過本文給大家簡單介紹下java修飾符訪問權(quán)限相關(guān)知識,感興趣的朋友一起學(xué)習(xí)吧
    2017-04-04
  • SpringBoot整合tkMapper的方法

    SpringBoot整合tkMapper的方法

    項(xiàng)目使用SpringBoot2.0,H2數(shù)據(jù)庫,使用了?Lombok?簡化代碼,下面是本人使用SpringBoot整合tkMapper的一個(gè)小demo,記錄下來本人在此處踩得坑
    2022-11-11
  • Ubuntu搭建Java開發(fā)環(huán)境筆記

    Ubuntu搭建Java開發(fā)環(huán)境筆記

    這篇文章主要介紹了Ubuntu搭建Java開發(fā)環(huán)境筆記,本文講解了JDK安裝、MyEclipse安裝及第一個(gè)Hello world程序等內(nèi)容,需要的朋友可以參考下
    2015-03-03
  • 使用Maven進(jìn)行依賴排除的詳細(xì)步驟

    使用Maven進(jìn)行依賴排除的詳細(xì)步驟

    在Maven中,依賴排除是一種常見的技術(shù),用于從項(xiàng)目的依賴中排除特定的傳遞性依賴,這通常用于解決依賴沖突或避免引入不需要的庫,以下是如何在Maven中使用依賴排除的詳細(xì)步驟,包括代碼示例,需要的朋友可以參考下
    2024-11-11
  • MybatisPlus實(shí)現(xiàn)數(shù)據(jù)權(quán)限隔離的示例詳解

    MybatisPlus實(shí)現(xiàn)數(shù)據(jù)權(quán)限隔離的示例詳解

    Mybatis Plus對Mybatis做了無侵入的增強(qiáng),非常的好用,今天就給大家介紹它的其中一個(gè)實(shí)用功能:數(shù)據(jù)權(quán)限插件,感興趣的可以跟隨小編一起了解下
    2024-04-04
  • 舉例講解Java的Spring框架中AOP程序設(shè)計(jì)方式的使用

    舉例講解Java的Spring框架中AOP程序設(shè)計(jì)方式的使用

    這篇文章主要介紹了Java的Spring框架中AOP程序設(shè)計(jì)方式的使用講解,文中舉的AOP下拋出異常的例子非常實(shí)用,需要的朋友可以參考下
    2016-04-04
  • JAVA定義變量與輸出詳解

    JAVA定義變量與輸出詳解

    這篇文章主要介紹了JAVA定義變量與輸出詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Kotlin 基礎(chǔ)教程之?dāng)?shù)組容器

    Kotlin 基礎(chǔ)教程之?dāng)?shù)組容器

    這篇文章主要介紹了Kotlin 基礎(chǔ)教程之?dāng)?shù)組容器的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • java基于JSON實(shí)現(xiàn)前后端交互(附代碼)

    java基于JSON實(shí)現(xiàn)前后端交互(附代碼)

    本文主要介紹了java基于JSON實(shí)現(xiàn)前后端交互,通過實(shí)際代碼示例展示了如何前后端JSON交互,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01

最新評論

梁山县| 阿图什市| 昭通市| 察雅县| 镇平县| 临沭县| 丹巴县| 额尔古纳市| 澄迈县| 徐汇区| 来宾市| 尤溪县| 民乐县| 报价| 策勒县| 临颍县| 泰和县| 利辛县| 西华县| 武宁县| 菏泽市| 安宁市| 鹤壁市| 耿马| 泗洪县| 屯留县| 丰镇市| 永定县| 平顺县| 开江县| 南昌县| 通州市| 华宁县| 饶河县| 囊谦县| 八宿县| 黑龙江省| 丰台区| 镇江市| 张北县| 青浦区|