IT教程 ·

曹工说Spring Boot源码(20)– 码网恢恢,疏而不漏,怎样纪录Spring RedisTemplate每次操纵日记

如何用Java8 Stream API找到心仪的女朋友

写在前面的话

相干背景及资本:

工程构造图:

曹工说Spring Boot源码(20)-- 码网恢恢,疏而不漏,怎样纪录Spring RedisTemplate每次操纵日记 IT教程 第1张

提要

本篇是自力的,和前面几篇aop相干剖析没有迥殊关联,然则运用了上一篇提到的东西类。

之前也运用相似的思绪,完成过完全sql日记纪录。

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

但这类代码,平常进口许多,修正这些值的处所许多,编码时很容易发生脱漏,或许编码毛病,致使末了统计数据不正确。数据不正确,固然是bug,问题是,这类bug还不好排查。

假如能够纪录下redis操纵日记就好了。

以下,是我已完成的效果,这是一次要求中的一次redis操纵,能够看到,是put要领。

曹工说Spring Boot源码(20)-- 码网恢恢,疏而不漏,怎样纪录Spring RedisTemplate每次操纵日记 IT教程 第2张

完成思绪

我们用的是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要领。

总结下,需要做两件事:

  1. 对redisTemplate做代办,阻拦opsForHash要领;
  2. 在拿到第一步的原有的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;
    }

人人能够细致看上面的代码,利用了前一讲我们进修了的ProxyFactory,来生成代办;运用它呢,比较轻易,不必管底层它是用jdk动态代办,照样cglib代办,spring已帮我们处置惩罚好了。

总之,上面这段,就是把redisTemplate给换了。我们详细要在阻拦了opsForHash里,做什么行动呢?我们再看。

代办opsForHash的返回效果

@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 {
                boolean b = invocation.getMethod().getName().equals("opsForHash");
                if (b) {
                    // 1. 这一步,拿到原有的opsForHash的返回效果
                    HashOperations hashOperations = (HashOperations) invocation.proceed();
                    
                    //2. 下边,对hashOperations举行代办
                    ProxyFactory proxyFactory = new ProxyFactory();
                    proxyFactory.setTarget(hashOperations);
                    proxyFactory.setProxyTargetClass(false);
                    proxyFactory.setInterfaces(HashOperations.class);
                    //3. 我们这个代办干什么事呢,就是加了一个要领前的阻拦器,纪录日记
                    proxyFactory.addAdvice(new MethodBeforeAdviceInterceptor(new MethodBeforeAdvice() {
                        // 运用fastjson花样化了参数,并纪录到日记
                        @Override
                        public void before(Method method, Object[] args, Object target) {
                            log.info("method:{},args:{}",method.getName(),
                                    JSON.toJSONString(args, SerializerFeature.PrettyFormat));
                        }
                    }));
                   // 这里返回针对hashOperations的代办
                    return proxyFactory.getProxy();
                }

                return invocation.proceed();
            }
        });
        Object proxy = proxyFactory.getProxy();

        return (RedisTemplate<String, Object>) proxy;
    }

总结

我这个阻拦比较粗,如今是把get类的日记也打出来了。人人能够推断下method的称号,来自行过滤掉。

ok,本篇先到这里。下讲继承讲Spring ProxyFactory的内容。

【二】、UML基础知识——图图解乾坤

参与评论