ABP框架详解(七)Caching
在ABP框架中存在一个缓存机制,使用ICache的继承类来存储最终需要缓存的数据,可以吧ICache看成一个字典对象,使用Key作为真实数据的具有唯一性的表示。使用上与字典对象完全相同,Get方法传递Key,还有数据工厂作为参数就能返回最终的值,Set方法用于存储。包含一个TimeSpan属性用于指定最长不使用数据的过期时间。
/// <summary> /// Defines a cache that can be store and get items by keys. /// </summary> public interface ICache : IDisposable { /// <summary> /// Unique name of the cache. /// </summary> string Name { get; } /// <summary> /// Default sliding expire time of cache items. /// Default value: 60 minutes. Can be changed by configuration. /// </summary> TimeSpan DefaultSlidingExpireTime { get; set; } /// <summary> /// Gets an item from the cache. /// </summary> /// <param name="key">Key</param> /// <param name="factory">Factory method to create cache item if not exists</param> /// <returns>Cached item</returns> object Get(string key, Func<string, object> factory); /// <summary> /// Gets an item from the cache. /// </summary> /// <param name="key">Key</param> /// <param name="factory">Factory method to create cache item if not exists</param> /// <returns>Cached item</returns> Task<object> GetAsync(string key, Func<string, Task<object>> factory); /// <summary> /// Gets an item from the cache or null if not found. /// </summary> /// <param name="key">Key</param> /// <returns>Cached item or null if not found</returns> object GetOrDefault(string key); /// <summary> /// Gets an item from the cache or null if not found. /// </summary> /// <param name="key">Key</param> /// <returns>Cached item or null if not found</returns> Task<object> GetOrDefaultAsync(string key); /// <summary> /// Saves/Overrides an item in the cache by a key. /// </summary> /// <param name="key">Key</param> /// <param name="value">Value</param> /// <param name="slidingExpireTime">Sliding expire time</param> void Set(string key, object value, TimeSpan? slidingExpireTime = null); /// <summary> /// Saves/Overrides an item in the cache by a key. /// </summary> /// <param name="key">Key</param> /// <param name="value">Value</param> /// <param name="slidingExpireTime">Sliding expire time</param> Task SetAsync(string key, object value, TimeSpan? slidingExpireTime = null); /// <summary> /// Removes a cache item by it's key. /// </summary> /// <param name="key">Key</param> void Remove(string key); /// <summary> /// Removes a cache item by it's key (does nothing if given key does not exists in the cache). /// </summary> /// <param name="key">Key</param> Task RemoveAsync(string key); /// <summary> /// Clears all items in this cache. /// </summary> void Clear(); /// <summary> /// Clears all items in this cache. /// </summary> Task ClearAsync(); }
框架中提供了一个ICache的抽象实现类CacheBase,作为所有具体缓存方式的基类,实现了所有基础逻辑,最终的Cache对象只需继承CacheBase,并重写object GetOrDefault(string key),void Set(string key, object value, TimeSpan? slidingExpireTime = null),void Clear()等三个抽象方法就可以完成一个真实可用的缓存Cache(另外Dispose方法通常是需要重写)。同时CacheBase的所有其他公共方法也是可重写的。ABP中默认提供了一个以内存方式缓存数据的实现即AbpMemoryCache,只是简单实用微软的MemoryCache类作为内部缓存容器。
ICacheManager使用单例模式用于管理所有的ICache,其中包含一个传递Key返回ICache的GetCache方法,还有一个返回所有ICache的GetAllCaches方法,在调用GetCache传递Key的时候如果不存在就会创建一个新的保存起来,对于ICacheManager框架中已经存在一个名为CacheManagerBase的抽象实现了,其内部有一个线程安全的字典用于存放所有的ICache,用于需要继承CacheManagerBase实现自身的CacheManager,只需要实现抽象方法CreateCacheImplementation就行了,最简单的方式就是在方法内部返回一个AbpMemoryCache实例就行了
/// <summary> /// An upper level container for <see cref="ICache"/> objects. /// A cache manager should work as Singleton and track and manage <see cref="ICache"/> objects. /// </summary> public interface ICacheManager : IDisposable { /// <summary> /// Gets all caches. /// </summary> /// <returns>List of caches</returns> IReadOnlyList<ICache> GetAllCaches(); /// <summary> /// Gets (or creates) a cache. /// </summary> /// <param name="name">Unique name of the cache</param> /// <returns>The cache reference</returns> ICache GetCache(string name); }
缓存机制中存在一个ICachingConfiguration的配置类,用户可以在自定义的模块中添加ICache的初始化处理逻辑,通常是一个Action<ICache>的委托,添加到配置类的Configurators属性中,每次被创建一个新的ICache对象的时候都会遍历Configurators属性中名称与新建ICache相同,或者无名称的全局性Action<ICache>,用来初始化ICache。
/// <summary> /// Used to configure caching system. /// </summary> public interface ICachingConfiguration { /// <summary> /// List of all registered configurators. /// </summary> IReadOnlyList<ICacheConfigurator> Configurators { get; } /// <summary> /// Used to configure all caches. /// </summary> /// <param name="initAction"> /// An action to configure caches /// This action is called for each cache just after created. /// </param> void ConfigureAll(Action<ICache> initAction); /// <summary> /// Used to configure a specific cache. /// </summary> /// <param name="cacheName">Cache name</param> /// <param name="initAction"> /// An action to configure the cache. /// This action is called just after the cache is created. /// </param> void Configure(string cacheName, Action<ICache> initAction); }
/// <summary> /// A registered cache configurator. /// </summary> public interface ICacheConfigurator { /// <summary> /// Name of the cache. /// It will be null if this configurator configures all caches. /// </summary> string CacheName { get; } /// <summary> /// Configuration action. Called just after the cache is created. /// </summary> Action<ICache> InitAction { get; } }
另外因为所有数据都是以字符串为Key,值为Object的方式存储的,所以对于一些依赖强类型的使用不太方便,作者也添加了ITypedCache<TKey,TValue>的方式,用于返回强类型的数据,虽然使用方便的,但是ICache所能缓存的对象也有了限制。
另外在WebApi端的存储,作者新建一个独立的AbpCacheController控制器,新建了几个Clear方法方便用户(通常是系统管理员级别的)通过远程调用WebApi方法的方式来清理缓存,为了安全起见需要提供一个安全验证码的,原生的是在SettingManager中写死一个密码参数和值,只要告诉管理员密码就行了,在访问AbpCacheController的Clear方法的时候提供预先知道的密码就有权限修改了,这个是非常不方便的,所以建议为清理缓存设置一个单独的Permission,并且添加一个AOP(WebApi的ActionFilter或者直接在AbpApiAuthorieAttribute中)根据当前用户的IAbpSession查看是否有权限清理Cache。