正文
提到缓存的使用(应用层面),我们可能首先想到spring-cache,直接在需要加缓存的方法上加一个注解即可。但是简单的东西往往不是很灵活,并不能很好的适配一些特定场景。所以我打算用函数接口实现一个简单的本地缓存(caffeine)处理器。(spring-cache也有很多可选的缓存实现,但貌似想要清除缓存,需要触发缓存失效的注解,不是那么随心所欲)
思路很简单,定义一个cacheKey,在业务逻辑调用前根据cacheKey判断我需要的数据是否在缓存中,如果存在,直接从缓存中返回数据,如果不存在,就去调用业务逻辑,之后把执行结果放入缓存中,从而避免了数据库的交互和多余的运算。(主要就是通过减少IO来提升性能)
代码
package cn.mrxionge.idemo.cache;
@FunctionalInterface
public interface LocalCacheHandler<T> {
T invoke();
}
↑↑↑ 这里我们创建一个函数接口,后续的具体业务逻辑写在接口的实现中。
package cn.mrxionge.idemo.cache;
import com.github.benmanes.caffeine.cache.Cache;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SuppressWarnings("all")
public class LocalCacheUtil {
public static <T> T cache(Cache<String, Object> cache, String cacheKey, LocalCacheHandler<T> cacheHandler) {
log.debug("LocalCache --> cacheKey: {}", cacheKey);
Object obj = cache.getIfPresent(cacheKey);
if (obj != null) {
log.debug("LocalCache --> cache hit");
return (T) obj;
}
log.debug("LocalCache --> cache miss");
T result = cacheHandler.invoke();
log.debug("LocalCache --> add into cache");
cache.put(cacheKey, result);
return result;
}
}
↑↑↑ 这里创建一个缓存工具,给出一个静态方法。cache方法中,在调用接口实现之前,我们根据cacheKey判断我们需要的数据是否在缓存中,在调用接口实现之后,把执行结果放到缓存中。
package cn.mrxionge.idemo;
import cn.hutool.core.thread.ThreadUtil;
import cn.mrxionge.idemo.cache.LocalCacheUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
@Slf4j
public class CacheTest {
public static void main(String[] args) {
Cache<String, Object> cache = Caffeine.newBuilder().maximumSize(100).expireAfterAccess(10, TimeUnit.SECONDS).build();
int a = 111;
int b = 222;
for (int i = 0; i < 2; i++) {
Integer result = LocalCacheUtil.cache(cache, "key_" + a + b, () -> {
log.debug("假装执行复杂运算...");
ThreadUtil.sleep(1000);
log.debug("终于执行完了.......");
return a + b;
});
log.info("方法执行结果: " + result);
}
cache.invalidateAll();
}
}
↑↑↑ 写一个简单的测试类,这里创建了一个方法级的缓存对象。(当然也可以选择全局的缓存对象,视情况而定)我们直接调用缓存工具中的方法,并且传入缓存相关的参数,剩下的就是写业务逻辑了。这里通过sleep方法假装这个逻辑需要运行1秒才会有结果。运行之后我们可以通过日志查看执行顺序和结果。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f37e4b9961cd0022ca7091a10b831ee8.png)
或者查看日志前的时间,也可以看出缓存的效果。
我这里只是给出了一个很简单的demo,但是这个思路可以发散下去。
思路和AOP的before、after是一样的,不仅仅可以应用在上面的本地缓存上。我们把本地缓存替换成redis也行,或者用它实现简单的分布式锁也OK,环绕日志、性能分析……等等。。。