发布时间:2026/9/3 18:34:44
专项攻克——重写 Redis 依赖包方法的 6 种实现方式 文章目录方式一继承重写最简单优先考虑示例重写 Jedis 的 set 方法示例重写 Spring Data Redis 的 RedisTemplate方式二装饰器模式组合比继承更灵活示例包装 RedisTemplate 的 opsForValue方式三Spring AOP 切面拦截无侵入推荐用于增强示例拦截 RedisTemplate 的所有 set 操作方式四同包同类名覆盖类加载优先级黑科技示例覆盖 Jedis 的某个类方式五Java Agent 字节码增强终极方案示例用 ByteBuddy 拦截 Jedis 的 set 方法方式六动态代理JDK 动态代理 / CGLIB示例JDK 动态代理包装 JedisCommands 接口方案选择决策树面试口述标准答案先确认你用的是哪个 Redis 客户端Jedis / Lettuce / Spring Data Redis / Redisson不同方式适用场景不同。下面按推荐优先级逐一给出可落地代码。方式一继承重写最简单优先考虑适用条件目标类不是final目标方法不是final/static/private。示例重写 Jedis 的set方法import redis.clients.jedis.Jedis; public class CustomJedis extends Jedis { public CustomJedis(String host, int port) { super(host, port); } Override public String set(String key, String value) { // 前置增强比如加日志、统计、权限校验 System.out.println(自定义set方法被调用key key); // 调用原方法 String result super.set(key, value); // 后置增强 System.out.println(set执行结果 result); return result; } }使用try (CustomJedis jedis new CustomJedis(localhost, 6379)) { jedis.set(key1, value1); // 走你重写的方法 }示例重写 Spring Data Redis 的 RedisTemplateimport org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; public class CustomRedisTemplateK, V extends RedisTemplateK, V { Override public ValueOperationsK, V opsForValue() { // 包装一层对 opsForValue 返回的对象做增强 return new CustomValueOperations(super.opsForValue()); } }坑点如果类是final比如 Lettuce 的很多类此方式不可用如果方法是static不能重写只能隐藏不推荐框架内部如果直接 new 原类而不是用你的子类重写不生效方式二装饰器模式组合比继承更灵活适用条件目标类是final无法继承或只想增强部分方法。示例包装 RedisTemplate 的 opsForValueimport org.springframework.data.redis.core.ValueOperations; import java.util.concurrent.TimeUnit; public class CustomValueOperationsK, V implements ValueOperationsK, V { private final ValueOperationsK, V delegate; // 被包装的原对象 public CustomValueOperations(ValueOperationsK, V delegate) { this.delegate delegate; } Override public void set(K key, V value) { System.out.println(装饰器拦截 setkey key); delegate.set(key, value); // 委托给原对象 } Override public Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit) { System.out.println(装饰器拦截 setIfAbsentkey key); return delegate.setIfAbsent(key, value, timeout, unit); } // 其余方法全部委托给 delegate不做改动 Override public V get(Object key) { return delegate.get(key); } Override public Long increment(K key) { return delegate.increment(key); } // ... 接口里所有方法都要实现逐个委托 }Spring 中注入使用Configuration public class RedisConfig { Bean public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(factory); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } // 直接暴露包装后的 ValueOperations Bean public ValueOperationsString, Object customValueOperations(RedisTemplateString, Object redisTemplate) { return new CustomValueOperations(redisTemplate.opsForValue()); } }坑点接口方法很多时实现起来繁琐可以用 LombokDelegate简化如果有多个重载方法每个都要单独处理方式三Spring AOP 切面拦截无侵入推荐用于增强适用条件只想加日志、监控、缓存、权限等横切逻辑不改原方法核心逻辑。示例拦截 RedisTemplate 的所有 set 操作import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; Aspect Component public class RedisMethodAspect { // 拦截 RedisTemplate 中所有以 set 开头的方法 Around(execution(* org.springframework.data.redis.core.RedisTemplate.set*(..))) public Object aroundSetMethods(ProceedingJoinPoint pjp) throws Throwable { long start System.currentTimeMillis(); String methodName pjp.getSignature().getName(); Object[] args pjp.getArgs(); System.out.println([Redis AOP] 方法 methodName , 参数 java.util.Arrays.toString(args)); try { Object result pjp.proceed(); // 执行原方法 long cost System.currentTimeMillis() - start; System.out.println([Redis AOP] 执行成功耗时 cost ms); return result; } catch (Throwable e) { System.out.println([Redis AOP] 执行失败: e.getMessage()); throw e; } } // 也可以拦截具体某个方法 Around(execution(* org.springframework.data.redis.core.ValueOperations.set(..))) public Object aroundValueSet(ProceedingJoinPoint pjp) throws Throwable { // 你的增强逻辑 return pjp.proceed(); } }坑点Spring AOP 只能拦截 Spring 容器管理的 Bean自己new出来的对象不生效只能拦截public方法CGLIB 可以拦截 protected但不推荐同类内部方法调用this.xxx()不会触发 AOP如果要完全替换方法逻辑不调用原方法不调用pjp.proceed()即可但要自己返回正确类型方式四同包同类名覆盖类加载优先级黑科技适用条件目标类是final 方法是final且你想彻底替换整个类的实现。原理在你的项目源码中创建一个包名、类名完全相同的类编译后你的 class 会优先被类加载器加载因为 target/classes 在 classpath 中排在依赖 jar 前面。示例覆盖 Jedis 的某个类假设要覆盖redis.clients.jedis.Jedis类// 在你的项目 src/main/java 下创建完全相同的包路径 package redis.clients.jedis; // 包名必须完全一致 public class Jedis { // 类名必须完全一致 // 重写整个类的实现或者只改你要的方法 // 注意这个类会完全替换 jar 包里的 Jedis 类 public String set(String key, String value) { System.out.println(完全替换的set方法); // 你的实现... return OK; } // 其他方法也要实现否则调用会报错 }坑点极多慎用这是完全替换不是增强原类的所有方法你都得自己实现依赖包升级后原类结构变了你的覆盖类可能直接报错团队协作时其他人很难发现这个黑科技维护成本极高如果有类加载器隔离比如 Tomcat、OSGi可能不生效除非万不得已否则不要用这种方式方式五Java Agent 字节码增强终极方案适用条件需要在运行时动态修改任意类包括 final 类、JDK 类且无法通过其他方式实现。工具ByteBuddy推荐、ASM、Javassist示例用 ByteBuddy 拦截 Jedis 的 set 方法import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.asm.Advice; import net.bytebuddy.matcher.ElementMatchers; import java.lang.instrument.Instrumentation; public class RedisAgent { public static void premain(String agentArgs, Instrumentation inst) { new AgentBuilder.Default() .type(ElementMatchers.named(redis.clients.jedis.Jedis)) .transform((builder, typeDescription, classLoader, module, protectionDomain) - builder.method(ElementMatchers.named(set) .and(ElementMatchers.takesArguments(String.class, String.class))) .intercept(Advice.to(RedisSetAdvice.class)) ) .installOn(inst); } }import net.bytebuddy.asm.Advice; public class RedisSetAdvice { Advice.OnMethodEnter public static void enter(Advice.Argument(0) String key, Advice.Argument(1) String value) { System.out.println([ByteBuddy] 进入set方法key key , value value); } Advice.OnMethodExit(onThrowable Exception.class) public static void exit(Advice.Return String result, Advice.Thrown Throwable throwable) { if (throwable ! null) { System.out.println([ByteBuddy] set方法异常: throwable.getMessage()); } else { System.out.println([ByteBuddy] set方法返回: result); } } }使用方式# 打成 agent jar启动时附加 java -javaagent:redis-agent.jar -jar your-app.jar坑点复杂度高需要理解字节码和类加载机制调试困难出问题很难排查对 JDK 版本敏感不同 JDK 版本的字节码格式可能有差异一般只有中间件、APM 工具如 SkyWalking、Pinpoint才会用方式六动态代理JDK 动态代理 / CGLIB适用条件目标类实现了接口用 JDK 动态代理没有接口用 CGLIB。示例JDK 动态代理包装 JedisCommands 接口import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCommands; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class RedisProxyFactory { public static JedisCommands createProxy(Jedis jedis) { return (JedisCommands) Proxy.newProxyInstance( jedis.getClass().getClassLoader(), new Class?[]{JedisCommands.class}, new RedisInvocationHandler(jedis) ); } static class RedisInvocationHandler implements InvocationHandler { private final Object target; public RedisInvocationHandler(Object target) { this.target target; } Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (set.equals(method.getName())) { System.out.println([动态代理] 拦截set方法参数 java.util.Arrays.toString(args)); } return method.invoke(target, args); } } }使用Jedis jedis new Jedis(localhost, 6379); JedisCommands proxy RedisProxyFactory.createProxy(jedis); proxy.set(key, value); // 走代理坑点JDK 动态代理只能代理接口不能代理具体类CGLIB 不能代理 final 类和 final 方法代理对象和原对象是两个对象框架内部如果用原对象代理不生效方案选择决策树你想做什么 ├── 只加日志/监控/统计等横切逻辑 → 方式三Spring AOP推荐 ├── 改方法逻辑类和方法都不是final → 方式一继承重写最简单 ├── 类是final或只想增强部分方法 → 方式二装饰器模式 ├── 目标有接口 → 方式六动态代理 ├── 类是final 方法是final必须彻底替换 → 方式四同包同类名覆盖慎用 └── 运行时动态修改无法通过其他方式 → 方式五Java Agent ByteBuddy面试口述标准答案重写 Redis 依赖包的方法按场景有六种方式。最常用的是继承重写如果类不是 final、方法不是 final直接继承后 Override 就行。如果类是 final 或者只想增强部分方法用装饰器模式把原对象组合进来接口方法逐个委托。如果只是加日志、监控这类横切逻辑用 Spring AOP 拦截最方便无侵入。如果目标有接口可以用 JDK 动态代理没有接口用 CGLIB。极端情况下类和方法都是 final只能用同包同类名覆盖但这个是黑科技维护成本高不推荐。终极方案是 Java Agent 加 ByteBuddy 做字节码增强一般只有 APM 工具才会用。实际项目里 90% 的场景用继承或装饰器就能解决。

相关新闻

2026/9/3 18:34:44

基于SSM与微信小程序的宿舍报修系统设计与实战解析

简介:这是基于微信小程序设计的宿舍报修系统毕业源码案例,后端采用SSM架构,Java语言编码,Mysql创建数据表,面向计算机相关专业学生及小程序开发者,可用于毕业设计、课程实践或项目参考。压缩包共664个文件&…

2026/9/3 18:29:44

Codex与Claude Code同题实战:谁更胜任AI编程工程交付?

前段时间做 AI 编程工具选型时,我一直习惯用“点状测试”来比较 Codex 和 Claude Code:让 Codex 补一个函数,让 Claude Code 修一个 Bug,再手动对比一次差异。这种测试其实参考价值很有限,因为任务范围太小&#xff0c…

2026/9/3 18:29:44

整车控制器(VCU)源代码开发实战:从工程架构到量产安全

简介:整车控制器(VCU)开发源代码资料包面向新能源汽车电控开发者、嵌入式工程师及相关专业学生,涵盖VCU核心控制算法、驱动与状态机逻辑、故障处理机制及软件架构,配套软件说明书详解功能描述、编程接口与调试方法&…

2026/9/3 19:29:54

同人预告片制作指南:基于FFmpeg的合成、验证与发布流程

看到“声波宇宙第18集「同人预告」”这样的标题时,观众最先记住的是“敬请期待”四个字。但把它放到工程视角里,这四个字背后是一条完整的制作链路:第18集的内容还没有上线,预告片要用哪些素材、按什么规格合成、配什么字幕、用什…

2026/9/3 19:29:54

M4 Max 128GB跑DeepSeek V4 Flash Q2:128K上下文实战记录

之前一直想在本地跑一个能处理超长上下文的大模型,把几份几十页的文档一次性丢进去做分析,但试过不少方案,要么内存不够,要么加载到一半系统直接卡死。最近在 M4 Max 128GB 这台机器上,把 DeepSeek V4 Flash 的 Q2 量化…

2026/9/3 19:29:54

沉浸式打包怎么做?从标准流程到高温防护的完整拆解

立秋都过了,天气还是三十多度,我坐在工作台前准备拍一期“沉浸式打包|李泽言set”的视频素材。结果光是整理包装材料,就已经开始出汗。这一刻我突然意识到,“不是立秋了吗怎么还是这么热”这句话,根本不只是…

2026/9/3 19:24:54

构建水面漂浮物检测数据集:从数据痛点分析到模型实战部署

简介:本资源是面向计算机视觉初学者与环保AI应用研究者的水面漂浮物目标检测数据集,聚焦水面垃圾识别这一实际环境监测需求,适用于YOLO、Faster R-CNN等主流目标检测模型的训练与验证。数据集共2400张图像(压缩包内含2000个XML标注…

2026/9/3 18:28:26

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/3 14:29:47

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/3 14:30:35

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/3 0:02:06

零基础装 OpenClaw 小龙虾 AI:Windows 一键部署教程与避坑要点

Windows 部署 OpenClaw 完整教程|本地 AI 智能体 5 分钟落地,环境配置一次搞定 版本说明:Windows 3.1.0 / Mac 2.7.9 写在前面 近两年开源 AI 领域有一款被称作「数字员工」的工具持续走热,它就是 OpenClaw,圈内人更习…

2026/9/3 0:02:06

Hermes Agent 本地部署新方案:Windows 整合包减少依赖报错

Windows 本地部署 Hermes 太麻烦?这版一键包 5 分钟快速跑通 很多人想体验 Hermes Agent,但真正开始部署时,往往会卡在环境配置这一步。 需要安装各类依赖、调试运行环境、处理路径问题,还容易遇到命令行报错、系统拦截、文件缺…

2026/9/3 0:02:06

实测 OpenClaw 一键包,5 分钟完成本地自动化环境搭建

OpenClaw 本地 AI 自动化工具部署指南|使用一键包规避环境配置难题 痛点:部署 AI 自动化工具常常要处理 Python、Node.js 各类依赖,版本冲突、环境配置耗费大量时间,OpenClaw 提供一键安装包,降低部署门槛。 适配系统&…

2026/9/2 1:15:22

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/3 17:51:43

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/2 1:15:20

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…