直到现在,我们已经学会了如何使用开箱即用的 spring cache,这基本能够满足一般应用对缓存的需求。
但现实总是很复杂,当你的用户量上去或者性能跟不上,总需要进行扩展,这个时候你或许对其提供的内存缓存不满意了,因为其不支持高可用性,也不具备持久化数据能力,这个时候,你就需要自定义你的缓存方案了。还好,spring 也想到了这一点。我们先不考虑如何持久化缓存,毕竟这种第三方的实现方案很多。我们要考虑的是,怎么利用 spring 提供的扩展点实现我们自己的缓存,且在不改原来已有代码的情况下进行扩展。
首先,我们需要提供一个 CacheManager接口的实现,这个接口告诉 spring 有哪些 cache 实例,spring 会根据 cache 的名字查找 cache 的实例。另外还需要自己实现 Cache接口,Cache 接口负责实际的缓存逻辑,例如增加键值对、存储、查询和清空等。
利用 Cache 接口,我们可以对接任何第三方的缓存系统,例如 EHCache、OSCache,甚至一些内存数据库例如 memcache 或者 redis 等。下面我举一个简单的例子说明如何做。
public class MyCacheManager extends AbstractCacheManager {
private Collection<? extends MyCache> caches;
/**
* Specify the collection of Cache instances to use for this CacheManager.
*/
public void setCaches(Collection<? extends MyCache> caches) {
this.caches = caches;
}
@Override
protected Collection<? extends MyCache> loadCaches() {
return this.caches;
}
}
上面的自定义的 CacheManager 实际继承了 spring 内置的 AbstractCacheManager,实际上仅仅管理 MyCache 类的实例。
下面是MyCache的定义:
public class MyCache implements Cache {
private String name;
private Map<String,Account> store = new HashMap<String,Account>();;
public MyCache() {
}
public MyCache(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public Object getNativeCache() {
return store;
}
@Override
public ValueWrapper get(Object key) {
ValueWrapper result = null;
Account thevalue = store.get(key);
if(thevalue!=null) {
thevalue.setPassword("from mycache:"+name);
result = new SimpleValueWrapper(thevalue);
}
return result;
}
@Override
public void put(Object key, Object value) {
Account thevalue = (Account)value;
store.put((String)key, thevalue);
}
@Override
public void evict(Object key) {
}
@Override
public void clear() {
}
}
上面的自定义缓存只实现了很简单的逻辑,但这是我们自己做的,也很令人激动是不是,主要看 get 和 put 方法,其中的 get 方法留了一个后门,即所有的从缓存查询返回的对象都将其 password 字段设置为一个特殊的值,这样我们等下就能演示“我们的缓存确实在起作用!”了。
这还不够,spring 还不知道我们写了这些东西,需要通过 spring*.xml 配置文件告诉它:
<cache:annotation-driven />
<bean id="cacheManager" class="com.rollenholt.spring.cache.MyCacheManager">
<property name="caches">
<set>
<!--
p:name属性大概等同于<property name="name" ref="Cache中的name"></property>是给
Cache类中的name属性注入名字
-->
<bean class="com.rollenholt.spring.cache.MyCache" p:name="accountCache" />
</set>
</property>
</bean>
接下来我们来编写测试代码:
Account account = accountService.getAccountByName("someone");
logger.info("passwd={}", account.getPassword());
account = accountService.getAccountByName("someone");
logger.info("passwd={}", account.getPassword());
上面的测试代码主要是先调用 getAccountByName 进行一次查询,这会调用数据库查询,然后缓存到 mycache 中,然后我打印密码,应该是空的;下面我再次查询 someone 的账号,这个时候会从 mycache 中返回缓存的实例,记得上面的后门么?我们修改了密码,所以这个时候打印的密码应该是一个特殊的值。










网友评论