SpringBoot整合Redis使用基于注解的缓存

环境准备

注解

@EnableCaching

image.png

@CacheConfig

@CacheConfig 提供了一种在类级别共享公共缓存相关设置的机制。

| 参数 | 作用 |
|
| — | — | — |
| cacheNames | 使用在类上的默认缓存名称 | |
| keyGenerator | 用于类的默认KeyGenerator的bean名称 | |
| cacheManager | 自定义CacheManager的bean名称,如果尚未设置,则可以用于创建默认CacheResolver | |
| cacheResolver | 要使用的自定义CacheResolver的bean名称 | |

@Cacheable

@Cacheable 可以标记在一个方法上,也可以标记在类上,当标记在类上时,当前类的所有方法都支持缓存,当注解的方法被调用时,如果缓存中有值,则直接返回缓存中的数据

参数作用example
cacheNames / value缓存的空间名称,这两个配置只能二选一
key / keyGenerator缓存的key,同一个空间名称value下的key唯一,可以通过SpEL 表达式编写指定这个key的值,或者通过keyGenerator生成,这两个配置只能二选一
cacheManager自定义CacheManager的bean名称,如果尚未设置,则可以用于创建默认CacheResolver
cacheResolver要使用的自定义CacheResolver的bean名称
condition缓存的条件,默认为true,使用 SpEL 编写,返回true或者false,只有为true才进行缓存。为true时:如果缓存有值,则不执行方法;如果缓存没值,则执行方法并将结果保存到缓存。为false时:不执行缓存,每次都执行方法。
unless函数返回值符合条件的不缓存、只缓存其余不符合条件的。可以使用 SpEL 编写,方法参数可以通过索引访问,例如:第二个参数可以通过#root.args[1]、#p1或#a1访问
sync是否使用异步模式。默认是方法执行完,以同步的方式将方法返回的结果存在缓存中
   @Cacheable(cacheNames =spaceUserPre, key = "#redisSyc.name",condition = "true",unless = "#result?.result==null")public RedisSyc select(RedisSyc redisSyc){RedisSyc select = redisAnnoMapper.select(redisSyc);return select;}
@CachePut

@CachePut 可以标记在一个方法上,也可以标记在类上。使用 @CachePut 标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,然后将执行结果以键值对的形式存入指定的缓存中

| 参数 | 作用 |
|
| — | — | — |
| cacheNames / value | 缓存的空间名称,这两个配置只能二选一 | |
| key / keyGenerator | 缓存的key,同一个空间名称value下的key唯一,可以通过SpEL 表达式编写指定这个key的值,或者通过keyGenerator生成,这两个配置只能二选一 | |
| cacheManager | 自定义CacheManager的bean名称,如果尚未设置,则可以用于创建默认CacheResolver | |
| cacheResolver | 要使用的自定义CacheResolver的bean名称 | |
| condition | 缓存的条件,默认为true,使用 SpEL 编写,返回true或者false,只有为true才进行缓存。为true时:如果缓存有值,则不执行方法;如果缓存没值,则执行方法并将结果保存到缓存。为false时:不执行缓存,每次都执行方法。 | |
| unless | 函数返回值符合条件的不缓存、只缓存其余不符合条件的。可以使用 SpEL 编写,方法参数可以通过索引访问,例如:第二个参数可以通过#root.args[1]、#p1或#a1访问 | |

  @CachePut(cacheNames = spaceUserPre, key = "#redisSyc.name",condition = "true",unless = "#result==null" )public RedisSyc add(RedisSyc redisSyc){redisAnnoMapper.add(redisSyc);log.info("添加成功:{}",new Gson().toJson(redisSyc));return redisSyc;}
@CacheEvict

@CacheEvict 可以标记在一个方法上,也可以标记在类上,用来清除缓存元素的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。

| 参数 | 作用 |
|
| — | — | — |
| cacheNames / value | 缓存的空间名称,这两个配置只能二选一 | |
| key / keyGenerator | 缓存的key,同一个空间名称value下的key唯一,可以通过SpEL 表达式编写指定这个key的值,或者通过keyGenerator生成,这两个配置只能二选一 | |
| cacheManager | 自定义CacheManager的bean名称,如果尚未设置,则可以用于创建默认CacheResolver | |
| cacheResolver | 要使用的自定义CacheResolver的bean名称 | |
| condition | 缓存的条件,默认为true,使用 SpEL 编写,返回true或者false,只有为true才进行缓存。为true时:如果缓存有值,则不执行方法;如果缓存没值,则执行方法并将结果保存到缓存。为false时:不执行缓存,每次都执行方法。 | |
| allEntries | 为true时表示清除(cacheNames或value)空间名里的所有的数据 | |
| beforeInvocation | 为false时,缓存的清除是否再方法之前执行,默认代表缓存清除操作是在方法执行后执行,如果出现异常缓存就不会清除;为true时,代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除 | |

  @CacheEvict(cacheNames = spaceUserPre, key = "#redisSyc.name",condition = "true",allEntries = true,beforeInvocation = true )public void delete(RedisSyc redisSyc){redisAnnoMapper.delete(redisSyc);}
@Caching

@Caching 多个缓存注解(不同或相同类型)的组注解。

| 参数 | 作用 |
|
| — | — | — |
| cacheNames / value | 缓存的空间名称,这两个配置只能二选一 | |
| key / keyGenerator | 缓存的key,同一个空间名称value下的key唯一,可以通过SpEL 表达式编写指定这个key的值,或者通过keyGenerator生成,这两个配置只能二选一 | |
| cacheManager | 自定义CacheManager的bean名称,如果尚未设置,则可以用于创建默认CacheResolver | |
| cacheResolver | 要使用的自定义CacheResolver的bean名称 | |
| condition | 缓存的条件,默认为true,使用 SpEL 编写,返回true或者false,只有为true才进行缓存。为true时:如果缓存有值,则不执行方法;如果缓存没值,则执行方法并将结果保存到缓存。为false时:不执行缓存,每次都执行方法。 | |
| allEntries | 为true时表示清除(cacheNames或value)空间名里的所有的数据 | |
| beforeInvocation | 为false时,缓存的清除是否再方法之前执行,默认代表缓存清除操作是在方法执行后执行,如果出现异常缓存就不会清除;为true时,代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除 | |

spEL 编写 key

微信截图_20231113143318.png

springboot集成

<?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.17</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.redis</groupId><artifactId>redis01</artifactId><version>0.0.1-SNAPSHOT</version><name>redis01</name><description>redis01</description><properties><java.version>8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.0.0</version></dependency><!--alibaba的druid数据库连接池 (德鲁伊)--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!--Mybatis整合springboot的起步依赖--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.0.0</version></dependency><!--【数据库】数据库连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.11</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--jedis--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.3.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource></resources></build></project>
#连接数据源
spring.datasource.druid.username=root
spring.datasource.druid.password=xgm@2023..
spring.datasource.druid.url=jdbc:mysql://172.16.204.51:3306/redis?serverTimezone=GMT%2B8
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.initial-size=5##指定缓存类型redis
#spring.cache.type=redis
##一个小时,以毫秒为单位
#spring.cache.redis.time-to-live=3600000
##给缓存的建都起一个前缀。  如果指定了前缀就用我们指定的,如果没有就默认使用缓存的名字作为前缀,一般不指定
#spring.cache.redis.key-prefix=CACHE_
##指定是否使用前缀
#spring.cache.redis.use-key-prefix=true
##是否缓存空值,防止缓存穿透
#spring.cache.redis.cache-null-values=true#redis
spring.redis.host=172.16.204.51
spring.redis.port=6379
spring.redis.password=123456
spring.redis.database=1# mybatis配置
mybatis:
check-config-location: true
#  mybatis框架配置文件,对mybatis的生命周期起作用
config-location: "classpath:mybatis/mybatis-config.xml"
#  配置xml路径
mapper-locations: "classpath:mybatis/mapper/*Mapper.xml"
#  配置model包路径
type-aliases-package: "com.redis.redis01.bean.*"#日志
logging.level.root=debug
logging.level.io.lettuce.core=debug
logging.level.org.springframework.data.redis=debug
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC"-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><!-- 全局的映射器启用或禁用缓存。 --><setting name="cacheEnabled" value="true"/><!-- 全局启用或禁用延迟加载 --><setting name="lazyLoadingEnabled" value="true"/><!-- 允许或不允许多种结果集从一个单独的语句中返回 --><setting name="multipleResultSetsEnabled" value="true"/><!-- 使用列标签代替列名 --><setting name="useColumnLabel" value="true"/><!-- 允许JDBC支持生成的键 --><setting name="useGeneratedKeys" value="false"/><!-- 配置默认的执行器 --><setting name="defaultExecutorType" value="SIMPLE"/><!-- 设置超时时间 --><setting name="defaultStatementTimeout" value="60"/><!-- 设置驼峰标识 --><setting name="mapUnderscoreToCamelCase" value="true"/></settings><plugins><!-- 分页插件 --><plugin interceptor="com.github.pagehelper.PageInterceptor" /></plugins>
</configuration>
package com.redis.redis01.conf;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.redis.redis01.bean.CacheConstant;
import lombok.extern.slf4j.Slf4j;import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.BatchStrategies;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.*;import javax.annotation.Resource;
import java.time.Duration;import static java.util.Collections.singletonMap;/*** 开启缓存支持* @author zyf* @Return:*/
@Slf4j
//创建redis配置类,一定要注意打上@EnableCaching注解,否则spring自带的缓存注解功能将不会自动启用
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {@Resourceprivate LettuceConnectionFactory lettuceConnectionFactory;//	/**
//	 * @description 自定义的缓存key的生成策略 若想使用这个key
//	 *              只需要讲注解上keyGenerator的值设置为keyGenerator即可</br>
//	 * @return 自定义策略生成的key
//	 */
//	@Override
//	@Bean
//	public KeyGenerator keyGenerator() {
//		return new KeyGenerator() {
//			@Override
//			public Object generate(Object target, Method method, Object... params) {
//				StringBuilder sb = new StringBuilder();
//				sb.append(target.getClass().getName());
//				sb.append(method.getDeclaringClass().getName());
//				Arrays.stream(params).map(Object::toString).forEach(sb::append);
//				return sb.toString();
//			}
//		};
//	}/*** RedisTemplate配置* @param lettuceConnectionFactory* @return*/@Beanpublic RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {log.debug(" --- redis config init --- ");Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();redisTemplate.setConnectionFactory(lettuceConnectionFactory);RedisSerializer<String> stringSerializer = new StringRedisSerializer();// key序列化redisTemplate.setKeySerializer(stringSerializer);// value序列化redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// Hash key序列化redisTemplate.setHashKeySerializer(stringSerializer);// Hash value序列化redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);redisTemplate.afterPropertiesSet();return redisTemplate;}/*** 缓存配置管理器** @param factory* @return*/@Beanpublic CacheManager cacheManager(LettuceConnectionFactory factory) {Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = jacksonSerializer();// 配置序列化(解决乱码的问题),并且配置缓存默认有效期 6小时RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(6));RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));//.disableCachingNullValues();// 以锁写入的方式创建RedisCacheWriter对象//update-begin-author:taoyan date:20210316 for:注解CacheEvict根据key删除redis支持通配符*RedisCacheWriter writer = new MyRedisCacheWriter(factory, Duration.ofMillis(50L));//RedisCacheWriter.lockingRedisCacheWriter(factory);// 创建默认缓存配置对象/* 默认配置,设置缓存有效期 1小时*///RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));/* 自定义配置test:demo 的超时时间为 5分钟*/RedisCacheManager cacheManager = RedisCacheManager.builder(writer).cacheDefaults(redisCacheConfiguration).withInitialCacheConfigurations(singletonMap(CacheConstant.SYS_DICT_TABLE_CACHE,RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).disableCachingNullValues().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)))).withInitialCacheConfigurations(singletonMap(CacheConstant.TEST_DEMO_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues())).transactionAware().build();//        RedisCacheManager cacheManager =
//                RedisCacheManager
//                        .builder(RedisCacheWriter.lockingRedisCacheWriter
//                (factory, BatchStrategies.scan(1000))).cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(Long.valueOf(10))).disableCachingNullValues())
//                .transactionAware()
//                .build();//update-end-author:taoyan date:20210316 for:注解CacheEvict根据key删除redis支持通配符*return cacheManager;}private Jackson2JsonRedisSerializer jacksonSerializer() {Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);return jackson2JsonRedisSerializer;}}
package com.redis.redis01.conf;import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.PessimisticLockingFailureException;
import org.springframework.data.redis.cache.CacheStatistics;
import org.springframework.data.redis.cache.CacheStatisticsCollector;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;/*** 该类参照 DefaultRedisCacheWriter 重写了 remove 方法实现通配符*删除,解决生产环境禁用kyes*后。@CacheEvict报错问题*/
@Slf4j
public class MyRedisCacheWriter implements RedisCacheWriter {private final RedisConnectionFactory connectionFactory;private final Duration sleepTime;public MyRedisCacheWriter(RedisConnectionFactory connectionFactory) {this(connectionFactory, Duration.ZERO);}public MyRedisCacheWriter(RedisConnectionFactory connectionFactory, Duration sleepTime) {Assert.notNull(connectionFactory, "ConnectionFactory must not be null!");Assert.notNull(sleepTime, "SleepTime must not be null!");this.connectionFactory = connectionFactory;this.sleepTime = sleepTime;}public void put(String name, byte[] key, byte[] value, @Nullable Duration ttl) {Assert.notNull(name, "Name must not be null!");Assert.notNull(key, "Key must not be null!");Assert.notNull(value, "Value must not be null!");this.execute(name, (connection) -> {if (shouldExpireWithin(ttl)) {connection.set(key, value, Expiration.from(ttl.toMillis(), TimeUnit.MILLISECONDS), SetOption.upsert());} else {connection.set(key, value);}return "OK";});}public byte[] get(String name, byte[] key) {Assert.notNull(name, "Name must not be null!");Assert.notNull(key, "Key must not be null!");return this.execute(name, (connection) -> {return connection.get(key);});}public byte[] putIfAbsent(String name, byte[] key, byte[] value, @Nullable Duration ttl) {Assert.notNull(name, "Name must not be null!");Assert.notNull(key, "Key must not be null!");Assert.notNull(value, "Value must not be null!");return this.execute(name, (connection) -> {if (this.isLockingCacheWriter()) {this.doLock(name, connection);}Object var7;try {boolean put;if (shouldExpireWithin(ttl)) {put = connection.set(key, value, Expiration.from(ttl), SetOption.ifAbsent());} else {put = connection.setNX(key, value);}if (!put) {byte[] var11 = connection.get(key);return var11;}var7 = null;} finally {if (this.isLockingCacheWriter()) {this.doUnlock(name, connection);}}return (byte[])var7;});}public void remove(String name, byte[] key) {Assert.notNull(name, "Name must not be null!");Assert.notNull(key, "Key must not be null!");String keyString = new String(key);log.debug("redis remove key:" + keyString);if(keyString!=null && keyString.endsWith("*")){execute(name, connection -> {// 获取某个前缀所拥有的所有的键,某个前缀开头,后面肯定是*Set<byte[]> keys = connection.keys(key);int delNum = 0;for (byte[] keyByte : keys) {delNum += connection.del(keyByte);}return delNum;});}else{this.execute(name, (connection) -> {return connection.del(new byte[][]{key});});}}public void clean(String name, byte[] pattern) {Assert.notNull(name, "Name must not be null!");Assert.notNull(pattern, "Pattern must not be null!");this.execute(name, (connection) -> {boolean wasLocked = false;try {if (this.isLockingCacheWriter()) {this.doLock(name, connection);wasLocked = true;}// byte[][] keys = (byte[][])((Set)Optional.ofNullable(connection.keys(pattern)).orElse(Collections.emptySet())).toArray(new byte[0][]);// 使用scan命令代替原本的keys命令搜索keyfinal Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().count(100).match(pattern).build());Set<byte[]> byteSet = new HashSet<>();while (cursor.hasNext()) {byteSet.add(cursor.next());}byte[][] keys = byteSet.toArray(new byte[0][]);if (keys.length > 0) {connection.del(keys);}} finally {if (wasLocked && this.isLockingCacheWriter()) {this.doUnlock(name, connection);}}return "OK";});}@Overridepublic void clearStatistics(String name) {}@Overridepublic RedisCacheWriter withStatisticsCollector(CacheStatisticsCollector cacheStatisticsCollector) {return null;}void lock(String name) {this.execute(name, (connection) -> {return this.doLock(name, connection);});}void unlock(String name) {this.executeLockFree((connection) -> {this.doUnlock(name, connection);});}private Boolean doLock(String name, RedisConnection connection) {return connection.setNX(createCacheLockKey(name), new byte[0]);}private Long doUnlock(String name, RedisConnection connection) {return connection.del(new byte[][]{createCacheLockKey(name)});}boolean doCheckLock(String name, RedisConnection connection) {return connection.exists(createCacheLockKey(name));}private boolean isLockingCacheWriter() {return !this.sleepTime.isZero() && !this.sleepTime.isNegative();}private <T> T execute(String name, Function<RedisConnection, T> callback) {RedisConnection connection = this.connectionFactory.getConnection();try {this.checkAndPotentiallyWaitUntilUnlocked(name, connection);return callback.apply(connection);} finally {connection.close();}}private void executeLockFree(Consumer<RedisConnection> callback) {RedisConnection connection = this.connectionFactory.getConnection();try {callback.accept(connection);} finally {connection.close();}}private void checkAndPotentiallyWaitUntilUnlocked(String name, RedisConnection connection) {if (this.isLockingCacheWriter()) {try {while(this.doCheckLock(name, connection)) {Thread.sleep(this.sleepTime.toMillis());}} catch (InterruptedException var4) {Thread.currentThread().interrupt();throw new PessimisticLockingFailureException(String.format("Interrupted while waiting to unlock cache %s", name), var4);}}}private static boolean shouldExpireWithin(@Nullable Duration ttl) {return ttl != null && !ttl.isZero() && !ttl.isNegative();}private static byte[] createCacheLockKey(String name) {return (name + "~lock").getBytes(StandardCharsets.UTF_8);}@Overridepublic CacheStatistics getCacheStatistics(String cacheName) {return null;}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ARM Trace32(劳特巴赫) 使用介绍 2.1 -- TRACE32 Practice 脚本 cmm 脚本学习】

请阅读【ARM Coresight SoC-400/SoC-600 专栏导读】 上篇文章【ARM Trace32(劳特巴赫) 使用介绍 2 - Veloce 环境中使用trace32 连接 Cortex-M33】 下篇文章【ARM Trace32(劳特巴赫) 使用介绍 2.2 – TRACE32 进阶命令之 DIAG 弹框命令】 文章目录 1. TRACE32 Practice 语法1.…

Python | 机器学习之PCA降维

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《人工智能奇遇记》&#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 目录结构 1. 机器学习之PCA降维概念 1.1 机器学习 1.2 PCA降维 2. PCA降维 2.1 实验目的 2…

如何在Jupyter Lab中安装不同的Kernel

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Jupyter Notebook的下载安装与使用教程_Python数据分析与可视化

Jupyter Notebook的下载安装与使用 Jupyter简介下载与安装启动与创建NotebookJupyter基本操作 在计算机编程领域&#xff0c;有一个很强大的工具叫做Jupyter。它不仅是一个集成的开发环境&#xff0c;还是一个交互式文档平台。对于初学者来说&#xff0c;Jupyter提供了友好的界…

linux实现SSH免密登录设置,以及shell脚本实现

原创/朱季谦 最近在搭建linux集群&#xff0c;做了SSH免密登录的设置&#xff0c;正好把过程记录一下&#xff1a; 一.用搭建好的两台虚拟机做演示&#xff0c;A机器&#xff1a;192.168.200.129&#xff0c;B机器&#xff1a;192.168.200.128 二.分别在两台机器上执行以下步…

机器人制作开源方案 | 守护一体化护耆卫士

作者&#xff1a;白玲玲、张硕、孔亚轩单位&#xff1a;兰州理工大学指导老师&#xff1a;毕广利 1. 场景调研 “探索者”平台是结合机械、电子、传感器、计算机软硬件、控制、人工智能和造型技术等众多的先进技术研发推出的专业型机器人设备原型设计工具&#xff0c;包含机构…

利用jquery对HTML中的名字进行替代

想法&#xff1a;将网页中经常要修改的名字放在一个以jquery编写的js文件中&#xff0c;如果需要修改名字&#xff0c;直接修改js文件中的名字即可。 新建name_07.html文件&#xff0c;写入下面的代码&#xff1a; <!DOCTYPE html> <html> <head><meta …

Excel Unix时间戳和日期时间格式的相互转换

时间戳转日期时间 ((A18*3600)/86400)DATE(1970,1,1) # 或 (A18*3600)/8640070*36519# 带格式化 TEXT((C18*3600)/8640070*36519,"yyyy-mm-dd hh:mm:ss")首先加8小时进行时区转换&#xff0c;然后转换成天数&#xff0c;再加上1970年1月1日&#xff0c;最后设置日期…

kafka分布式安装部署

1.集群规划 2.集群部署 官方下载地址&#xff1a;http://kafka.apache.org/downloads.html &#xff08;1&#xff09;上传并解压安装包 [zhangflink9wmwtivvjuibcd2e package]$ tar -zxvf kafka_2.12-3.3.1.tgz -C ../software/&#xff08;2&#xff09;修改解压后的文件…

休闲娱乐 - 挂耳咖啡

公司有一个小的茶歇间&#xff0c;平时去喝个咖啡、放松身心、锻炼下身体。咖啡机是现磨咖啡豆的&#xff0c;喝喝就习惯了。 而我旁边一位同事习惯每天早上来自己泡一杯挂耳咖啡&#xff0c;再配上牛奶&#xff0c;感觉挺高级的。 关于挂耳咖啡就查了一下资料&#xff0c;介绍…

DDR SDRAM 学习笔记

一、基本知识 1.SDRAM SDRAM : 即同步动态随机存储器&#xff08;Synchronous Dynamic Random Access Memory&#xff09;, 同步是指其时钟频率与对应控制器&#xff08;CPU/FPGA&#xff09;的系统时钟频率相同&#xff0c;并且内部命令 的发送与数据传输都是以该时钟为基准…

web环境实现一键式安装启动

部署的痛点 一般在客户环境安装web环境&#xff0c;少说需要花费1-2小时。一般需要安装jdk、nginx、mysql、redis等 等你接触到了inno setup &#xff0c;你有可能会节约更少的时间去部署。也有可能是一个不懂技术的人&#xff0c;都可以进行操作的。废话不多说&#xff0c;接…

Ansible 企业实战详解

一、ansible简介1. ansible是什么2.ansible的特点ansible的架构图 二、ansible 任务执行1、ansible 任务执行模式2、ansible 执行流程3、ansible 命令执行过程 二 .Ansible安装部署1.yum安装2.ansible 程序结构3、ansible配置文件查找顺序4、ansible配置文件5.ansible自动化配置…

yolov5模型代码怎么修改

yaml配置文件 深度乘积因子 宽度乘积因子 所有版本只有这两个参数的不同&#xff0c;s m l x逐渐加宽加深 各种类型层参数对照 backbone里的各层&#xff0c;在这里解析&#xff0c;只需要改.yaml里的各层参数就能控制网络结构 修改网络结构 第一步&#xff1a;把新加的模块…

C++打怪升级(十一)- STL之list

~~~~ 前言1. list是什么2. list接口函数的使用1. 构造相关默认构造n个val构造迭代器范围构造拷贝构造 2 赋值运算符重载函数2 析构函数3 迭代器相关begin 和 endrbegin 和rend 4 容量相关emptysize 5 元素访问相关frontback 6 修改相关push_backpop_backpush_frontpop_frontins…

二进制的形式在内存中绘制一个对象实例

一、引用类型实例的内存布局 从内存布局的角度来看&#xff0c;一个引用类型的实例由如下图所示的三部分组成&#xff1a;ObjHeader TypeHandle Fields。前置的ObjHeader用来缓存哈希值和同步状态&#xff0c;TypeHandle部分存储类型对应方法表&#xff08;Method Table&…

并发编程之生产者消费者模型

什么是生产者消费者模型 生产者消费者模型是多线程中一个比较典型的模型。 打个比方&#xff1a;你是一个客户&#xff0c;你去超市里买火腿肠。 这段话中的 "你"就是消费者&#xff0c; 那么给超市提供火腿肠的供货商就是生产者。超市呢&#xff1f;超市是不是被…

Scrapy----Scrapy简介

文章目录 概述与应用背景架构和组件功能和特点社区生态概述与应用背景 Scrapy,一个高效、灵活、且强大的Web爬取框架,被广泛应用于数据抓取和网页内容的结构化提取。它是用Python编写的,支持多平台运行,适用于数据挖掘、在线零售信息收集、历史数据存档等多种场景。Scrapy…

【ArcGIS处理】行政区划与流域区划间转化

【ArcGIS处理】行政区划与流域区划间转化 引言数据准备1、行政区划数据2、流域区划数据 ArcGIS详细处理步骤Step1&#xff1a;统计行政区划下子流域面积1、创建批量处理模型2、添加批量裁剪处理3、添加计算面积 Step2&#xff1a;根据子流域面积占比均化得到各行政区固定值 参考…

设计基于STM32F103C8T6微控制器的巡线小车

巡线小车是一种能够在一条预定线追踪路径的小车&#xff0c;广泛应用于工业自动化、物流仓储、智能家居等领域。本设计将使用STM32F103C8T6微控制器来实现一个基础的巡线小车。 硬件组成&#xff1a;1. STM32F103C8T6微控制器开发板&#xff1a;作为巡线小车的核心控制器&…