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

SpringBoot結(jié)合Redis實現(xiàn)接口冪等性的示例代碼

 更新時間:2022年06月23日 10:44:00   作者:南方葵籽  
本文主要介紹了SpringBoot結(jié)合Redis實現(xiàn)接口冪等性的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

介紹

冪等性的概念是,任意多次執(zhí)行所產(chǎn)生的影響都與一次執(zhí)行產(chǎn)生的影響相同,按照這個含義,最終的解釋是對數(shù)據(jù)庫的影響只能是一次性的,不能重復處理。手段如下

  • 數(shù)據(jù)庫建立唯一索引
  • token機制
  • 悲觀鎖或者是樂觀鎖
  • 先查詢后判斷

小小主要帶你們介紹Redis實現(xiàn)自動冪等性。其原理如下圖所示。

實現(xiàn)過程

引入 maven 依賴

     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

spring 配置文件寫入

server.port=8080
core.datasource.druid.enabled=true
core.datasource.druid.url=jdbc:mysql://192.168.1.225:3306/?useUnicode=true&characterEncoding=UTF-8
core.datasource.druid.username=root
core.datasource.druid.password=
core.redis.enabled=true
spring.redis.host=192.168.1.225 #本機的redis地址
spring.redis.port=16379
spring.redis.database=3
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.max-wait=5s
spring.redis.jedis.pool.min-idle=10

引入 Redis

引入 Spring boot 中的redis相關(guān)的stater,后面需要用到 Spring Boot 封裝好的 RedisTemplate

package cn.smallmartial.demo.utils;
 
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 java.io.Serializable;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
 
/**
 * @Author smallmartial
 * @Date 2020/4/16
 * @Email smallmarital@qq.com
 */
@Component
public class RedisUtil {
 
    @Autowired
    private RedisTemplate redisTemplate;
 
    /**
     * 寫入緩存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
 
    /**
     * 寫入緩存設置時間
     *
     * @param key
     * @param value
     * @param expireTime
     * @return
     */
    public boolean setEx(final String key, Object value, long expireTime) {
        boolean result = false;
        try {
            ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
 
    /**
     * 讀取緩存
     *
     * @param key
     * @return
     */
    public Object get(final String key) {
        Object result = null;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }
 
    /**
     * 刪除對應的value
     *
     * @param key
     */
    public boolean remove(final String key) {
        if (exists(key)) {
            Boolean delete = redisTemplate.delete(key);
            return delete;
        }
        return false;
 
    }
 
    /**
     * 判斷key是否存在
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        boolean result = false;
        ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
        if (Objects.nonNull(operations.get(key))) {
            result = true;
        }
        return result;
    }
 
 
}

自定義注解

自定義一個注解,定義此注解的目的是把它添加到需要實現(xiàn)冪等的方法上,只要某個方法注解了其,都會自動實現(xiàn)冪等操作。其代碼如下

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoIdempotent {
  
}

token 的創(chuàng)建和實現(xiàn)

token 服務接口,我們新建一個接口,創(chuàng)建token服務,里面主要是有兩個方法,一個用來創(chuàng)建 token,一個用來驗證token

public interface TokenService {
 
    /**
     * 創(chuàng)建token
     * @return
     */
    public  String createToken();
 
    /**
     * 檢驗token
     * @param request
     * @return
     */
    public boolean checkToken(HttpServletRequest request) throws Exception;
 
}

token 的實現(xiàn)類,token中引用了服務的實現(xiàn)類,token引用了 redis 服務,創(chuàng)建token采用隨機算法工具類生成隨機 uuid 字符串,然后放入 redis 中,如果放入成功,返回token,校驗方法就是從 header 中獲取 token 的值,如果不存在,直接跑出異常,這個異常信息可以被直接攔截到,返回給前端。

package cn.smallmartial.demo.service.impl;
 
import cn.smallmartial.demo.bean.RedisKeyPrefix;
import cn.smallmartial.demo.bean.ResponseCode;
import cn.smallmartial.demo.exception.ApiResult;
import cn.smallmartial.demo.exception.BusinessException;
import cn.smallmartial.demo.service.TokenService;
import cn.smallmartial.demo.utils.RedisUtil;
import io.netty.util.internal.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
 
import javax.servlet.http.HttpServletRequest;
import java.util.Random;
import java.util.UUID;
 
/**
 * @Author smallmartial
 * @Date 2020/4/16
 * @Email smallmarital@qq.com
 */
@Service
public class TokenServiceImpl implements TokenService {
    @Autowired
    private RedisUtil redisService;
 
    /**
     * 創(chuàng)建token
     *
     * @return
     */
    @Override
    public String createToken() {
        String str = UUID.randomUUID().toString().replace("-", "");
        StringBuilder token = new StringBuilder();
        try {
            token.append(RedisKeyPrefix.TOKEN_PREFIX).append(str);
            redisService.setEx(token.toString(), token.toString(), 10000L);
            boolean empty = StringUtils.isEmpty(token.toString());
            if (!empty) {
                return token.toString();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
 
    /**
     * 檢驗token
     *
     * @param request
     * @return
     */
    @Override
    public boolean checkToken(HttpServletRequest request) throws Exception {
 
        String token = request.getHeader(RedisKeyPrefix.TOKEN_NAME);
        if (StringUtils.isEmpty(token)) {// header中不存在token
            token = request.getParameter(RedisKeyPrefix.TOKEN_NAME);
            if (StringUtils.isEmpty(token)) {// parameter中也不存在token
                throw new BusinessException(ApiResult.BADARGUMENT);
            }
        }
 
        if (!redisService.exists(token)) {
            throw new BusinessException(ApiResult.REPETITIVE_OPERATION);
        }
 
        boolean remove = redisService.remove(token);
        if (!remove) {
            throw new BusinessException(ApiResult.REPETITIVE_OPERATION);
        }
        return true;
    }
}

攔截器的配置

用于攔截前端的 token,判斷前端的 token 是否有效

@Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
 
    @Bean
    public AuthInterceptor authInterceptor() {
        return new AuthInterceptor();
    }
 
    /**
     * 攔截器配置
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authInterceptor());
//                .addPathPatterns("/ksb/**")
//                .excludePathPatterns("/ksb/auth/**", "/api/common/**", "/error", "/api/*");
        super.addInterceptors(registry);
    }
 
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations(
                "classpath:/static/");
        registry.addResourceHandler("swagger-ui.html").addResourceLocations(
                "classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations(
                "classpath:/META-INF/resources/webjars/");
        super.addResourceHandlers(registry);
    }
 
 
}

攔截處理器:主要用于攔截掃描到 Autoldempotent 到注解方法,然后調(diào)用 tokenService 的 checkToken 方法校驗 token 是否正確,如果捕捉到異常就把異常信息渲染成 json 返回給前端。這部分代碼主要和自定義注解部分掛鉤。其主要代碼如下所示

@Slf4j
public class AuthInterceptor extends HandlerInterceptorAdapter {
 
    @Autowired
    private TokenService tokenService;
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        //被ApiIdempotment標記的掃描
        AutoIdempotent methodAnnotation = method.getAnnotation(AutoIdempotent.class);
        if (methodAnnotation != null) {
            try {
                return tokenService.checkToken(request);// 冪等性校驗, 校驗通過則放行, 校驗失敗則拋出異常, 并通過統(tǒng)一異常處理返回友好提示
            } catch (Exception ex) {
                throw new BusinessException(ApiResult.REPETITIVE_OPERATION);
            }
        }
        return true;
    }
 
}

測試用例

這里進行相關(guān)的測試用例 模擬業(yè)務請求類,通過相關(guān)的路徑獲得相關(guān)的token,然后調(diào)用 testidempotence 方法,這個方法注解了 @Autoldempotent,攔截器會攔截所有的請求,當判斷到處理的方法上面有該注解的時候,就會調(diào)用 TokenService 中的 checkToken() 方法,如果有異常會跑出,代碼如下所示

/**
 * @Author smallmartial
 * @Date 2020/4/16
 * @Email smallmarital@qq.com
 */
@RestController
public class BusinessController {
 
 
    @Autowired
    private TokenService tokenService;
 
    @GetMapping("/get/token")
    public Object  getToken(){
        String token = tokenService.createToken();
        return ResponseUtil.ok(token) ;
    }
 
 
    @AutoIdempotent
    @GetMapping("/test/Idempotence")
    public Object testIdempotence() {
        String token = "接口冪等性測試";
        return ResponseUtil.ok(token) ;
    }
}

用瀏覽器進行訪問

用獲取到的token第一次訪問

用獲取到的token再次訪問

可以看到,第二次訪問失敗,即,冪等性驗證通過。

到此這篇關(guān)于SpringBoot結(jié)合Redis實現(xiàn)接口冪等性的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot Redis接口冪等性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中JSqlParser的使用

    java中JSqlParser的使用

    JSqlParse是一款很精簡的sql解析工具,它可以將常用的sql文本解析成具有層級結(jié)構(gòu)的語法樹,本文主要介紹了java中JSqlParser的使用,具有一定的參考價值,感興趣的可以了解一下
    2024-07-07
  • MyBatis Map結(jié)果的Key轉(zhuǎn)為駝峰式

    MyBatis Map結(jié)果的Key轉(zhuǎn)為駝峰式

    今天小編就為大家分享一篇關(guān)于MyBatis Map結(jié)果的Key轉(zhuǎn)為駝峰式,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • Java多線程三種主要實現(xiàn)方式解析

    Java多線程三種主要實現(xiàn)方式解析

    這篇文章主要介紹了Java多線程三種主要實現(xiàn)方式解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • 解析Spring RestTemplate必須搭配MultiValueMap的理由

    解析Spring RestTemplate必須搭配MultiValueMap的理由

    本文給大家介紹Spring RestTemplate必須搭配MultiValueMap的理由,本文通過實例圖文相結(jié)合給大家介紹的非常詳細,需要的朋友參考下吧
    2021-11-11
  • 一文帶你秒懂Java為什么只有值傳遞

    一文帶你秒懂Java為什么只有值傳遞

    值傳遞是指在調(diào)用函數(shù)時將實際參數(shù)復制一份傳遞到函數(shù)中,引用傳遞是指在調(diào)用函數(shù)時將實際參數(shù)的引用直接傳遞到函數(shù)中,本文將詳細介紹Java中值傳遞的相關(guān)知識,感興趣的可以了解下
    2024-11-11
  • Java SpringBoot微服務框架驗證碼報錯問題解決方案

    Java SpringBoot微服務框架驗證碼報錯問題解決方案

    這篇文章主要介紹了Java SpringBoot微服務框架驗證碼報錯問題解決方案,包括dockerfile容器操作和完整dockerfile,本文給大家介紹的非常詳細,需要的朋友可以參考下
    2024-08-08
  • java 學習筆記(入門篇)_程序流程控制結(jié)構(gòu)和方法

    java 學習筆記(入門篇)_程序流程控制結(jié)構(gòu)和方法

    程序流程控制結(jié)構(gòu)分為:順序、選擇、循環(huán)以及異常處理結(jié)構(gòu),語句是程序的基本組成單位,一般來說語句的執(zhí)行流程是按順序來進行的,但是當遇到一些特殊的條件,比如循環(huán),這時候語句就會按照流程控制結(jié)構(gòu)來進行了
    2013-01-01
  • jmeter接口測試教程及接口測試流程詳解(全網(wǎng)僅有)

    jmeter接口測試教程及接口測試流程詳解(全網(wǎng)僅有)

    Jmeter是由Apache公司開發(fā)的一個純Java的開源項目,即可以用于做接口測試也可以用于做性能測試。本文給大家分享jmeter接口測試教程及接口測試流程,感興趣的朋友跟隨小編一起看看吧
    2021-12-12
  • Java多線程并發(fā)編程(互斥鎖Reentrant Lock)

    Java多線程并發(fā)編程(互斥鎖Reentrant Lock)

    這篇文章主要介紹了ReentrantLock 互斥鎖,在同一時間只能被一個線程所占有,在被持有后并未釋放之前,其他線程若想獲得該鎖只能等待或放棄,需要的朋友可以參考下
    2017-05-05
  • Dubbo異步調(diào)用的實現(xiàn)介紹

    Dubbo異步調(diào)用的實現(xiàn)介紹

    dubbo默認使用同步的方式調(diào)用。但在有些特殊的場景下,我們可能希望異步調(diào)用dubbo接口,從而避免不必要的等待時間,這時候我們就需要用到異步。那么dubbo的異步是如何實現(xiàn)的呢?下面就來看看這個問題
    2022-09-09

最新評論