缓存在一个业务系统中十分重要,常用的场景就是用来储存调用频率较高的数据。Abp 也提供了一套缓存机制供用户使用,在使用 Abp 框架的时候可以通过注入 ICacheManager 来新建/设置缓存。
同时 Abp 框架也提供了 Redis 版本的 ICacheManager 实现,你也可以很方便的将现有的内存缓存替换为 Redis 缓存。
0.1 典型使用方法 public class TestAppService : ApplicationService { private readonly ICacheManager _cacheMgr; private readonly IRepository<TestEntity> _rep; // 注入缓存管理器与测试实体的仓储 public TestAppService(ICacheManager cacheMgr, IRepository<TestEntity> rep) { _cacheMgr = cacheMgr; _rep = rep; } public void TestMethod() { // 获取/创建一个新的缓存 var cache = _cacheMgr.GetCache("缓存1"); // 转换为强类型的缓存 var typedCache = cache.AsTyped<int, string>(); // 获取缓存的数据,如果存在则直接返回。 // 如果不存在则执行工厂方法,将其值存放到 // 缓存项当中,最后返回缓存项数据。 var cacheValue = typedCache.Get(10, id => _rep.Get(id).Name); Console.WriteLine(cacheValue); } } 1.启动流程同其他的基础设施一样,缓存管理器 ICacheManager 在 Abp 框架启动的时候就自动被注入到了 Ioc 容器当中,因为他的基类 CacheManagerBase 继承了 ISingletonDependency 接口。
public abstract class CacheManagerBase : ICacheManager, ISingletonDependency { // ... 其他代码 }其次就是他的 ICachingConfiguration 缓存配置是在 AbpCoreInstaller 注入到 Ioc 容器,并且同其他基础设施的配置一起被集成到了 IAbpStartupConfiguration 。
internal class AbpCoreInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register( // 其他被注入的基础设施配置 Component.For<ICachingConfiguration, CachingConfiguration>().ImplementedBy<CachingConfiguration>().LifestyleSingleton() // 其他被注入的基础设施配置 ); } }你可以在其他模块的 PreInitialize() 方法里面可以直接通过 Configuration.Caching 来配置缓存过期时间等功能。
public override void PreInitialize() { Configuration.Caching.ConfigureAll(z=>z.DefaultSlidingExpireTime = TimeSpan.FromHours(1)); } 2. 代码分析缓存这块可能是 Abp 框架实现当中最简单的一部分了,代码量不多,但是设计思路还是值得借鉴的。
2.1 缓存管理器 2.1.1 基本定义缓存管理器即 ICacheManager ,通常它用于管理所有缓存,他的接口定义十分简单,就两个方法:
public interface ICacheManager : IDisposable { // 获得所有缓存 IReadOnlyList<ICache> GetAllCaches(); // 根据缓存名称获取缓存 [NotNull] ICache GetCache([NotNull] string name); } 2.1.2 获取/创建缓存Abp 实现了一个抽象基类 CacheBase 实现了本接口,在 CacheBase 内部维护了一个 ConcurrentDictionary<string,ICache> 字典,这个字典里面就是存放的所有缓存。
同时在他的 GetCache(string name) 内部呢,通过传入的缓存名字来从字典获取已经存在的缓存,如果不存在呢,执行其工厂方法来创建一个新的缓存。
public virtual ICache GetCache(string name) { Check.NotNull(name, nameof(name)); // 从字典根据名称取得缓存,不存在则使用工厂方法 return Caches.GetOrAdd(name, (cacheName) => { // 得到创建成功的缓存 var cache = CreateCacheImplementation(cacheName); // 遍历缓存配置集合,查看当前名字的缓存是否存在配置项 var configurators = Configuration.Configurators.Where(c => c.CacheName == null || c.CacheName == cacheName); // 遍历这些配置项执行配置操作,更改缓存的过期时间等参数 foreach (var configurator in configurators) { configurator.InitAction?.Invoke(cache); } // 返回配置完成的缓存 return cache; }); } // 真正创建缓存的方法 protected abstract ICache CreateCacheImplementation(string name);这里的 CreateCacheImplementation()由具体的缓存管理器实现的缓存创建方法,因为 Redis 与 MemoryCache 的实现各不一样,所以这里定义了一个抽象方法。
2.1.3 缓存管理器销毁当缓存管理器被销毁的时候,首先是遍历字典内存储的所有缓存,并通过 IIocManager.Release() 方法来释放这些缓存,之后则是调用字典的 Clear() 方法清空字典。
public virtual void Dispose() { DisposeCaches(); // 清空字典 Caches.Clear(); } // 遍历字典,释放对象 protected virtual void DisposeCaches() { foreach (var cache in Caches) { IocManager.Release(cache.Value); } } 2.1.4 内存缓存管理器Abp 对于缓存管理器的默认实现是 AbpMemoryCacheManager ,其实没多复杂,就是实现了基类的 CreateCacheImplementation() 返回特定的 ICache 。
public class AbpMemoryCacheManager : CacheManagerBase { // ... 忽略了的代码 protected override ICache CreateCacheImplementation(string name) { // 就 new 一个新的内存缓存而已,内存缓存的实现请看后面的 // 这里是因为 AbpMemory 没有注入到 IOC 容器,所以需要手动 new return new AbpMemoryCache(name) { Logger = Logger }; } // 重写了基类的缓存释放方法 protected override void DisposeCaches() { foreach (var cache in Caches.Values) { cache.Dispose(); } } } 2.1.5 Redis 缓存管理器如果要使用 Redis 缓存管理器,根据模块的加载顺序,你需要在启动模块的 PreInitialize() 调用 Abp.Redis 库提供的集成方法即可。