SpringBoot整合Ehcache3
前言
公司部门老项目要迁移升级java版本,需要进行缓存相关操作,原框架未支持这部分,经过调研java相关缓存方案大致分为ehcache和redis两种,redis的value最大值为500mb且超过1mb会对存取有性能影响,业务系统需要支持列表查询缓存就不可避免的涉及到大量的数据存取过滤,ehcache支持内存+磁盘缓存不用担心缓存容量问题,所以框架初步版本决定集成ehcache3,设计流程结构如下图所示
缓存配置
maven引用
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
<dependency>
<groupId>org.ehcachegroupId>
<artifactId>ehcacheartifactId>
dependency>
复制代码
个性化配置
#缓存配置
cache:
ehcache:
heap: 1000
offheap: 100
disk: 500
diskDir: tempfiles/cache/
复制代码
@Component
@ConfigurationProperties("frmae.cache.ehcache")
public class EhcacheConfiguration {
/**
* ehcache heap大小
* jvm内存中缓存的key数量
*/
private int heap;
/**
* ehcache offheap大小
* 堆外内存大小, 单位: MB
*/
private int offheap;
/**
* 磁盘持久化目录
*/
private String diskDir;
/**
* ehcache disk
* 持久化到磁盘的大小, 单位: MB
* diskDir有效时才生效
*/
private int disk;
public EhcacheConfiguration(){
heap = 1000;
offheap = 100;
disk = 500;
diskDir = "tempfiles/cache/";
}
}
复制代码
代码注入配置
因为springboot默认缓存优先注入redis配置,所以需要手动声明bean进行注入,同时ehcache的value值必须支持序列化接口,不能使用Object代替,这里声明一个缓存基类,所有缓存value对象必须继承该类
public class BaseSystemObject implements Serializable {
}
复制代码
@Configuration
@EnableCaching
public class EhcacheConfig {
@Autowired
private EhcacheConfiguration ehcacheConfiguration;
@Autowired
private ApplicationContext context;
@Bean(name = "ehCacheManager")
public CacheManager getCacheManager() {
//资源池生成器配置持久化
ResourcePoolsBuilder resourcePoolsBuilder = ResourcePoolsBuilder.newResourcePoolsBuilder()
// 堆内缓存大小
.heap(ehcacheConfiguration.getHeap(), EntryUnit.ENTRIES)
// 堆外缓存大小
.offheap(ehcacheConfiguration.getOffheap(), MemoryUnit.MB)
// 文件缓存大小
.disk(ehcacheConfiguration.getDisk(), MemoryUnit.MB);
//生成配置
ExpiryPolicy expiryPolicy = ExpiryPolicyBuilder.noExpiration();
CacheConfiguration config = CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, BaseSystemObject.class, resourcePoolsBuilder)
//设置永不过期
.withExpiry(expiryPolicy)
.build();
CacheManagerBuilder cacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(ehcacheConfiguration.getDiskDir()));
return cacheManagerBuilder.build(true);
}
}
复制代码
缓存操作
缓存预热
针对缓存框架选择的双写策略,即数据库和缓存同时写入,所以在系统启动时需要预先将数据库数据加载到缓存中
针对单表声明自定义注解,个性化缓存定义自定义接口
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HPCache {
}
复制代码
public interface IHPCacheInitService {
String getCacheName();
void initCache();
}
复制代码
系统初始化时同步进行缓存初始化,扫描注解实体类与接口实现Bean
@Async
public void initCache(Class runtimeClass, List extraPackageNameList) {
List> cacheEntityList = new ArrayList<>();
if (!runtimeClass.getPackage().getName().equals(Application.class.getPackage().getName())) {
cacheEntityList.addAll(ScanUtil.getAllClassByPackageName_Annotation(runtimeClass.getPackage(), HPCache.class));
}
for (String packageName : extraPackageNameList) {
cacheEntityList.addAll(ScanUtil.getAllClassByPackageName_Annotation(packageName, HPCache.class));
}
for (Class clazz : cacheEntityList) {
TableName tableName = (TableName) clazz.getAnnotation(TableName.class);
List> resultList = commonDTO.selectList(tableName.value(), "*", "1=1", "", new HashMap<>(), false);
for (LinkedHashMap map : resultList) {
Cache cache = cacheManager.getCache(clazz.getName(), String.class, BaseSystemObject.class);
String unitguid = ConvertOp.convert2String(map.get("UnitGuid"));
try {
Object obj = clazz.newInstance();
obj = ConvertOp.convertLinkHashMapToBean(map, obj);
cache.put(unitguid, obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//自定义缓存
Map res = context.getBeansOfType(IHPCacheInitService.class);
for (Map.Entry en : res.entrySet()) {
IHPCacheInitService service = (IHPCacheInitService) en.getValue();
service.initCache();
}
System.out.println("缓存初始化完毕");
}
复制代码
需要注意,在EhcacheConfig配置类中需要进行缓存名称的提前注册,否则会导致操作缓存时空指针异常
Map annotatedBeans = context.getBeansWithAnnotation(SpringBootApplication.class);
Class runtimeClass = annotatedBeans.values().toArray()[0].getClass();
//do,dao扫描
List extraPackageNameList = new ArrayList();
extraPackageNameList.add(Application.class.getPackage().getName());
List> cacheEntityList = new ArrayList<>();
if (!runtimeClass.getPackage().getName().equals(Application.class.getPackage().getName())) {
cacheEntityList.addAll(ScanUtil.getAllClassByPackageName_Annotation(runtimeClass.getPackage(), HPCache.class));
}
for (String packageName : extraPackageNameList) {
cacheEntityList.addAll(ScanUtil.getAllClassByPackageName_Annotation(packageName, HPCache.class));
}
for (Class clazz : cacheEntityList) {
cacheManagerBuilder = cacheManagerBuilder.withCache(clazz.getName(), config);
}
//自定义缓存
Map res = context.getBeansOfType(IHPCacheInitService.class);
for (Map.Entry en :res.entrySet()) {
IHPCacheInitService service = (IHPCacheInitService)en.getValue();
cacheManagerBuilder = cacheManagerBuilder.withCache(service.getCacheName(), config);
}
复制代码
更新操作
手动获取ehcache的bean对象,调用put,repalce,delete方法进行操作
private CacheManager cacheManager = (CacheManager) SpringBootBeanUtil.getBean("ehCacheManager");
public void executeUpdateOperation(String cacheName, String key, BaseSystemObject value) {
Cache cache = cacheManager.getCache(cacheName, String.class, BaseSystemObject.class);
if (cache.containsKey(key)) {
cache.replace(key, value);
} else {
cache.put(key, value);
}
}
public void executeDeleteOperation(String cacheName, String key) {
Cache cache = cacheManager.getCache(cacheName, String.class, BaseSystemObject.class);
cache.remove(key);
}
复制代码
查询操作
缓存存储单表以主键—object形式存储,个性化缓存为key-object形式存储,单条记录可以通过getCache方法查询,列表查询需要取出整个缓存按条件进行过滤
public Object getCache(String cacheName, String key){
Cache cache = cacheManager.getCache(cacheName, String.class, BaseSystemObject.class);
return cache.get(key);
}
public List