本文共 7989 字,大约阅读时间需要 26 分钟。
缓存是现代应用开发中至关重要的一环。优先从缓存中取数据,缓存中取不到再去数据库中取,取到了再扔进缓存中去。这一原则看似简单,但在实际项目中却常常导致重复代码的繁殖。为了解决这一问题,我们可以利用AOP(面向切面编程)来简化缓存操作。
定义缓存接口
定义一个ICachingProvider接口及其实现MemoryCachingProvider,用于简化缓存读写操作。定义Attribute
创建一个QCachingAttribute,用于标注需要缓存的方法。这里使用了绝对过期时间(单位:秒)作为演示。定义辅助接口
IQCaching:用于标识需要缓存的方法。IQCachable:用于自定义类实现,指定缓存键。实现拦截器
创建一个QCachingInterceptor,继承IInterceptor,用于拦截需要缓存的方法。拦截器会自动处理缓存键,并在方法执行前后进行缓存读写操作。using Castle.Core;using Castle.MicroKernel;using Castle.MicroKernel.Extensions;using Autofac;using Autofac.Extensions.DependencyInjection;public class QCachingInterceptor : IInterceptor{ private ICachingProvider _cacheProvider; private char _linkChar = ':'; public QCachingInterceptor(ICachingProvider cacheProvider) { _cacheProvider = cacheProvider; } public void Intercept(IInvocation invocation) { var qCachingAttribute = GetQCachingAttributeInfo(invocation); if (qCachingAttribute != null) { ProceedCaching(invocation, qCachingAttribute); } else { invocation.Proceed(); } } private QCachingAttribute GetQCachingAttributeInfo(IInvocation invocation) { return invocation.Method.GetCustomAttributes(true) .FirstOrDefault(x => x.GetType() == typeof(QCachingAttribute)) as QCachingAttribute; } private void ProceedCaching(IInvocation invocation, QCachingAttribute attribute) { var cacheKey = GenerateCacheKey(invocation); var cacheValue = _cacheProvider.Get(cacheKey); if (cacheValue != null) { invocation.ReturnValue = cacheValue; return; } invocation.Proceed(); if (!string.IsNullOrWhiteSpace(cacheKey)) { _cacheProvider.Set(cacheKey, invocation.ReturnValue, TimeSpan.FromSeconds(attribute.AbsoluteExpiration)); } } private string GenerateCacheKey(IInvocation invocation) { var typeName = invocation.TargetType.Name; var methodName = invocation.Method.Name; var methodArguments = FormatArgumentsToPartOfCacheKey(invocation.Arguments); return GenerateCacheKey(typeName, methodName, methodArguments); } private string GenerateCacheKey(string typeName, string methodName, IList parameters) { var builder = new StringBuilder(); builder.Append(typeName); builder.Append(_linkChar); builder.Append(methodName); builder.Append(_linkChar); foreach (var param in parameters) { builder.Append(param); builder.Append(_linkChar); } return builder.ToString().TrimEnd(_linkChar); } private IList FormatArgumentsToPartOfCacheKey( IList IDateTimeService,并在方法上标注QCachingAttribute。public interface IDateTimeService : IQCaching{ [QCaching.QCaching(AbsoluteExpiration = 10)] string GetCurrentUtcTime();}public class DateTimeService : IDateTimeService{ public string GetCurrentUtcTime() { return System.DateTime.UtcNow.ToString(); }} QCachingAttribute。public class BllController : Controller{ private ILifetimeScope _scope; private DateTimeBLL _dateTimeBLL; public BllController(ILifetimeScope scope) { _scope = scope; _dateTimeBLL = _scope.Resolve (); } public IActionResult Index() { return Content(_dateTimeBLL.GetCurrentUtcTime()); }} QCachingInterceptor,继承AbstractInterceptor,并重写Invoke方法。using AspectCore.Core;using AspectCore.Extensions.DependencyInjection;public class QCachingInterceptor : AbstractInterceptor{ [FromContainer] public ICachingProvider CacheProvider { get; set; } public async override Task Invoke(AspectContext context, AspectDelegate next) { var qCachingAttribute = GetQCachingAttributeInfo(context.ServiceMethod); if (qCachingAttribute != null) { await ProceedCaching(context, next, qCachingAttribute); } else { await next(context); } } private QCachingAttribute GetQCachingAttributeInfo(IInvocation invocation) { return invocation.Method.GetCustomAttributes(true) .FirstOrDefault(x => x.GetType() == typeof(QCachingAttribute)) as QCachingAttribute; } private async Task ProceedCaching(AspectContext context, AspectDelegate next, QCachingAttribute attribute) { var cacheKey = GenerateCacheKey(context); var cacheValue = await _cacheProvider.Get(cacheKey); if (cacheValue != null) { context.ReturnValue = cacheValue; return; } await next(context); if (!string.IsNullOrWhiteSpace(cacheKey)) { await _cacheProvider.Set(cacheKey, context.ReturnValue, TimeSpan.FromSeconds(attribute.AbsoluteExpiration)); } } private string GenerateCacheKey(IInvocation invocation) { var typeName = invocation.TargetType.Name; var methodName = invocation.Method.Name; var methodArguments = FormatArgumentsToPartOfCacheKey(invocation.Arguments); return GenerateCacheKey(typeName, methodName, methodArguments); } private string GenerateCacheKey(string typeName, string methodName, IList parameters) { var builder = new StringBuilder(); builder.Append(typeName); builder.Append(_linkChar); builder.Append(methodName); builder.Append(_linkChar); foreach (var param in parameters) { builder.Append(param); builder.Append(_linkChar); } return builder.ToString().TrimEnd(_linkChar); } private IList FormatArgumentsToPartOfCacheKey( IList 在Startup中完成服务配置和拦截器注册。
public class Startup{ public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddScoped (); services.AddScoped (); var assembly = this.GetType().GetTypeInfo().Assembly; AddBLLClassToServices(assembly, services); var container = services.ToServiceContainer(); container.AddType (); container.Configure(config => { config.Interceptors.AddTyped ( method => typeof(IQCache).IsAssignableFrom(method.DeclaringType)); }); return container.Build(); } public void AddBLLClassToServices(Assembly assembly, IServiceCollection services) { var types = assembly.GetTypes().ToList(); foreach (var type in types.Where(x => x.Name.EndsWith("BLL", StringComparison.OrdinalIgnoreCase) && !x.GetTypeInfo().IsAbstract())) { services.AddSingleton(type); } }} AOP通过简化缓存操作,显著减少了重复代码的出现。无论是使用Castle还是AspectCore,实现方式基本一致,区别主要体现在配置和使用习惯上。Castle的配置稍微复杂一些,而AspectCore则更加简洁高效。通过合理配置和注入,AOP可以有效提升应用性能,同时降低代码维护成本。
转载地址:http://vgxkz.baihongyu.com/