曹工说Spring Boot源码(20)-- 码网灰灰,疏而不漏,如何记录Spring RedisTemplate每次操作日志

曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享

曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解

曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下

曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean definition的?

曹工说Spring Boot源码(5)-- 怎么从properties文件读取bean

曹工说Spring Boot源码(6)-- Spring怎么从xml文件里解析bean的

曹工说Spring Boot源码(7)-- Spring解析xml文件,到底从中得到了什么(上)

曹工说Spring Boot源码(8)-- Spring解析xml文件,到底从中得到了什么(util命名空间)

曹工说Spring Boot源码(9)-- Spring解析xml文件,到底从中得到了什么(context命名空间上)

曹工说Spring Boot源码(10)-- Spring解析xml文件,到底从中得到了什么(context:annotation-config 解析)

曹工说Spring Boot源码(11)-- context:component-scan,你真的会用吗(这次来说说它的奇技淫巧)

曹工说Spring Boot源码(12)-- Spring解析xml文件,到底从中得到了什么(context:component-scan完整解析)

曹工说Spring Boot源码(13)-- AspectJ的运行时织入(Load-Time-Weaving),基本内容是讲清楚了(附源码)

曹工说Spring Boot源码(14)-- AspectJ的Load-Time-Weaving的两种实现方式细细讲解,以及怎么和Spring Instrumentation集成

曹工说Spring Boot源码(15)-- Spring从xml文件里到底得到了什么(context:load-time-weaver 完整解析)

曹工说Spring Boot源码(16)-- Spring从xml文件里到底得到了什么(aop:config完整解析【上】)

曹工说Spring Boot源码(17)-- Spring从xml文件里到底得到了什么(aop:config完整解析【中】)

曹工说Spring Boot源码(18)-- Spring AOP源码分析三部曲,终于快讲完了 (aop:config完整解析【下】)

曹工说Spring Boot源码(19)-- Spring 带给我们的工具利器,创建代理不用愁(ProxyFactory)

工程代码地址 思维导图地址

工程结构图:

曹工说Spring Boot源码(20)-- 码网灰灰,疏而不漏,如何记录Spring RedisTemplate每次操作日志

概要

本篇是独立的,和前面几篇aop相关分析没有特别关联,但是使用了上一篇提到的工具类。

曹工说Spring Boot源码(19)-- Spring 带给我们的工具利器,创建代理不用愁(ProxyFactory)

之前也使用类似的思路,实现过完整sql日志记录。

曹工杂谈--使用mybatis的同学,进来看看怎么在日志打印完整sql吧,在数据库可执行那种

这两天在搬砖,有个需求,是统计类的。一般来说,统计类的东西,比要统计:用户总数,用户的新增总数,当天每个小时为维度的新增数量,各个渠道的新增用户数量;这些,可能都得在redis里维护,然后某个用户注册时,去把所有这些redis结构+1。

但这种代码,一般入口很多,修改这些值的地方很多,编码时很容易发生遗漏,或者编码错误,导致最后统计数据不准确。数据不准确,当然是bug,问题是,这种bug还不好排查。

果能够记录下redis操作日志就好了。

以下,是我已经实现的效果,这是一次请求中的一次redis操作,可以看到,是put方法。

曹工说Spring Boot源码(20)-- 码网灰灰,疏而不漏,如何记录Spring RedisTemplate每次操作日志

实现思路

我们用的是spring boot 2.1.7,直接集成的RedisTemplate。当然,只要是使用RedisTemplate即可,和spring boot没多大关系。

我看了下我们平时是怎么去操作redis 的hash结构的,大概代码如下:

@Autowired @Qualifier("redisTemplate") private RedisTemplate<String,Object> redisTemplate; HashOperations<String, HK, HV> ops = redisTemplate.opsForHash(); ops.put(key, hashKey,fieldValue);

一般就是,先通过opsForHash,拿到HashOperations,再去操作hash结构。

我现在的想法就是,在执行类似ops的put的方法之前,把那几个参数记录到日志里。

要想让ops记录我们的日志,我们只能拦截其每个方法,这一步就得使用一个代理对象,去替换掉真实的对象。

但是,怎么才能让redisTemplate.opsForHash()返回的ops,是我们代理过的对象呢?

所以,这一步,还得在生成redisTemplate的地方下功夫,让其生成一个redisTemplate的代理对象,这个代理对象,拦截opsForHash方法。

总结下,需要做两件事:

对redisTemplate做代理,拦截opsForHash方法;

在拿到第一步的原有的ops对象后,对ops对象做代理,拦截其put方法等。

代码实现 原有代码 @Configuration public class RedisConfig { @Bean public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String,Object> template = new RedisTemplate<>(); template.setValueSerializer(new CustomGenericJackson2JsonRedisSerializer()); template.setHashKeySerializer(new CustomHashKeyRedisSerializer()); template.setKeySerializer(RedisSerializer.string()); template.setHashValueSerializer(new CustomGenericJackson2JsonRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } 代理RedisTemplate @Bean public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String,Object> template = new RedisTemplate<>(); template.setValueSerializer(new CustomGenericJackson2JsonRedisSerializer()); template.setHashKeySerializer(new CustomHashKeyRedisSerializer()); template.setKeySerializer(RedisSerializer.string()); template.setHashValueSerializer(new CustomGenericJackson2JsonRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(template); proxyFactory.setProxyTargetClass(true); proxyFactory.addAdvice(new MethodInterceptor() { @Override public Object invoke(MethodInvocation invocation) throws Throwable { //拦截opsForHash boolean b = invocation.getMethod().getName().equals("opsForHash"); if (b) { // todo,下一步再完善这里 } return invocation.proceed(); } }); //这里获取到针对template的代理对象,并返回 Object proxy = proxyFactory.getProxy(); return (RedisTemplate<String, Object>) proxy; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdzzw.html