文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

SpringBoot整合Ehcache3的实现步骤是什么

2023-06-22 08:10

关注

这篇文章主要介绍“SpringBoot整合Ehcache3的实现步骤是什么”,在日常操作中,相信很多人在SpringBoot整合Ehcache3的实现步骤是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”SpringBoot整合Ehcache3的实现步骤是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

前言

公司部门老项目要迁移升级java版本,需要进行缓存相关操作,原框架未支持这部分,经过调研java相关缓存方案大致分为ehcache和redis两种,redis的value最大值为500mb且超过1mb会对存取有性能影响,业务系统需要支持列表查询缓存就不可避免的涉及到大量的数据存取过滤,ehcache支持内存+磁盘缓存不用担心缓存容量问题,所以框架初步版本决定集成ehcache3,设计流程结构如下图所示

SpringBoot整合Ehcache3的实现步骤是什么

缓存配置

maven引用

        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-cache</artifactId>        </dependency>        <dependency>            <groupId>org.ehcache</groupId>            <artifactId>ehcache</artifactId>        </dependency>

个性化配置

  #缓存配置  cache:    ehcache:      heap: 1000      offheap: 100      disk: 500      diskDir: tempfiles/cache/
@Component@ConfigurationProperties("frmae.cache.ehcache")public class EhcacheConfiguration {        private int heap;        private int offheap;        private String 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@EnableCachingpublic 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@Inheritedpublic @interface HPCache {}
public interface IHPCacheInitService {    String getCacheName();    void initCache();}

系统初始化时同步进行缓存初始化,扫描注解实体类与接口实现Bean

@Async    public void initCache(Class runtimeClass, List<String> extraPackageNameList) {        List<Class<?>> 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<LinkedHashMap<String, Object>> resultList = commonDTO.selectList(tableName.value(), "*", "1=1", "", new HashMap<>(), false);            for (LinkedHashMap<String, Object> 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<String, IHPCacheInitService> res = context.getBeansOfType(IHPCacheInitService.class);        for (Map.Entry en : res.entrySet()) {            IHPCacheInitService service = (IHPCacheInitService) en.getValue();            service.initCache();        }        System.out.println("缓存初始化完毕");    }

需要注意,在EhcacheConfig配置类中需要进行缓存名称的提前注册,否则会导致操作缓存时空指针异常

    Map<String, Object> annotatedBeans = context.getBeansWithAnnotation(SpringBootApplication.class);        Class runtimeClass = annotatedBeans.values().toArray()[0].getClass();        //do,dao扫描        List<String> extraPackageNameList = new ArrayList<String>();        extraPackageNameList.add(Application.class.getPackage().getName());        List<Class<?>> 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<String, IHPCacheInitService> 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);    }

查询操作

缓存存储单表以主键&mdash;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<Object> getAllCache(String cacheName){        List result = new ArrayList<>();        Cache cache = cacheManager.getCache(cacheName, String.class, BaseSystemObject.class);        Iterator iter = cache.iterator();        while (iter.hasNext()) {            Cache.Entry entry = (Cache.Entry) iter.next();            result.add(entry.getValue());        }        return result;    }

缓存与数据库数据一致性

数据库数据操作与缓存操作顺序为先操作数据后操作缓存,在开启数据库事务的情况下针对单条数据单次操作是没有问题的,如果是组合操作一旦数据库操作发生异常回滚,缓存并没有回滚就会导致数据的不一致,比如执行顺序为dbop1=》cacheop1=》dbop2=》cacheop2,dbop2异常,cacheop1的操作已经更改了缓存
这里选择的方案是在数据库全部执行完毕后统一操作缓存,这个方案有一个缺点是如果缓存操作发生异常还是会出现上述问题,实际过程中缓存只是对内存的操作异常概率较小,对缓存操作持乐观状态,同时我们提供手动重置缓存的功能,算是一个折中方案,下面概述该方案的一个实现

声明自定义缓存事务注解

@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface CacheTransactional {}

声明切面监听,在标记了CacheTransactional注解的方法执行前进行Redis标识,统一执行完方法体后执行缓存操作
将缓存操作以线程id区分放入待执行队列中序列化到redis,提供方法统一操作

public class CacheExecuteModel implements Serializable {    private String obejctClazzName;    private String cacheName;    private String key;    private BaseSystemObject value;    private String executeType;}private  CacheManager cacheManager = (CacheManager) SpringBootBeanUtil.getBean("ehCacheManager");    @Autowired    private RedisUtil redisUtil;    public void putCacheIntoTransition(){        String threadID = Thread.currentThread().getName();        System.out.println("init threadid:"+threadID);        CacheExecuteModel cacheExecuteModel = new CacheExecuteModel();        cacheExecuteModel.setExecuteType("option");        redisUtil.redisTemplateSetForCollection(threadID,cacheExecuteModel, GlobalEnum.RedisDBNum.Cache.get_value());        redisUtil.setExpire(threadID,5, TimeUnit.MINUTES, GlobalEnum.RedisDBNum.Cache.get_value());    }    public void putCache(String cacheName, String key, BaseSystemObject value) {        if(checkCacheOptinionInTransition()){            String threadID = Thread.currentThread().getName();            CacheExecuteModel cacheExecuteModel = new CacheExecuteModel("update", cacheName, key, value.getClass().getName(),value);            redisUtil.redisTemplateSetForCollection(threadID,cacheExecuteModel, GlobalEnum.RedisDBNum.Cache.get_value());            redisUtil.setExpire(threadID,5, TimeUnit.MINUTES, GlobalEnum.RedisDBNum.Cache.get_value());        }else{            executeUpdateOperation(cacheName,key,value);        }    }    public void deleteCache(String cacheName, String key) {        if(checkCacheOptinionInTransition()){            String threadID = Thread.currentThread().getName();            CacheExecuteModel cacheExecuteModel = new CacheExecuteModel("delete", cacheName, key);            redisUtil.redisTemplateSetForCollection(threadID,cacheExecuteModel, GlobalEnum.RedisDBNum.Cache.get_value());            redisUtil.setExpire(threadID,5, TimeUnit.MINUTES, GlobalEnum.RedisDBNum.Cache.get_value());        }else{            executeDeleteOperation(cacheName,key);        }    }    public void executeOperation(){        String threadID = Thread.currentThread().getName();        if(checkCacheOptinionInTransition()){            List<LinkedHashMap> executeList =  redisUtil.redisTemplateGetForCollectionAll(threadID, GlobalEnum.RedisDBNum.Cache.get_value());            for (LinkedHashMap obj:executeList) {                String executeType = ConvertOp.convert2String(obj.get("executeType"));                if(executeType.contains("option")){                    continue;                }                String obejctClazzName = ConvertOp.convert2String(obj.get("obejctClazzName"));                String cacheName = ConvertOp.convert2String(obj.get("cacheName"));                String key = ConvertOp.convert2String(obj.get("key"));                LinkedHashMap valueMap = (LinkedHashMap)obj.get("value");                String valueMapJson =  JSON.toJSONString(valueMap);                try{                    Object valueInstance = JSON.parseObject(valueMapJson,Class.forName(obejctClazzName));                    if(executeType.equals("update")){                        executeUpdateOperation(cacheName,key,(BaseSystemObject)valueInstance);                    }else if(executeType.equals("delete")){                        executeDeleteOperation(cacheName,key);                    }                }catch (Exception e){                    e.printStackTrace();                }            }            redisUtil.redisTemplateRemove(threadID,GlobalEnum.RedisDBNum.Cache.get_value());        }    }    public boolean checkCacheOptinionInTransition(){        String threadID = Thread.currentThread().getName();        System.out.println("check threadid:"+threadID);        return redisUtil.isValid(threadID, GlobalEnum.RedisDBNum.Cache.get_value());    }

到此,关于“SpringBoot整合Ehcache3的实现步骤是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯