.NET + AOP + AutoFac + Redis
直接上代码
using Autofac; using Autofac.Extras.DynamicProxy; using Framework.Core.AOP; using Framework.Core.AOP.Cache; using Framework.Core.Configuration; using Framework.Core.DependencyInjection; using Framework.Core.Extensions.DataType; using Microsoft.Extensions.DependencyModel; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Loader; using System.Text; using System.Threading.Tasks; namespace Framework.Core.AutoFac { public class AutofacModuleRegister : Autofac.Module { protected override void Load(ContainerBuilder builder) { var assemblyList = new List<Assembly>(); var deps = DependencyContext.Default; var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && !lib.Type.Equals("package") && !lib.Name.Contains("Microsoft") && !lib.Name.Contains("System")); foreach (var lib in libs) { var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); assemblyList.Add(assembly); } if (!assemblyList.Any()) { throw new Exception("未找到任何可以注入的接口,请查看项目是否依赖"); } Assembly[] arrayAssembly = assemblyList.ToArray(); // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。 var cacheType = new List<Type>(); if (AopConfig.IsOpenCacheRedisAop) { builder.RegisterType<RedisCacheAOP>(); cacheType.Add(typeof(RedisCacheAOP)); } else { builder.RegisterType<MemoryCacheAOP>(); cacheType.Add(typeof(MemoryCacheAOP)); } #region 继承接口注入 //注入瞬时模式 //每次请求获取的实例不同 builder.RegisterAssemblyTypes(arrayAssembly) .Where(p => typeof(ITransientDependency).IsAssignableFrom(p) && p.IsClass) .AsImplementedInterfaces() .InstancePerDependency() .PropertiesAutowired() .EnableInterfaceInterceptors(); // .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。 若去掉该注释,可以不添加[Intercept(typeof(RedisCacheAOP))],所有依赖的类都会经过拦截器。 //注入作用域模式 //相同的作用域下获取的实例相同 builder.RegisterAssemblyTypes(arrayAssembly) .Where(p => typeof(IScopedDependency).IsAssignableFrom(p) && p.IsClass) .AsImplementedInterfaces() .InstancePerLifetimeScope() .PropertiesAutowired(); //单例模式 //每次请求进来获取的实例都相同 builder.RegisterAssemblyTypes(arrayAssembly) .Where(p => typeof(ISingletonDependency).IsAssignableFrom(p) && p.IsClass) .AsImplementedInterfaces() .SingleInstance() .PropertiesAutowired(); #endregion //// 获取 Service.dll 程序集服务,并注册 //var assemblysServices = Assembly.LoadFrom(servicesDllFile); //builder.RegisterAssemblyTypes(assemblysServices).AsImplementedInterfaces().InstancePerDependency() // .PropertiesAutowired(); // //.EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy; // //.InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。 //// 获取 Repository.dll 程序集服务,并注册 //var assemblysRepository = Assembly.LoadFrom(repositoryDllFile); //builder.RegisterAssemblyTypes(assemblysRepository).AsImplementedInterfaces().InstancePerDependency() // .PropertiesAutowired(); } } }
using Castle.DynamicProxy; using Framework.Core.AOP.Cache.Attribute; using Framework.Core.Cache; using Framework.Core.Extensions; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Framework.Core.AOP.Cache { /// <summary> /// Redis AOP 拦截器 /// </summary> public class RedisCacheAOP : IInterceptor { private readonly IRedisProvider _cache; public RedisCacheAOP(IRedisProvider redisProvider) { _cache = redisProvider; } public void Intercept(IInvocation invocation) { var method = invocation.MethodInvocationTarget ?? invocation.Method; //对当前方法的特性验证 dynamic cachingAttr = method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingAttribute)); if (cachingAttr != null) { //获取自定义缓存键 var cacheKey = CustomCacheKey(invocation); //注意是 string 类型,方法GetValue var cacheValue = _cache.GetValue(cacheKey).Result; if (cacheValue != null) { //将当前获取到的缓存值,赋值给当前执行方法 var type = invocation.Method.ReturnType; var resultTypes = type.GenericTypeArguments; if (type.FullName == "System.Void") { return; } object response; if (typeof(Task).IsAssignableFrom(type)) { //返回Task<T> if (resultTypes.Any()) { dynamic temp = cacheValue.FromJson(resultTypes.First()); response = Task.FromResult(temp); } else { //Task 无返回方法 指定时间内不允许重新运行 response = Task.Yield(); } } else { // 核心2,要进行 ChangeType response = Convert.ChangeType(_cache.Get<object>(cacheKey), type); } invocation.ReturnValue = response; return; } //去执行当前的方法 invocation.Proceed(); //存入缓存 if (!string.IsNullOrWhiteSpace(cacheKey)) { object response; //Type type = invocation.ReturnValue?.GetType(); var type = invocation.Method.ReturnType; if (typeof(Task).IsAssignableFrom(type)) { var resultProperty = type.GetProperty("Result"); response = resultProperty.GetValue(invocation.ReturnValue); } else { response = invocation.ReturnValue; } if (response == null) response = string.Empty; var exp = TimeSpan.FromMinutes(cachingAttr.AbsoluteExpiration); _cache.Set(cacheKey, response, exp).Wait(); } } else { invocation.Proceed();//直接执行被拦截方法 } } //自定义缓存键 private 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 += $"{param}:"; } return key.TrimEnd(':'); } //object 转 string private string GetArgumentValue(object arg) { if (arg is int || arg is long || arg is string) return arg.ToString(); if (arg is DateTime) return ((DateTime)arg).ToString("yyyyMMddHHmmss"); return ""; } } }
使用方式在注入到容器的接口类里面添加特性就可以实现拦截,在方法上添加缓存时效的特性就能实现改放回结果缓存
缓存特性实现类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Framework.Core.AOP.Cache.Attribute { /// <summary> /// 这个Attribute就是使用时候的验证,把它添加到要缓存数据的方法中,即可完成缓存的操作。 /// </summary> [AttributeUsage(AttributeTargets.Method, Inherited = true)] public class CachingAttribute : System.Attribute { /// <summary> /// 缓存绝对过期时间(分钟) /// </summary> public int AbsoluteExpiration { get; set; } = 30; } }