详解SpringBoot中如何使用布隆过滤器

目录
  • 前言
  • 一、Guava 实现布隆过滤器
  • 二、Hutool 布隆过滤器
  • 三、Redission 布隆过滤器
  • 四、小结
  • 五、Guava 布隆过滤器结合 Redis 使用

昨天写了一篇Redis布隆过滤器相关的命令的文章,今天来说一说springboot中如何简单在代码中使用布隆过滤器吧。

目前市面上也有好几种实现方式,如果你需要高度定制化,可以完全从零实现,当然这不是一个简单的工程。

如果只是想快速开始的话,那么市面上现成的实现,无疑是最快的。

前言

今天说到的实现方式有以下几种:

  • 引入 Guava 实现
  • 引入 hutool 实现
  • 引入 Redission 实现
  • Guava 布隆过滤器结合 Redis (重点)

项目工程的搭建,就在这里先写明啦~

boot项目就是四步走~ 导包->写配置->编写配置类->使用

补充说明:我使用的 redis 是用docker下载的一个集成redis和布隆过滤器的镜像。安装方式:Docker安装Redis布隆过滤器

如果你是在windows上安装的redis 是3.0版本的,是无法集成布隆过滤器。

如果是在liunx版本上的redis,需要再额外下载一个布隆过滤器的模块。需要自行百度啦~

我将要用到的所有jar都放在这里啦~

 <parent>
     <artifactId>spring-boot-dependencies</artifactId>
     <groupId>org.springframework.boot</groupId>
     <version>2.5.2</version>
 </parent>
 <dependencies>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-redis</artifactId>
     </dependency>
     <!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
     <dependency>
         <groupId>org.redisson</groupId>
         <artifactId>redisson-spring-boot-starter</artifactId>
         <version>3.17.6</version>
     </dependency>
 ​
     <dependency>
         <groupId>com.google.guava</groupId>
         <artifactId>guava</artifactId>
         <version>30.0-jre</version>
     </dependency>
     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
     </dependency>
     <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <scope>test</scope>
     </dependency>
     <dependency>
         <groupId>org.projectlombok</groupId>
         <artifactId>lombok</artifactId>
     </dependency>
     <dependency>
         <groupId>cn.hutool</groupId>
         <artifactId>hutool-all</artifactId>
         <version>5.7.22</version>
     </dependency>
 </dependencies>

yml 配置文件:

 server:
   port: 8081
 spring:
   redis:
     port: 6379
     host: 192.xxx

一、Guava 实现布隆过滤器

这个方式非常快捷:

直接用一个Demo来说明吧

     @Test
     public void test2() {
         // 预期插入数量
         long capacity = 10000L;
         // 错误比率
         double errorRate = 0.01;
         //创建BloomFilter对象,需要传入Funnel对象,预估的元素个数,错误率
         BloomFilter<Long> filter = BloomFilter.create(Funnels.longFunnel(), capacity, errorRate);
 //        BloomFilter<String> filter = BloomFilter.create(Funnels.stringFunnel(Charset.forName("utf-8")), 10000, 0.0001);
         //put值进去
         for (long i = 0; i < capacity; i++) {
             filter.put(i);
         }
         // 统计误判次数
         int count = 0;
         // 我在数据范围之外的数据,测试相同量的数据,判断错误率是不是符合我们当时设定的错误率
         for (long i = capacity; i < capacity * 2; i++) {
             if (filter.mightContain(i)) {
                 count++;
             }
         }
         System.out.println(count);
     }

当容量为1k,误判率为 0.01时

2022-08-26 23:50:01.028  INFO 14748 --- [           main] com.nzc.test.RedisBloomFilterTest        : 存入元素为==1000
 误判个数为==>10

当容量为1w,误判率为 0.01时

2022-08-26 23:49:23.618  INFO 21796 --- [           main] com.nzc.test.RedisBloomFilterTest        : 存入元素为==10000
 误判个数为==>87

当容量为100w,误判率为 0.01时

2022-08-26 23:50:45.167  INFO 8964 --- [           main] com.nzc.test.RedisBloomFilterTest        : 存入元素为==1000000
 误判个数为==>9946

BloomFilter<Long> filter = BloomFilter.create(Funnels.longFunnel(), capacity, errorRate);

create方法实际上调用的方法是:

 public static <T> BloomFilter<T> create(
     Funnel<? super T> funnel, int expectedInsertions, double fpp) {
   return create(funnel, (long) expectedInsertions, fpp);
 }
  • funnel 用来对参数做转化,方便生成hash值
  • expectedInsertions 预期插入的数据量大小
  • fpp 误判率

里面具体的实现,相对我来说,数学能力有限,没法说清楚。希望大家多多包含。

二、Hutool 布隆过滤器

Hutool 工具中的布隆过滤器,内存占用太高了,并且功能相比于guava也弱了很多,个人不建议使用。

 @Test
 public void test4(){
     int capacity = 100;
     // 错误比率
     double errorRate = 0.01;
     // 初始化
     BitMapBloomFilter filter = new BitMapBloomFilter(capacity);
     for (int i = 0; i < capacity; i++) {
         filter.add(String.valueOf(i));
     }
 ​
     log.info("存入元素为=={}",capacity);
     // 统计误判次数
     int count = 0;
     // 我在数据范围之外的数据,测试相同量的数据,判断错误率是不是符合我们当时设定的错误率
     for (int i = capacity; i < capacity * 2; i++) {
         if (filter.contains(String.valueOf(i))) {
             count++;
         }
     }
     log.info("误判元素为==={}",count);
 }

三、Redission 布隆过滤器

redission的使用其实也很简单,官方也有非常好的教程。

引入jar,然后编写一个config类即可

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter -->
 <dependency>
     <groupId>org.redisson</groupId>
     <artifactId>redisson-spring-boot-starter</artifactId>
     <version>3.17.6</version>
 </dependency>

出了注入 redissionclient,还注入了一些redis相关的东西,都是历史包裹~

 /**
  * @description:
  * @author: Yihui Wang
  * @date: 2022年08月26日 22:06
  */
 @Configuration
 @EnableCaching
 public class RedisConfig {
 ​
     @Bean
     public RedissonClient redissonClient(){
         Config config = new Config();
         config.useSingleServer().setAddress("redis://47.113.227.254:6379");
         RedissonClient redissonClient = Redisson.create(config);
         return  redissonClient;
     }
 ​
     @Bean
     public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
         RedisCacheManager rcm=RedisCacheManager.create(connectionFactory);
         return rcm;
     }
     @Bean
     public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
         RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
         redisTemplate.setConnectionFactory(factory);

         Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
                 Jackson2JsonRedisSerializer(Object.class);
         ObjectMapper om = new ObjectMapper();
         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
         jackson2JsonRedisSerializer.setObjectMapper(om);
         //序列化设置 ,这样计算是正常显示的数据,也能正常存储和获取
         redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
         redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
         redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
         redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

         return redisTemplate;
     }
     @Bean
     public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
         StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
         stringRedisTemplate.setConnectionFactory(factory);
         return stringRedisTemplate;
     }
 }

我们在中间再编写一个Service,

 @Service
 public class BloomFilterService {
 ​
     @Autowired
     private RedissonClient redissonClient;
 ​
     /**
      * 创建布隆过滤器
      * @param filterName 布隆过滤器名称
      * @param capacity 预测插入数量
      * @param errorRate 误判率
      * @param <T>
      * @return
      */
     public <T> RBloomFilter<T> create(String filterName, long capacity, double errorRate) {
         RBloomFilter<T> bloomFilter = redissonClient.getBloomFilter(filterName);
         bloomFilter.tryInit(capacity, errorRate);
         return bloomFilter;
     }
 }

测试:

 package com.nzc.test;
 ​
 import com.nzc.WebApplication;
 import com.nzc.service.BloomFilterService;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.redisson.api.RBloomFilter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.junit4.SpringRunner;
 ​
 @Slf4j
 @RunWith(SpringRunner.class)
 @SpringBootTest(classes = WebApplication.class)
 public class BloomFilterTest {
 ​
     @Autowired
     private BloomFilterService bloomFilterService;
 ​
     @Test
     public void testBloomFilter() {
         // 预期插入数量
         long expectedInsertions = 1000L;
         // 错误比率
         double falseProbability = 0.01;
         RBloomFilter<Long> bloomFilter = bloomFilterService.create("NZC:BOOM-FILTER", expectedInsertions, falseProbability);
         // 布隆过滤器增加元素
         for (long i = 0; i < expectedInsertions; i++) {
             bloomFilter.add(i);
         }
         long elementCount = bloomFilter.count();
         log.info("布隆过滤器中含有元素个数 = {}.", elementCount);
 ​
         // 统计误判次数
         int count = 0;
         // 我在数据范围之外的数据,测试相同量的数据,判断错误率是不是符合我们当时设定的错误率
         for (long i = expectedInsertions; i < expectedInsertions * 2; i++) {
             if (bloomFilter.contains(i)) {
                 count++;
             }
         }
         log.info("误判次数 = {}.", count);
 ​
         // 清空布隆过滤器 内部实现是个异步线程在执行  我只是为了方便测试
         bloomFilter.delete();
     }
 }

当容量为1k,误判率为0.01时的输出情况

2022-08-26 23:37:04.903  INFO 1472 --- [           main] com.nzc.test.BloomFilterTest             : 布隆过滤器中含有元素个数 = 993.
 2022-08-26 23:37:38.549  INFO 1472 --- [           main] com.nzc.test.BloomFilterTest             : 误判次数 = 36.

当容量为1w,误判率为0.01时的输出情况

2022-08-26 23:50:54.478  INFO 17088 --- [           main] com.nzc.test.BloomFilterTest             : 布隆过滤器中含有元素个数 = 9895.
 2022-08-26 23:56:56.171  INFO 17088 --- [           main] com.nzc.test.BloomFilterTest             : 误判次数 = 259.

四、小结

我实际测试的时候,Guava 的效果应该是最好的,Redission 虽然是直接集成了Redis,但实际效果比起Guava较差一些,我这里没有贴上时间,Redission所创建出来的布隆过滤器,速度较慢。

当然我的测试范围是有限的,并且只是循环测试,另外服务器也并非在本地,这都有影响。

但是仅目前看来是这样的。

还有就是将 Guava 结合 Redis 一起使用。

五、Guava 布隆过滤器结合 Redis 使用

仅限于测试,一切效果还是需看实测。

我是以 Guava 中创建 布隆过滤器为基础,利用它构造的方法,来进行修改,功能相比于 guava 还是少了很多的。

 package com.nzc.boom;

 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.hash.Funnel;
 import com.google.common.hash.Hashing;
 import com.google.common.primitives.Longs;
 ​
 public class BloomFilterHelper<T> {

     private int numHashFunctions;

     private int bitSize;

     private Funnel<T> funnel;

     public BloomFilterHelper(Funnel<T> funnel, int expectedInsertions, double fpp) {
         Preconditions.checkArgument(funnel != null, "funnel不能为空");
         this.funnel = funnel;
         // 计算bit数组长度
         bitSize = optimalNumOfBits(expectedInsertions, fpp);
         // 计算hash方法执行次数
         numHashFunctions = optimalNumOfHashFunctions(expectedInsertions, bitSize);
     }
 ​
 ​
     /** 源码
      *public <T> boolean mightContain(
      *         T object, Funnel<? super T> funnel, int numHashFunctions, LockFreeBitArray bits) {
      *       long bitSize = bits.bitSize();
      *       byte[] bytes = Hashing.murmur3_128().hashObject(object, funnel).getBytesInternal();
      *       long hash1 = lowerEight(bytes);
      *       long hash2 = upperEight(bytes);
      *
      *       long combinedHash = hash1;
      *       for (int i = 0; i < numHashFunctions; i++) {
      *         // Make the combined hash positive and indexable
      *         if (!bits.get((combinedHash & Long.MAX_VALUE) % bitSize)) {
      *           return false;
      *         }
      *         combinedHash += hash2;
      *       }
      *       return true;
      *     }
      * @param value
      * @return
      */
     public long[] murmurHashOffset(T value) {
         long[] offset = new long[numHashFunctions];
         byte[] bytes = Hashing.murmur3_128().hashObject(value, funnel).asBytes();
         long hash1 = lowerEight(bytes);
         long hash2 = upperEight(bytes);
         long combinedHash = hash1;
         for (int i = 1; i <= numHashFunctions; i++) {
             long nextHash = hash1 + i * hash2;
             if (nextHash < 0) {
                 nextHash = ~nextHash;
             }
             offset[i - 1] = nextHash % bitSize;
         }
         return offset;
 ​
 ​
     }
     private /* static */ long lowerEight(byte[] bytes) {
         return Longs.fromBytes(
                 bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]);
     }
 ​
     private /* static */ long upperEight(byte[] bytes) {
         return Longs.fromBytes(
                 bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8]);
     }
     /**
      * 计算bit数组长度
      * 同样是guava创建布隆过滤器中的计算bit数组长度方法
      */
     private int optimalNumOfBits(long n, double p) {
         if (p == 0) {
             // 设定最小期望长度
             p = Double.MIN_VALUE;
         }
         return (int) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
     }

     /**
      * 这里是从guava 中 copy 出来的
      * 就是guava 创建一个 布隆过滤器时,
      * 计算hash方法执行次数的方法
      */
     private int optimalNumOfHashFunctions(long n, long m) {
         int countOfHash = Math.max(1, (int) Math.round((double) m / n * Math.log(2)));
         return countOfHash;
     }
 ​
 }

以上的这些代码,在guava包都可以找到的。

在redisConfig中注入布隆过滤器

 ​
 /**
  * @description:
  * @author: Yihui Wang
  * @date: 2022年08月26日 22:06
  */
 @Configuration
 @EnableCaching
 public class RedisConfig {
 ​
     @Bean
     public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
         RedisCacheManager rcm=RedisCacheManager.create(connectionFactory);
         return rcm;
     }
     @Bean
     public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
         RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
         redisTemplate.setConnectionFactory(factory);

         Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
                 Jackson2JsonRedisSerializer(Object.class);
         ObjectMapper om = new ObjectMapper();
         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
         jackson2JsonRedisSerializer.setObjectMapper(om);
         //序列化设置 ,这样计算是正常显示的数据,也能正常存储和获取
         redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);
         redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
         redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
         redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

         return redisTemplate;
     }
     @Bean
     public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
         StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
         stringRedisTemplate.setConnectionFactory(factory);
         return stringRedisTemplate;
     }

     //初始化布隆过滤器,放入到spring容器里面
     @Bean
     public BloomFilterHelper<String> initBloomFilterHelper() {
         return new BloomFilterHelper<String>((Funnel<String>) (from, into) -> into.putString(from, Charsets.UTF_8).putString(from, Charsets.UTF_8), 1000, 0.01);
     }
 ​
     @Bean
     public BloomFilterHelper<Long> initLongBloomFilterHelper() {
         return new BloomFilterHelper<Long>((Funnel<Long>) (from, into) -> into.putLong(from),1000, 0.01);
     }
 ​
 ​
 }

也就是注入我们刚刚编写的那个布隆过滤器。

然后再编写一个Service 层

 ​
 /**
  * @description:
  * @author: Yihui Wang
  */
 @Slf4j
 @Service
 public class RedisBloomFilter {
 ​
     @Autowired
     private RedisTemplate redisTemplate;
 ​
     /**
      * 根据给定的布隆过滤器添加值
      */
     public <T> void addByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) {
         Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
         long[] offset = bloomFilterHelper.murmurHashOffset(value);
         for (long i : offset) {
             log.info("key :{} ,value : {}", key,  i);
             redisTemplate.opsForValue().setBit(key, i, true);
         }
     }
 ​
     /**
      * 根据给定的布隆过滤器判断值是否存在
      */
     public <T> boolean includeByBloomFilter(BloomFilterHelper<T> bloomFilterHelper, String key, T value) {
         Preconditions.checkArgument(bloomFilterHelper != null, "bloomFilterHelper不能为空");
         long[] offset = bloomFilterHelper.murmurHashOffset(value);
         for (long i : offset) {
             log.info("key :{} ,value : {}", key,  i);
             if (!redisTemplate.opsForValue().getBit(key, i)) {
                 return false;
             }
         }
         return true;
     }
 }

测试:

     @Test
     public void test1() {
         // 预期插入数量
         long capacity = 1000L;
         // 错误比率
         double errorRate = 0.01;
         for (long i = 0; i < capacity; i++) {
             redisBloomFilter.addByBloomFilter(bloomFilterHelper, "nzc:bloomFilter1", i);
         }
         log.info("存入元素为=={}", capacity);
         // 统计误判次数
         int count = 0;
         // 我在数据范围之外的数据,测试相同量的数据,判断错误率是不是符合我们当时设定的错误率
         for (long i = capacity; i < capacity * 2; i++) {
             if (redisBloomFilter.includeByBloomFilter(bloomFilterHelper, "nzc:bloomFilter1", i)) {
                 count++;
             }
         }
         System.out.println("误判个数为==>" + count);
     }

输出:

存入元素为==1000
 误判个数为==>12

到此这篇关于详解SpringBoot中如何使用布隆过滤器的文章就介绍到这了,更多相关SpringBoot布隆过滤器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot 过滤器 Filter使用实例详解

    目录 简介 用法 功能 实现 简介 过滤器是AOP(面向切面编程)思想的具体实现.可以过滤浏览器发出的请求,并且决定放行请求还是中断请求. 在浏览器对服务器发起请求或者服务器对浏览器响应,都会经过过滤器. 基于过滤器的机制,我们可以在过滤器中对请求和响应做一些处理,可以在过滤器中决定是否放行,例如:校验请求中有没有敏感字符串,校验有没有Session,实现URL级别的权限控制.压缩响应信息.编码格式等. 用法 在spring的应用中我们存在两种过滤的用法,一种是拦截器.另外一种当然是过滤器.我们

  • SpringBoot实现过滤器拦截器的耗时对比

    目录 过滤器的方式 拦截器的方式 三种方式 下面为大家一一对应 过滤器的方式 拦截器的方式 过滤器的方式 这种方式简单点 但是可配置性不高 注意:一定得扫描到spring容器中 创建一个类 实现 filter接口 init:该方法是对filter对象进行初始化的方法,仅在容器初始化filter对象结束后被调用一次,参数FilterConfig可以获得filter的初始化参数: doFilter:可以对request和response进行<u>预处理</u>.其中FilterChai

  • SpringBoot详解shiro过滤器与权限控制

    目录 shiro过滤器 权限控制 动态配置权限 shiro过滤器 首先从客户端发来的所有请求都经过Shiro过滤器,如果用户没有认证的都打回去进行认证,认证成功的,再判断是否具有访问某类资源(公有资源,私有资源)的权限,如果没有权限,访问失败;如果有权限访问成功.注意:客户端传来的token要和realm中的认证信息进行相同规则的比较(加密算法要一致). 常见过滤器: 1.在shiro配置类中配置,使用 filterFactoryBean.setFilterChainDefinitionMa()

  • SpringBoot+Redis实现布隆过滤器的示例代码

    目录 简述 Redis安装BloomFilter 基本指令 结合SpingBoot 方式一 方式二 简述 关于布隆过滤器的详细介绍,我在这里就不再赘述一遍了 我们首先知道:BloomFilter使用长度为m bit的字节数组,使用k个hash函数,增加一个元素: 通过k次hash将元素映射到字节数组中k个位置中,并设置对应位置的字节为1.查询元素是否存在: 将元素k次hash得到k个位置,如果对应k个位置的bit是1则认为存在,反之则认为不存在. Guava 中已经有具体的实现,而在我们实际生产

  • SpringBoot之跨域过滤器配置允许跨域访问方式

    目录 SpringBoot跨域过滤器配置允许跨域访问 跨域请求 问题背景 跨域过滤器 跨域功能改进 SpringBoot跨域设置(CORS) 一.什么是跨域 二.跨域资源共享(CORS) 1. 简单请求 2. 非简单请求 三.SpringBoot设置CORS SpringBoot跨域过滤器配置允许跨域访问 跨域请求 当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求. 出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求. 例如,XMLHttp

  • SpringBoot中shiro过滤器的重写与配置详解

    目录 问题 解决方案 实现代码 1.重写shiro 登录 过滤器 2.重写role权限 过滤器 3.配置过滤器 问题 遇到问题:在前后端分离跨域访问的项目中shiro进行权限拦截失效 (即使有正确权限的访问也会被拦截) 时造成302重定向错误等问题报错:Response for preflight is invalid (redirect) 1.302原因:使用ajax访问后端项目时无法识别重定向操作 2.shiro拦截失效原因:跨域访问时有一种带预检访问的跨域,即访问时先发出一条methods

  • Springboot详解如何实现SQL注入过滤器过程

    目录 1.过滤器SqlInjectFilter 2.请求装饰类CustomRequestWrapper 3.过滤器注册 4.测试辅助类 4.1 结果对象ResultObj 4.2 Restful的Controller类 5.测试 5.1 POST请求测试 5.2 GET请求测试1 5.3 GET请求测试2 场景:以过滤器(Filter)的方式,对所有http请求的入参拦截,使用正则表达式匹配入参中的字符串.存在SQL注入风险的参数,中断请求,并立即返回提示信息.不存在SQL注入风险的参数,校验通

  • springBoot 过滤器去除请求参数前后空格实例详解

    目录 背景 一.实现代码 1.注册过滤器 2.自定义过滤器TrimFilter 3.自定义TrimRequestWrapper类 二.测试 1.Get请求测试首尾去空格 2.Post方法测试首尾去空格 3.post方法 json参数测试首尾去空格 背景 用户在前端页面中不小心输入的前后空格,为了防止因为前后空格原因引起业务异常,所以我们需要去除参数的前后空格! 如果我们手动去除参数前后空格,我们可以这样做 @GetMapping(value = "/manualTrim") publi

  • 详解SpringBoot中如何使用布隆过滤器

    目录 前言 一.Guava 实现布隆过滤器 二.Hutool 布隆过滤器 三.Redission 布隆过滤器 四.小结 五.Guava 布隆过滤器结合 Redis 使用 昨天写了一篇Redis布隆过滤器相关的命令的文章,今天来说一说springboot中如何简单在代码中使用布隆过滤器吧. 目前市面上也有好几种实现方式,如果你需要高度定制化,可以完全从零实现,当然这不是一个简单的工程. 如果只是想快速开始的话,那么市面上现成的实现,无疑是最快的. 前言 今天说到的实现方式有以下几种: 引入 Gua

  • 详解SpringBoot中自定义和配置拦截器的方法

    目录 1.SpringBoot版本 2.什么是拦截器 3.工作原理 4.拦截器的工作流程 4.1正常流程 4.2中断流程 5.应用场景 6.如何自定义一个拦截器 7.如何使其在Spring Boot中生效 8.实际使用 8.1场景模拟 8.2思路 8.3实现过程 8.4效果体验 9.总结 1.SpringBoot版本 本文基于的Spring Boot的版本是2.6.7 . 2.什么是拦截器 Spring MVC中的拦截器(Interceptor)类似于ServLet中的过滤器(Filter),它

  • 详解SpringBoot中的参数校验(项目实战)

    Java后端发工作中经常会对前端传递过来的参数做一些校验,在业务中还要抛出异常或者不断的返回异常时的校验信息,充满了if-else这种校验代码,在代码中相当冗长.例如说,用户注册时,会校验手机格式的正确性,用户名的长度等等.虽说前端也可以做参数校验,但是为了保证我们API接口的可靠性,以保证最终数据入库的正确性,后端进行参数校验不可忽视. Hibernate Validator 提供了一种统一方便的方式,让我们快速的实现参数校验. Hibernate Validator 使用注解,实现声明式校验

  • 详解SpringBoot中Controller接收对象列表实现

    如果Spring Boot中对应的Controller要接收一个对象,该对象中又存放了一个List列表,那么页面该如何传递相关应的参数信息呢. 本篇文章给大家一个简单的示例,提供一种实现方式. 实体类 首先看实体类的结构(注意使用了Lombok): @Data public class Rules { private List<Rule> rules; } 对应Rule实体类代码如下: @Data public class Rule { /** * 类名 */ private String c

  • 详解SpringBoot中的tomcat优化和修改

    项目背景 在做项目的时候,把SpringBoot的项目打包成安装包了,在客户上面安装运行,一切都是那么的完美,可是发生了意外,对方突然说导出导入的文件都不行了.我急急忙忙的查看日志,发现报了一个错误 java.io.IOException: The temporary upload location [C:\Windows\Temp\tomcat.1351070438015228346.8884\work\Tomcat\localhost\ROOT] is not valid at org.ap

  • 详解SpringBoot中添加@ResponseBody注解会发生什么

    SpringBoot版本2.2.4.RELEASE. [1]SpringBoot接收到请求 ① springboot接收到一个请求返回json格式的列表,方法参数为JSONObject 格式,使用了注解@RequestBody 为什么这里要说明返回格式.方法参数.参数注解?因为方法参数与参数注解会影响你使用不同的参数解析器与后置处理器!通常使用WebDataBinder进行参数数据绑定结果也不同. 将要调用的目标方法如下: @ApiOperation(value="分页查询") @Re

  • 详解SpringBoot中关于%2e的Trick

    分享一个SpringBoot中关于%2e的小Trick.先说结论,当SpringBoot版本在小于等于2.3.0.RELEASE的情况下, alwaysUseFullPath 为默认值false,这会使得其获取ServletPath,所以在路由匹配时会对 %2e 进行解码,这可能导致身份验证绕过.而反过来由于高版本将 alwaysUseFullPath 自动配置成了true从而开启全路径,又可能导致一些安全问题. 这里我们来通过一个例子看一下这个Trick,并分析它的原因. 首先我们先来设置Sp

  • 详解springboot中各个版本的redis配置问题

    今天在springboot中使用数据库,springboot版本为2.0.2.RELEASE,通过pom引入jar包,配置文件application.properties中的redis配置文件报错,提示例如deprecated configuration property 'spring.redis.pool.max-active',猜想应该是版本不对,发现springboot在1.4前后集成redis发生了一些变化.下面截图看下. 一.不同版本RedisProperties的区别 这是spri

  • 详解SpringBoot中@SessionAttributes的使用

    目录 简介 概述 代码 后端代码 前端代码 测试 简介 说明 本文介绍SpringBoot中@SessionAttributes的用法. 概述 在默认情况下,ModelMap中的属性作用域是request级别,也就是说,当本次请求结束后,ModelMap 中的属性将销毁.如果希望在多个请求中共享ModelMap中的属性,必须将其属性转存到session 中,这样 ModelMap 的属性才可以被跨请求访问. Spring 允许我们有选择地指定 ModelMap 中的哪些属性需要转存到 sessi

  • 详解SpringBoot中@ConditionalOnClass注解的使用

    目录 一.@ConditionalOnClass注解初始 二.@ConditionalOnClass注解用法 1.使用value属性 2.使用name属性 三.@ConditionalOnClass是怎么实现的 四.总结 今天给大家带来的是springboot中的@ConditionalOnClass注解的用法.上次的@ConditionalOnBean注解还记得吗? 一.@ConditionalOnClass注解初始 看下@CodidtionalOnClass注解的定义, 需要注意的有两点,

随机推荐