AOP-Redis缓存

aop,redis,缓存 · 浏览次数 : 10

小编点评

**AOP-Redis缓存功能实现** **引言** AOP-Redis缓存是一种基于内存和Redis的缓存解决方案,它可以用于提高应用程序的性能。 **依赖注入** 为了实现AOP-Redis缓存,我们需要依赖注入IRedisCaching接口。IRedisCaching接口用于获取和设置Redis中的值。 **实现** 1. **RedisCaching类**: - 使用ConnectionMultiplexer对象获取Redis连接。 - 使用StringGetAsync和StringSetAsync方法存储和读取缓存值。 2. **CustomCacheKey方法**: - 根据方法名称和参数名获取自定义缓存键。 3. **Intercept方法**: - 在MethodInvocation中获取自定义缓存键。 - 从Redis中获取相应的缓存值。 - 如果缓存值存在,将其返回。 - 将缓存值设置到Redis中。 **注入** 通过注入IRedisCaching接口,我们可以将AOP注册到应用程序中。 **示例** ```csharp // Configure services services.AddSingleton(); // Configure AOP services.AddSingleton(); // Configure Autofac to register services // ... ``` **总结** 通过依赖注入IRedisCaching接口,我们可以实现一个基于Redis的AOP缓存功能。此功能可以用于提高应用程序的性能。

正文

我没有单独使用过Redis,细节我可能解释不到位。该文章是采用依赖注入实现AOP-Redis缓存功能的 、

之前有写实现Memory缓存的。异曲同工之妙。

使用Redis离不开安装get包:StackExchange.Redis.

操作流程:

  1. 创建一个RedisAOP的.cs文件。继承IInterceptor的接口,允许程序进行拦截。该接口应该依赖于Autofac,所以依赖注入最好使用AUtofac。
  2. 实现接口的方法Intercept。拦截的功能的操作主要在此处执行。
  3. 老张的项目是有一个baseAop的,因为RedisAOP和MemoryAOP都是属于缓存,有公共的方法。例如截取Key值等。初学我就不搞太麻烦了。一个一个手写比较容易记住。
  4. 定义一个接口一个实现。IRedisCaching,RedisCaching。该实现主要是向Redis存储值和取值。所以定义两个方法,Get获取缓存,Set存入缓存。
  5. RedisCaching里依赖注入ConnectionMultiplexer、这个代表Redis的连接字符串对象(应该可以这么理解,也可能我说的不太准确)。通过该对象的GetDatabase()获取redis内的数据库链接对象。然后使用StringGet,或者StringSet来存储或读取缓存。
  6. 将接口注入到第一步的RedisAOP中,让我们可以进行操作Redis。
  7. 很重要的一部分。将IRedisCaching和RedisCaching注入程序,以至于我们可以依赖注入在RedisAOP中使用。其次!!!很重要的一部分不能忘记ConnectionMultiplexer也要注入进去!!!我一开始因为不常用redis。脑子抽抽忘记了。
  • RedisAOP代码
    public class RedisAOP : IInterceptor
        {
            protected readonly IRedisCaching redis; 
    
            public RedisAOP(IRedisCaching _redis)
            {
                this.redis = _redis;
            }
            public void Intercept(IInvocation invocation)
            {
                var method = invocation.MethodInvocationTarget ?? invocation.Method;
                //获取自定义缓存键
                var cacheKey = CustomCacheKey(invocation);
                //根据key获取相应的缓存值
                var cacheValue = redis.GetValue(cacheKey).Result;
                if (cacheValue != null)
                {
                    //动态返回类型
                    Type returnType;
                    //获取传入方法的返回类型。如果是多个就取默认第一个
                    if (typeof(Task).IsAssignableFrom(method.ReturnType))
                    {
                        returnType = method.ReturnType.GenericTypeArguments.FirstOrDefault();
                    }
                    else
                    {
                        returnType = method.ReturnType;
                    }
                    //将数据解析成方法返回类型
                    dynamic _result = JsonSerializer.Deserialize(cacheValue, returnType);
                    invocation.ReturnValue = (typeof(Task).IsAssignableFrom(method.ReturnType)) ? Task.FromResult(_result) : _result;
                    return;
                }
                //去执行当前的方法
                invocation.Proceed();
                //存入缓存
                if (!string.IsNullOrWhiteSpace(cacheKey))
                {
                    redis.Set(cacheKey, invocation.ReturnValue,TimeSpan.FromHours(24));
                }
            }
    
            /// <summary>
            /// object 转 string
            /// </summary>
            /// <param name="arg"></param>
            /// <returns></returns>
            protected static string GetArgumentValue(object arg)
            {
                if (arg is DateTime || arg is DateTime?)
                {
                    return ((DateTime)arg).ToString("yyyyMMddHHmmss");
                }
    
                if (arg is string || arg is ValueType || arg is Nullable)
                {
                    return arg.ToString();
                }
    
                if (arg != null)
                {
                    if (arg.GetType().IsClass)
                    {
                        return MD5Helper.MD5Encrypt16(JsonSerializer.Serialize(arg));
                    }
                }
    
                return string.Empty;
            }
    
            /// <summary>
            /// 自定义缓存的key
            /// </summary>
            /// <param name="invocation"></param>
            /// <returns></returns>
            protected string CustomCacheKey(IInvocation invocation)
            {
                var typeName = invocation.TargetType.Name;
                var methodName = invocation.Method.Name;
                var methodArguments = invocation.Arguments.Select(GetArgumentValue).Take(3).ToList();//获取参数列表,最多三个
    
                string key = $"{typeName}:{methodName}:";
                foreach (var param in methodArguments)
                {
                    key = $"{key}{param}:";
                }
                return key.TrimEnd(':');
            }
        }
    View Code
  • IRedisCaching/RedisCaching代码
    public class RedisCaching : IRedisCaching
        {
            private readonly ConnectionMultiplexer redis;
            private readonly IDatabase database;
            public RedisCaching(ConnectionMultiplexer _redis)
            {
                this.redis = _redis;
                database = _redis.GetDatabase();
            }
            /// <summary>
            /// 获取值
            /// </summary>
            /// <param name="key"></param>
            /// <returns></returns>
            public async Task<string> GetValue(string key)
            {
                return await database.StringGetAsync(key);
            }
            /// <summary>
            /// 存储值
            /// </summary>
            /// <param name="key"></param>
            /// <param name="value"></param>
            /// <param name="cacheTime">过期时间</param>
            /// <returns></returns>
            /// <exception cref="NotImplementedException"></exception>
            public async Task Set(string key, object value, TimeSpan cacheTime)
            {
                if (value != null)
                {
                    if (value is string cacheValue)
                    {
                        // 字符串无需序列化
                       await database.StringSetAsync(key, cacheValue, cacheTime);
                    }
                    else
                    {
                        //序列化,将object值生成RedisValue
                        await database.StringSetAsync(key, JsonSerializer.Serialize(value), cacheTime);
                    }
                }
            }
        }
    View Code
  • 注入相关的接口,和Redis
    //接口
    builder.Services.AddSingleton<IRedisCaching,RedisCaching>();
    //Redis
    builder.Services.AddSingleton<ConnectionMultiplexer>(sp =>
    {
        //获取连接字符串
        string redisConfiguration = "127.0.0.1:6678,password=123456";
        var configuration = ConfigurationOptions.Parse(redisConfiguration, true);
        configuration.ResolveDns = true;
        return ConnectionMultiplexer.Connect(configuration);
    });
  • 使用Autofac将AOP文件注册到服务的程序集中
               var assemblysServicesPath = Path.Combine(basePath, "DogService");
    
                builder.RegisterType<LogAOP>();//可以直接替换其他拦截器!一定要把拦截器进行注册
                builder.RegisterType<RedisAOP>();
    
                //注入仓储
                var assemblysRepository = Assembly.LoadFrom(assemblysRepositoryPath);
                builder.RegisterAssemblyTypes(assemblysRepository)
                          .AsImplementedInterfaces()//方法表示将组件以其实现的接口类型进行注册,这样在进行依赖注入时,可以根据接口类型获取相应的实现类
                          .PropertiesAutowired()//方法用于自动装配属性依赖
                          .InstancePerDependency()//方法表示每次请求时都创建一个新的实例。
                          .EnableInterfaceInterceptors()//方法启用接口拦截,使得后续可以对注册的接口进行拦截。
                          .InterceptedBy(typeof(LogAOP),typeof(RedisAOP));//

     

 

完活!

 

与AOP-Redis缓存相似的内容:

AOP-Redis缓存

我没有单独使用过Redis,细节我可能解释不到位。该文章是采用依赖注入实现AOP-Redis缓存功能的 、 之前有写实现Memory缓存的。异曲同工之妙。 使用Redis离不开安装get包:StackExchange.Redis. 操作流程: 创建一个RedisAOP的.cs文件。继承IInterc

AOP(面向切面编程)

AOP(Aspect Oriented Programming,面向切面编程),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP拦截日志

首先对于AOP切面编程,我也是刚学习,了解不深,这边先引用老张的博客,方便大家学习。 首先想一想,如果有这么一个需求,要记录整个项目的接口和调用情况,当然如果只是控制器的话,还是挺简单的,直接用一个过滤器或者一个中间件,还记得咱们开发Swagger拦截权限验证的中间件么,那个就很方便的把用户调用接口

通过AOP拦截Spring Boot日志并将其存入数据库

本文将介绍如何使用Spring Boot和AOP技术实现拦截系统日志并保存到数据库中的功能。

基于Spring-AOP的自定义分片工具

作者:陈昌浩 1 背景 随着数据量的增长,发现系统在与其他系统交互时,批量接口会出现超时现象,发现原批量接口在实现时,没有做分片处理,当数据过大时或超过其他系统阈值时,就会出现错误。由于与其他系统交互比较多,一个一个接口做分片优化,改动量较大,所以考虑通过AOP解决此问题。 2 Spring-AOP

Asp-Net-Core开发笔记:进一步实现非侵入性审计日志功能

前言 上次说了利用 AOP 思想实现了审计日志功能,不过有同学反馈还是无法实现完全无侵入,于是我又重构了一版新的。 回顾一下:Asp-Net-Core开发笔记:实现动态审计日志功能 现在已经可以实现对业务代码完全无侵入的审计日志了,在需要审计的接口上加上 [AuditLog] 特性,就可以记录这个接

.NET静态代码织入——肉夹馍(Rougamo)发布2.0

肉夹馍(https://github.com/inversionhourglass/Rougamo)通过静态代码织入方式实现AOP的组件,其主要特点是在编译时完成AOP代码织入,相比动态代理可以减少应用启动的初始化时间让服务更快可用,同时还能对静态方法进行AOP。 摆烂半年又一更,感谢各位的支持,那

Spring面试攻略:如何展现你对Spring的深入理解

本次面试涉及了Spring框架的多个方面,包括IOC和AOP的理解、Spring容器的启动流程、Bean的创建过程、Bean的线程安全性、循环依赖的处理、事务的处理以及Spring MVC中控制器的线程安全性。通过这些问题的回答,展示了对Spring框架的深入理解和应用经验。同时,也凸显了对面试题目的认真思考和清晰表达的能力。

Spring源码核心剖析

SpringAOP作为Spring最核心的能力之一,其重要性不言而喻。然后需要知道的是AOP并不只是Spring特有的功能,而是一种思想,一种通用的功能。

Spring源码核心剖析

SpringAOP作为Spring最核心的能力之一,其重要性不言而喻。然后需要知道的是AOP并不只是Spring特有的功能,而是一种思想,一种通用的功能。而SpringAOP只是在AOP的基础上将能力集成到SpringIOC中,使其作为bean的一种,从而我们能够很方便的进行使用。