Java 项目实战:交友项目 - SpringCache 入门案例深化:key 写法、RedisCacheManager 与写时失效

概率论 知识图谱 + 实战案例解析 本文通过构建概率论知识图谱,系统梳理了从随机试验、概率、条件概率到随机变量与分布的核心概念及其内在联系。结合朴素贝叶斯分类器和蒙特卡洛模拟等实战案例,深入解析了条件概率、独立性及大数定律等理论如何应用于机器学习与实际问题求解,旨在帮助读者建立结构化思维,将理论有效转化为解决实际问题的能力。 阅读详情

概述

SpringCache 的概念与注解体系是基础,本文只回答三个落地时绕不开的问题:key 到底怎么写才不会串、Redis 里的过期时间和序列化在哪里配、数据变了以后缓存什么时候失效。全部围绕交友项目的视频列表这一处真实缓存展开。

纲要

  • 缓存存放位置:默认 ConcurrentMap → 换成 Redis 的两步配置
  • key 写法专题
  • SpEL 表达式速查:#参数名、#p0、#result、T() 静态调用
  • 组合 key 的字符串拼接写法与工程实读逐项解释
  • key 长度与可读性的权衡
  • RedisCacheManager 配置
  • 工程实读:RedisCacheConfig 按 cacheName 分配 TTL
  • 完整可复制配置类:entryTtl + GenericJackson2JsonRedisSerializer
  • 写时失效:发布视频时 @CacheEvict(allEntries=true) 与失效粒度取舍
  • 视频列表落点预演:queryVideoList 实读代码逐项解释
  • 时序图:缓存命中与未命中的两条路径
  • 踩坑清单与可复现验证

缓存数据到底存在哪

入门案例跑通之后有个疑问:代码里没配 Redis 也没配别的缓存,数据去哪了。默认情况下 SpringCache 用 ConcurrentMapCacheManager,底层是每个命名空间一个 ConcurrentMapCache(内部就是 ConcurrentHashMap),纯内存的本地缓存,应用一重启全部清零,集群部署时各节点各存一份。

换存储介质只需要两步:

第一步,引入 spring-boot-starter-data-redis 依赖:

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

第二步,在 application.yml 中配置 Redis 连接:

spring:
  redis:
    port: 6379
    host: 192.168.136.160

只要 classpath 里同时有 spring-boot-starter-cache 和 spring-boot-starter-data-redis,容器自动装配的 CacheManager 就会从 ConcurrentMapCacheManager 切换为 RedisCacheManager,缓存数据随之写入 Redis。项目工程的 mf-app-server 正是这套依赖组合。

存储位置换成了 Redis,但真正决定「同一次调用会不会命中缓存」的是 key 的生成规则,这是下面要展开的重点。

key 写法专题

Redis 中 key 的命名规则

RedisCacheManager 生成的 key 遵循固定格式:

cacheName + "::" + key

cacheName 就是 @Cacheable 的 value 属性。它不是「缓存的值」,而是命名空间(分组):同一业务的数据放在同一个组下,value="user"、key 为 1 和 2 的两条数据,在 Redis 里就是 user::1 和 user::2。分组的好处是按业务隔离,后续做批量清除(allEntries=true)时也只清自己组。

SpEL 表达式速查

key 属性支持 Spring EL 表达式,常用的几种写法:

写法含义示例求值结果
#参数名直接引用方法参数key="#userId",传 1 则 key 为 user::1
#p0、#p1按参数位置引用(第 0 个、第 1 个参数)与 #参数名 等价,参数被编译时去名后仍可用
#result引用方法返回值(仅 @CachePut 场景有意义)key="#result.id"
'字面量'字符串常量,单引号包裹key="'test' + #id",得到 user::test1
T(全限定类名).静态方法()调用任意类的静态方法key="T(com.mf.server.interceptor.UserHolder).getUserId()"
#参数名 == null 等表达式SpEL 支持的任意运算配合 condition / unless 做条件缓存

T() 是最容易被忽略但工程里最实用的一个:它打破了「key 只能从方法参数里取」的限制。

组合 key:工程实读逐项解释

项目的视频列表接口在 mf-app-server 的 SmallVideosService 中,工程终态代码:

//查询视频列表
@Cacheable(
        value="videos",
        key = "T(com.mf.server.interceptor.UserHolder).getUserId()+'_'+#page+'_'+#pagesize")  //userid _ page_pagesize
public PageResult queryVideoList(Integer page, Integer pagesize) {
    ......
}

逐项拆解:

  • value="videos":命名空间定为 videos,这条缓存在 Redis 里所有 key 都以 videos:: 开头。
  • T(com.mf.server.interceptor.UserHolder).getUserId():方法签名里只有 page 和 pagesize,没有 userId,登录用户 id 存在 ThreadLocal 里,SpEL 的 #参数名 够不着它。T() 静态调用在表达式求值时现场从 UserHolder 取当前线程的用户 id,解决了「key 依赖不在参数里」的问题。
  • +'_'+#page+'_'+#pagesize:把三个维度拼成一个字符串 key,例如 1 号用户看第 1 页、每页 10 条,最终 key 就是 videos::1_1_10。

为什么要拼复合 key?视频列表是「每个用户、每一页」各不相同的数据。如果 key 只用 #page,那么 1 号用户和 2 号用户请求第 1 页会拿到同一份缓存,A 用户刷到的视频 B 用户也能刷到,数据直接串号;如果只用 userId,翻到第 2 页时又会命中第 1 页的缓存。三个维度缺一不可,用下划线隔开拼成一个确定性 key,是「不同请求必须映射到不同缓存」的最直接表达。

分隔符本身也有讲究。写成 key = "#page + #pagesize" 会踩 SpEL 的算术陷阱:两个 Integer 相加执行的是数学加法,page=1, pagesize=10 得到 key videos::11,而 page=11, pagesize=0 同样得到 11,两组完全不同的参数拼出了相同的 key,缓存就串了。用字符串 '_' 做分隔,1_10 和 11_0 天然不同,既保证唯一性,又让 key 在 redis-cli 里肉眼可读。

key 的长度与可读性权衡

方案示例 key优点缺点
精简拼接videos::1_1_10短、省内存、redis-cli 里直观见 key 不知语义,需要文档约定
带语义前缀videos::userId1_page1_size10自解释,排障时不用查表长,大 key 量下占额外内存
参数哈希videos::a3f5c...长度恒定完全不可读,排查问题必须重算

列表类缓存条目多、淘汰频繁,key 留在 Redis 里的时间是按秒计的,精简拼接(工程采用的 userId_page_pagesize)是更划算的选择。关键维度少、生命周期长的配置类缓存,才值得用带语义的长 key。

RedisCacheManager 配置

工程实读:按 cacheName 分配 TTL

工程里确实有缓存配置类,位于 com.mf.server.config.RedisCacheConfig:

package com.mf.server.config;

import com.google.common.collect.ImmutableMap;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;

import java.time.Duration;
import java.util.Map;

@Configuration
public class RedisCacheConfig {

    //设置失效时间
    private static final Map<String, Duration> cacheMap;

    static {
        cacheMap = ImmutableMap.<String, Duration>builder().put("videos", Duration.ofSeconds(30L)).build();
    }

    //配置RedisCacheManagerBuilderCustomizer对象
    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
        return (builder) -> {
            //根据不同的cachename设置不同的失效时间
            for (Map.Entry<String, Duration> entry : cacheMap.entrySet()) {
                builder.withCacheConfiguration(entry.getKey(),
                        RedisCacheConfiguration.defaultCacheConfig().entryTtl(entry.getValue()));
            }
        };
    }
}

思路:用一个 Map 维护「cacheName → TTL」的对应关系,RedisCacheManagerBuilderCustomizer 在自动装配 RedisCacheManager 时逐个调用 withCacheConfiguration 注册每个命名空间的配置。以后新增一种缓存,只需往 cacheMap 里加一行,不用动配置逻辑。

videos 只给了 30 秒。这是个务实的选择:视频列表对实时性有一定要求(新视频要尽快出现),30 秒的脏窗口可接受,同时把高频刷新动作的数据库压力削掉一大截。

这个写法有个隐患:默认序列化是 JDK 序列化

RedisCacheConfiguration.defaultCacheConfig() 没有指定序列化器,value 默认走 JdkSerializationRedisSerializer。存进去的 PageResult 是一段二进制,redis-cli 里看就是乱码,机翻稿里「存储的数据类似乱码的形式」说的就是它。能用,但有三个代价:对象必须实现 Serializable(PageResult 和 VideoVo 都实现了,见下文)、存储体积大、跨语言不可读。

生产上更常见的做法是显式配置 JSON 序列化。下面给出一份完整可复制的增强版配置类,在工程原版基础上补齐序列化:

package com.mf.server.config;

import com.google.common.collect.ImmutableMap;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.Map;

@Configuration
public class RedisCacheConfig {

    //各命名空间的失效时间:cacheName -> TTL
    private static final Map<String, Duration> cacheMap;

    static {
        cacheMap = ImmutableMap.<String, Duration>builder()
                .put("videos", Duration.ofSeconds(30L))
                .build();
    }

    //通用配置:key 用字符串,value 用 JSON
    private RedisCacheConfiguration cacheConfiguration(Duration ttl) {
        return RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(ttl)
                //key 序列化为可读字符串,避免出现 \xac\xed\x00\x05 前缀
                .serializeKeysWith(
                        RedisCacheConfiguration.SerializationPair
                                .fromSerializer(new StringRedisSerializer()))
                //value 存 JSON:可读、体积小、不要求实现 Serializable
                .serializeValuesWith(
                        RedisCacheConfiguration.SerializationPair
                                .fromSerializer(new GenericJackson2JsonRedisSerializer()))
                //null 值不缓存,防止缓存穿透时把 null 也存进去
                .disableCachingNullValues();
    }

    @Bean
    public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
        return (builder) -> {
            //根据不同的 cacheName 设置不同的失效时间与序列化方式
            for (Map.Entry<String, Duration> entry : cacheMap.entrySet()) {
                builder.withCacheConfiguration(entry.getKey(),
                        cacheConfiguration(entry.getValue()));
            }
        };
    }
}

为什么选 GenericJackson2JsonRedisSerializer 而不是 JDK 序列化:

对比项JDK 序列化(默认)GenericJackson2JsonRedisSerializer
可读性二进制乱码JSON 文本,redis-cli 直接看
体积大(类元数据全量写入)小
对象要求必须实现 Serializable无硬性要求
反序列化强依赖原类路径GenericJackson2Json 在 JSON 里携带 @class 类型信息,能还原原类型
跨服务/跨语言仅 Java 之间任何能读 JSON 的端都能解析

换 JSON 序列化后有一点要留意:缓存对象里如果只有无参构造缺失或字段类型多态,反序列化可能失败,所以返回值尽量用结构稳定的 VO(VideoVo 这种字段固定的类正合适)。

写时失效:发布视频时清缓存

@Cacheable 只管「读的时候放进去」,不管「数据变了怎么办」。用户发布一条新视频后,如果不清缓存,所有人要在 TTL 窗口内(工程里是 30 秒)继续刷旧列表。常见做法是在发布方法上挂 @CacheEvict:

/**
 * 发布小视频
 */
@CacheEvict(value="videos", allEntries = true)  //清空缓存
public void saveVideos(MultipartFile videoThumbnail, MultipartFile videoFile) throws IOException {
    //上传 FastDFS、OSS,构建 Video 对象,调用 videoApi.save(video) 持久化
    ......
}

allEntries = true 的含义是清空 videos 命名空间下的全部条目,而不是某一条。为什么必须全清?发布新视频影响的是「所有用户的推荐列表」:新视频会出现在每个用户的第 1 页,缓存里的 key 有 videos::1_1_10、videos::2_1_10、videos::3_2_15……如果要精确失效,发布方根本算不出有哪些用户看过列表、各自停在第几页,key 的组合空间是发散的,逐个清不现实。全清虽然粗暴,但保证一致性,代价只是发布后第一批请求回源数据库。

工程快照说明:当前 mf 工程的 saveVideos 上实测没有这个注解,上述写法以规范做法为准。照抄时记得补上,否则「发布后 30 秒内列表不刷新」会成为一个偶现且难解释的 bug。
失效粒度的取舍:

粒度写法适用场景代价
精确 key@CacheEvict(value="videos", key="#userId")单条数据被定点修改,且 key 可以由参数算出key 带组合维度时算不全,漏清就脏
全部失效@CacheEvict(value="videos", allEntries=true)写操作影响整个集合(发布、删除、推荐位调整)一次写打掉整个命名空间,命中率瞬时下跌
TTL 兜底entryTtl(Duration.ofSeconds(30))长尾不一致可容忍、写入方无法感知失效时机TTL 窗口内是脏数据,纯属被动等待

三种粒度不是互斥的,工程里的常见组合是「精确失效为主、TTL 兜底」:能算出 key 的就精确清,算不全的依赖 TTL 自然过期,videos 这种发散 key 场景则退化为「全清 + TTL」双保险。

视频列表落点预演:queryVideoList 全解

把工程实读的完整方法贴出来,看缓存注解如何套在一段真实的「三段开销」查询上:

//查询视频列表
@Cacheable(
        value="videos",
        key = "T(com.mf.server.interceptor.UserHolder).getUserId()+'_'+#page+'_'+#pagesize")  //userid _ page_pagesize
public PageResult queryVideoList(Integer page, Integer pagesize) {

    //1、查询redis数据(推荐视频的vid列表)
    String redisKey = Constants.VIDEOS_RECOMMEND +UserHolder.getUserId();
    String redisValue = redisTemplate.opsForValue().get(redisKey);
    //2、判断redis数据是否存在,判断redis中数据是否满足本次分页条数
    List<Video> list = new ArrayList<>();
    int redisPages = 0;
    if(!StringUtils.isEmpty(redisValue)) {
        //3、如果redis数据存在,根据VID查询数据
        String[] values = redisValue.split(",");
        //判断当前页的起始条数是否小于数组总数
        if( (page -1) * pagesize < values.length) {
            List<Long> vids = Arrays.stream(values).skip((page - 1) * pagesize).limit(pagesize)
                    .map(e->Long.valueOf(e))
                    .collect(Collectors.toList());
            //5、调用API根据VID数组查询视频数据
            list = videoApi.findMovementsByVids(vids);
        }
        redisPages = PageUtil.totalPage(values.length,pagesize);
    }
    //4、如果redis数据不存在,分页查询视频数据
    if(list.isEmpty()) {
        //page的计算规则:传入的页码 -- redis查询的总页数
        list = videoApi.queryVideoList(page - redisPages, pagesize);
    }
    //5、提取视频列表中所有的用户id
    List<Long> userIds = CollUtil.getFieldValues(list, "userId", Long.class);
    //6、批量查询用户信息
    Map<Long, UserInfo> map = userInfoApi.findByIds(userIds, null);
    //7、构建返回值
    List<VideoVo> vos = new ArrayList<>();
    for (Video video : list) {
        UserInfo info = map.get(video.getUserId());
        if(info != null) {
            VideoVo vo = VideoVo.init(info, video);
            vos.add(vo);
        }
    }

    return new PageResult(page,pagesize,0l,vos);
}

这段方法体本身就有三段开销:MongoDB 分页查询、按 userId 批量查 MySQL 用户信息、读 Redis 推荐列表做状态判断(前文「查询视频列表:代码实现」里已详细分析)。@Cacheable 套在最外层,等于给整段方法体做了一层 membrane:命中时方法体一行都不执行,三段开销全部归零;未命中时才完整跑一遍,把最终 PageResult 写进 videos::userId_page_pagesize。这套配置就是为视频列表量身服务的,读多写少、按用户和页维度天然分片、单条 30 秒内允许不变。
缓存对象的可序列化也在工程里有对应:PageResult 和 VideoVo 都声明了 implements Serializable。即使后续把序列化方式换成 JSON,这个声明保留着也无害;但若走默认 JDK 序列化,漏了它就直接抛 NotSerializableException。

工程结构中与这里相关的文件位置:

mf-app-server/src/main/java/com/mf/server/
├── AppServerApplication.java          # @EnableCaching 开启缓存支持
├── config/
│   └── RedisCacheConfig.java          # videos 命名空间 TTL=30s
├── interceptor/
│   └── UserHolder.java                # ThreadLocal 存登录用户,key 的 T() 调用目标
└── service/
    └── SmallVideosService.java        # queryVideoList @Cacheable / saveVideos 发布入口
mf-model/src/main/java/com/mf/model/vo/
├── PageResult.java                    # implements Serializable
└── VideoVo.java                       # implements Serializable

缓存命中与未命中的两条路径

Mongo/MySQL queryVideoList RedisCacheManager CacheInterceptor(AOP) Controller Mongo/MySQL queryVideoList RedisCacheManager CacheInterceptor(AOP) Controller alt [缓存命中] [缓存未命中] queryVideoList(page=1, pagesize=10) 求值 SpEL:UserHolder.getUserId()+'_'+ GET videos::1_1_10 PageResult(JSON/JDK 反序列化) 直接返回,方法体未执行 null 执行方法体 读推荐 vid 列表(VIDEOS_RECOMMEND_userId) Mongo 分页 + MySQL 批查用户 返回 PageResult SET videos::1_1_10 (TTL 30s) 返回结果

发布视频那条线则在另一个切面上:saveVideos 先执行入库,方法正常返回后 AOP 清空 videos::* 全部条目,下一次任何人刷列表都是回源重建。

踩坑清单

坑现象规避
key 拼接遗漏或误用 +不同参数拼出相同 key,缓存串了(1_10 与 11_0 都变 11)组合维度之间必须插入字符串分隔符 +'_'+,拼完先在 redis-cli 里抽查 key 形态
只配 TTL 不做写时失效发布新视频后 30 秒内列表不刷新,偶现难复现写方法上补 @CacheEvict,TTL 只作兜底
只做失效不配 TTL某次 evict 异常或遗漏后,脏缓存永不消失每个命名空间都给 entryTtl
缓存对象不可序列化抛 NotSerializableException,或 JSON 反序列化失败返回值用实现 Serializable、结构稳定的 VO(PageResult/VideoVo 均已实现)
同类自调用同一个类里 this.queryVideoList() 调用,注解完全不生效自调用不走 AOP 代理,拆到另一个 Bean 或注入自身代理(概念篇已展开)
ThreadLocal 时机问题T(UserHolder).getUserId() 报 NPE拦截器未把用户放入 ThreadLocal(登录校验没走到)就调了缓存方法,检查拦截器路径
默认序列化乱码redis-cli 里 value 是二进制按上文配置 GenericJackson2JsonRedisSerializer

可复现验证

以 spring-cache-demo 工程为例(项目工程同理,把 key 换成组合 key 即可)。

第一步,验证两次调用第二次命中:

@SpringBootTest
class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    void testCache() {
        userService.findById(1L);  //第一次:查库并写入 Redis
        userService.findById(1L);  //第二次:命中缓存,SQL 日志不再出现
    }
}

开启 SQL 日志或断点在 Dao 层,第二次调用不会有任何数据库访问。

第二步,发布视频后观察失效(项目工程):

# 发布前:能看到组合 key 形态
redis-cli keys "videos::*"
# 输出示例:1) "videos::1_1_10"  2) "videos::1_2_10"  3) "videos::2_1_10"

# 查看 TTL
redis-cli ttl videos::1_1_10
# 输出:28(随时间递减,上限 30)

# 调用发布视频接口后
redis-cli keys "videos::*"
# 输出:(empty) —— allEntries=true 已全清

注意 keys 命令仅适合排查环境,生产上用 scan videos::* count 100 代替。

常见误解澄清

误解事实
value 属性是缓存的值value 是命名空间(cacheName),真正缓存值是方法返回值
key 不写就默认用返回值做 key默认用 SimpleKey.EMPTY,无参方法共享一条缓存,多参方法用参数对象组合
@Cacheable 注解加上就生效必须有 @EnableCaching,且调用要经过 Spring 代理
过期时间由注解控制SpringCache 注解本身没有 TTL 属性,过期由 CacheManager 层(RedisCacheConfiguration.entryTtl)决定
redis-cli 里 value 乱码是 bug默认 JDK 序列化的正常表现,想可读就显式换 JSON 序列化器
T() 是工具类导入是 SpEL 的静态调用语法,与 Java 的 import 无关

API 速览

属性所属注解作用示例
value / cacheNames两者通用命名空间,Redis key 前缀value="videos"
key两者通用SpEL 表达式生成 key"T(...).getUserId()+'_'+#page+'_'+#pagesize"
allEntries@CacheEvicttrue 时清空整个命名空间allEntries=true
condition两者通用方法执行前判断,false 则不走缓存condition="#pagesize <= 50"
unless@Cacheable方法执行后判断,true 则不写缓存unless="#result.rows.isEmpty()"
entryTtlRedisCacheConfiguration该命名空间的过期时间entryTtl(Duration.ofSeconds(30))

官方文档

总结

  • SpringCache 的 key = cacheName::SpEL求值结果。组合 key 用字符串分隔符拼接,既防维度碰撞又防 SpEL 数字相加;key 依赖不在参数里的上下文(如 ThreadLocal 中的用户)时,用 T(类).静态方法() 现场取。
  • TTL 和序列化都不在注解上配,而在 RedisCacheManager 层:工程用 RedisCacheManagerBuilderCustomizer 按 cacheName 注册差异化 TTL(videos 为 30 秒);默认 JDK 序列化建议换成 GenericJackson2JsonRedisSerializer,可读、省内存、不强制 Serializable。
  • 失效粒度按「能否算出 key」决定:算得清就精确清,算不清(如发布视频影响所有用户的列表)就 allEntries=true 全清,TTL 永远留着兜底。
  • 项目视频列表是这套配置的标准受益者:三段开销的查询被一层 @Cacheable 整体包住,命中即归零。

下一篇同章的通用缓存SpringCache:常用注解会把 @CachePut、@Caching、条件缓存这些注解逐一展开,spring-cache-demo 的 UserService 里已经能看到 @CachePut 和 @Caching 组合清除的身影,正好承接。

亚马逊(Amazon ads)广告API申请步骤(已经更新完) 你如果是自己的店铺使用,就选择左边的、如果是授权的那种给别人用就选右边的。审核通过了才能进行下一步,我目前就是刚申请完,后续会持续更新!然后会调整到,下面的页面,选择Agency然后右上角点击创建。审核通过会发邮件到你的企业邮箱、时间大概是1-7天左右。输入对应的信息,点击提交审核,就可以开始申请了。1、广告平台跟卖家平台是分开的,这个概念要清楚。2、注册广告一定要用企业邮箱!回到首页,点击成为合作伙伴。注册了以后会跳转到广告。然后再点击加入合作伙伴。然后会弹出下面的页面。 阅读详情

相关推荐

智能车竞赛轮腿穿越组:GPS+IMU导航绕桩实战技巧(附避坑指南)

本文详细介绍了智能车竞赛轮腿穿越组中GPS+IMU导航绕桩的实战技巧,包括硬件选型、传感器标定、路径规划及多传感器数据融合策略。通过实际案例和避坑指南,帮助参赛队伍提升导航精度和稳定性,避免常见错误。特别强调IMU安装位置和标定对导航系统的重要性。

gin88的博客 584

java map 模糊匹配key_SpringCache扩展@CacheEvict的key模糊匹配清除

之前就一直集成SpringCache+redis缓存数据,这次项目中的缓存一直有缓存清除失效的问题,我就花时间好好梳理了一番,主要是针对@CacheEvict的key模糊匹配支持。熟悉redis命令的人都知道,查询和删除都可以做模糊匹配,所以我就想让SpringCache也支持模糊匹配清除。起因是对外发布的API接口做了缓存,API需要支持分页,所以在@Cacheable的key中用current...

weixin_39810196的博客 1338

S7-1200g2运动控制实战:从轴组配置到程序调试

本文详细介绍了S7-1200g2运动控制功能的实战应用,从轴组配置到程序调试的全流程。通过TIA Portal中的工艺对象配置,实现单轴、同步轴组等运动控制模型,并分享常见问题排查与性能优化经验,帮助工程师快速掌握中小型自动化设备的运动控制技术。

weixin_28698059的博客 588

Springboot 整合 SpringCache

简单整理一下 `SpringCache + Redis`的使用

博客 961

深入剖析QWEN 2.5模型架构:从GQA到RoPE的技术实现

本文深入解析了QWEN 2.5模型架构,重点介绍了其创新的GQA(分组查询注意力)和RoPE(旋转位置编码)技术实现。通过详细代码解读,展示了这些技术如何提升模型的计算效率和长文本处理能力,适用于代码生成和数学推理等任务。文章还提供了实践部署建议和调优经验,帮助开发者更好地应用这一先进模型。

weixin_42548816的博客 390

逆向分析顶象5代各类验证码类型的图片还原方法(附源码)

6.刷新滑块验证码图片,会进入之前的for循环断点,分析此处的代码会发现,该循环循环了处理了n字符串的值,取出n字符中的值并使用charCodeAt生成Unicode 编码,然后又调用了a函数,如果a函数返回true就会把之前生成的Unicode编码+1,a函数参数分别是c数组和Unicode取余32后的值,然后再把Unicode取余32后的值push进c数组,这里的n值为滑块碎片背景图的name。2.搜索"ranges":,会找到ranges赋值的地方,在此处代码断点,并点击跳过断点,结束此次调试。

老衲爱洗头的博客 1195

cachemanage管理_spring使用RedisCacheManager管理key的一些问题

spring可以很好地管理各种内存的快速缓存。这些常见的内存缓存库实现方式有redis,Ehcache。本文阐述的是redis,毕竟这个东西相当容易使用。spring通过 org.springframework.cache.Cache 和org.springframework.cache.CacheManager两个接口来管理缓存redis的cache实现类是 RedisCacheManager,...

weixin_35629057的博客 1374

基于STM32L4XX、HAL库的MCP4561数字电位器驱动程序设计

/ HAL库需要左移一位。* @param address: 设备地址(0x2C-0x2F)* @param hmcp: MCP4561句柄。* @param hmcp: MCP4561句柄。* @param hmcp: MCP4561句柄。* @param hmcp: MCP4561句柄。* @param hmcp: MCP4561句柄。* @param hmcp: MCP4561句柄。* @param hmcp: MCP4561句柄。* @param hi2c: I2C句柄。

zhusl6688的博客 340

SpringBoot配置RedisCacheManager

Redis数据库做缓存,已经是比较常见的缓存技术的解决方案。就会得到缓存实例,在使用get方法,传参为key和value的类对象。2.缓存配置,配置的是个列表,keyName为Cache实例,过期时间为3600,1800,时间单位在配置类,设置具体过期时间看业务定。总结,这样配置RedisCacheManger的好处就是,每当由新接口需要做缓存时,只需要在yml配置文件中新增个缓存来对应这个接口,这样多个缓存接口都有一个Cache实例,互不影响,就算key相同,但是他们的缓存名字不同,是不会有影响的。

qq_50930205的博客 1426

AI 辅助编程全景:演进、提效场景与未来趋势

本文系统梳理了AI辅助编程的发展历程与应用场景。演进路径分为三代:从代码补全工具(2023)、对话式助手(2024)到自主编程代理(2025)。在软件全生命周期中,AI可显著提升各环节效率:需求分析阶段节省30-60%文档编写时间;设计阶段缩短40-60%技术调研周期;编码阶段通过智能补全提升55%任务速度;测试阶段减少30-60%测试编写时间。数据显示,2025年84%开发者使用AI工具,41%代码由AI生成,平均每周节省3.6小时。AI正推动开发者角色从编写者转变为审核者,但复杂业务逻辑和架构设计仍需人

甘蓝的专栏 472

使用 MATLAB/Simulink + Simscape Electrical + Simulink Design Optimization,构建一个基于SiC/GaN的Z源逆变器在储能系统中的软启

《手把手教你学Simulink:Z源逆变器在储能系统中的应用》摘要 本文详细介绍了使用MATLAB/Simulink构建Z源逆变器储能系统仿真模型的全过程。Z源逆变器凭借其独特的阻抗网络结构,解决了传统逆变器在直通保护和升压能力方面的痛点。文章通过6个关键步骤:主电路搭建、调制策略设计、软启动实现、仿真分析、效率对比和工程问题解决方案,完整展示了Z源逆变器从理论到仿真的实现路径。重点讲解了直通零矢量SPWM调制策略和渐进式升压比控制方法,验证了系统93.3%的效率和优异的抗扰性能。该模型为光伏储能、电动汽车

MHD0815的博客 122

SpringBoot集成SpringCache

最近项目中开发测试中,总监对我们开发人员提出了一个疑问,服务器也给你们加了,怎么我访问还是很慢! 我以此开始进入优化之路,无法自拔. 前言: 做过开发的程序员都是,程序的瓶颈在于数据库,数据库是需要磁盘IO,我们也知道内存的速度远远快于磁盘的.当访问的数据是相同和重复调用的,一次又一次的请求数据库或者远程服务,导致大量时间耗费在数据库查询或远程方法访问上,如果同时大量请求过来,也会导致服务的的宕机等不可避免的问题.导致性能的恶化,这边是数据缓存要解决的问题. 缓存数据库: 提到缓存数据库,当然少不

qq_42390636的博客 413

大模型复杂问答能力评测框架:从原理到实践

在人工智能领域,大语言模型(LLM)的评估是衡量其实际应用价值的关键环节。其核心原理在于通过系统化的评测框架,对模型的理解、推理、整合与表达能力进行量化分析。传统的NLP基准测试往往侧重于事实性检索或简单推理,难以全面反映模型在应对多跳推理、隐含条件、长上下文整合等复杂现实任务时的真实性能。因此,专门针对复杂问答设计的评测技术应运而生,其价值在于为模型选型、效果优化和瓶颈诊断提供数据驱动的科学依据。该技术广泛应用于需要深度逻辑判断的业务场景,如智能投顾、法律咨询、医疗诊断等。本文聚焦的`tan92hl/Co

weixin_28679635的博客 362

Springboot整合SpringCache+Redis

Springboot整合SpringCache+Redis

SampsonKong的博客 2648

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

SpringCache详解(尚硅谷谷粒商城)

原理:CacheManager(RedisCacheManager) --> Cache(RedisCache) --> Cache负责缓存的读写。常规数据(读多写少,即时性,一致性要求不高的数据),完全可以使用spring-cache, 写模式(只要缓存的数据有过期时间就足够了)缓存穿透:查询一个null数据 解决:缓存空数据:spring.cache.redis.cache-null-values=true。---> 初始化所有缓存 --> 每个缓存决定使用什么配置。Spring-Cache的不足。

qq_60313016的博客 708

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

自定义RedisCacheManager

自定义RedisCacheManager ​ 在项目的Redis配置类RedisConfig中,按照上一步分析的定制方法自定义名为cacheManager的Bean组件 @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { // 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换 RedisSerializer strSerializer = n

weixin_48320264的博客 298

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

记一下Shiro重构之RedisCacheManager

package com.ccb.web.shiro; import com.ccb.web.configs.CsRedisUtil; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheExcep...

程序员朱永胜 1303

【Java】java | CacheManager | redisCacheManager | @Cacheable失效

1、查询增加缓存,使用@Cacheable注解2、项目中已经用到了ehcache,现在需求是两个都用二、备份配置1、redisConfig增加代码。

hgSuper的博客 985

redis缓存失效时间设为多少_配置redis缓存失效时间

最近想利用redis缓存做一个简陋版的类似腾讯新闻这样的查看新闻的网页。用了缓存以后,添加新闻缓存没有更新,想使用缓存的失效时间做到数据库缓存一致性。刚开始做的时候认为使用@CachePut注解会起到更新缓存的作用,设置了cacheName和key都和查找方法中的@Cacheable中的key和cacheName的一样,然而并没有成功,反而是被替换了,想想hashMap就能理解这个问题了。如何设置...

weixin_39943000的博客 2254

springBoot2 下配置springCache redis 缓存配置及使用

学习内容: springCache + redis 缓存 例如: 1、 springBoot2 配置springCache + redis 缓存生存周期 + 配置redis 序列化方式 Json 2、 配置SpringCache Key自定义Key 下篇学习: 1、 统一配置springCache 缓存redis expireTIme yml配置 及如何定制个性化时间 2 springCache 注解详细解答 3, 提供demo git 代码 学习时间: 提示:这里可以添加计划学习的时间 例如:

ywtech的博客 1344

优雅的缓存框架:SpringCache增强支持自定义过期时间TTL

基于SpringCache缓存框架下动态设置TTL缓存过期时间

weixin_42973338的博客 1023

Java 项目实战: 外卖平台优化-使用Redis作为缓存产品与TTL配置

本文介绍从内存缓存切换至Redis缓存的优化实践。通过引入spring-boot-starter-cache与spring-boot-starter-data-redis,实现分布式缓存,解决重启丢失、数据不一致、无过期机制等问题。配置spring.cache.redis.time-to-live设置全局过期时间(如30分钟),并强调condition需用unless替代,避免因方法未执行导致缓存失效。实测验证缓存键格式为userCache::1502198504921124865,且@CacheEvict

Wang的专栏 460

Springboot使用@Cacheable 更优雅的使用缓存 以及如何设置失效时间

可以自定义一个cacheManager, 如下图的RedisCacheManager该构造方法需要3个参数, 分别是我的redis 缓存相关 具体配置文件, 如下, 可直接拷贝使用@Bean/**默认失效时间配置@param days 未设置失效事件的key 默认days失效*//***/

m0_61549740的博客 2611

自定义RedisTemplate和RedisCacheManager

默认的RedisTemplate和RedisCacheManager的使用JDK的序列化方式,可以自定义为string和json序列化 @Configuration public class MyRedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws Unknow

weixin_43631820的博客 2265
上一篇: Java 项目实战: 交友项目 - 通用缓存SpringCache:介绍以及入门案例
下一篇: Java 项目实战: 交友项目 - SpringCache 常用注解的深度用法与易错点
Wang's Blog
博客等级 码龄12年 7418粉丝 2407原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值