1、应用场景
在系统设计中,需要考虑到系统性能方面的需求,比如课程列表那里需要对课程类别路径进行翻译,例如课程类别为 Spring, 需要翻译成:后端/Java/Spring,如果在接口调用过程中通过 for 循环每次都先通过课程的类别 id 去查询该类别及父类别,再把路径拼接出来,这样的操作会对系统性能造成影响。而且在 for 循环中调用微服务接口去访问服务,耗时会非常的大。
2、解决思路
在系统设计过程中,通过建立字典缓存工具类解决上诉问题,通过缓存一些常用的字典数据,提高系统性能和响应速度。系统主要采用了 Hutools 提供的 TimedCache 时间缓存工具,使用 TimedCache 可以大大降低频繁查询数据库的性能开销,避免因为数据查询造成的系统瓶颈,优化系统运行效率。同时,TimedCache 的自动过期策略也可以保证缓存数据的实效性,提高缓存数据的准确性。
3、代码实现
首先创建 TimedCache 缓存,并设置为默认一分钟过期,在静态代码块中启动定时任务,每 5 秒清理一次过期条目,接着定义方法 findDictByKindCode(String kind), 如果传递的参数为 KIND_SUBJECT_PATH , 会 通 过 微 服 务 接 口 调 用 去 获 取 课 程 类 别 路 径 字 典 , 返 回List<DictDTO>字典集合,DictDTO 对象中主要包含两个属性 code 和 detail,对于课程类别路径字典而言,code 为课程类别的 id, detail 为课程路径,然后定义 translateSubjectPath(Stringcode)方法,该方法用于课程类别字典的翻译,通过传入参数 code 就可以返回对应的课程路径。课程列表只需要在 for 循环中调用此方法就可以设置课程路径了,因为数据都放在缓存中,所以提高了系统性能和响应速度。关键代码如下
packagecn.sinobest.sinostar.base.utils; importcn.hutool.cache.CacheUtil; importcn.hutool.cache.impl.TimedCache; importcn.hutool.core.util.ObjectUtil; importcn.sinobest.sinostar.base.common.model.dto.DictDTO; importcn.sinobest.sinostar.base.modules.education.service.EducationFeignService; importorg.springframework.stereotype.Component; importjavax.annotation.Resource; importjava.util.Collections; importjava.util.List; importjava.util.Objects; /*** <p>* 字典缓存工具类* </p>*/publicclassDictCacheUtils { // 创建缓存,默认一分钟过期privatestaticfinalTimedCache<String, List<DictDTO>>DICT_CACHE=CacheUtil.newTimedCache(1000*60); static { // 启动定时任务,每5秒清理一次过期条目,注释此行首次启动仍会清理过期条目DICT_CACHE.schedulePrune(1000*5); } staticEducationFeignServiceeducationFeignService; publicvoidsetEducationFeignService(EducationFeignServiceeducationFeignService) { DictCacheUtils.educationFeignService=educationFeignService; } publicstaticfinalStringKIND_SUBJECT_PATH="subject_path"; publicstaticList<DictDTO>findDictByKindCode(Stringkind) { returnDICT_CACHE.get(kind, false, () -> { try { if (KIND_SUBJECT_PATH.equals(kind)) { returneducationFeignService.getSubjectPathDict(); } returnCollections.emptyList(); } catch (Throwablee) { e.printStackTrace(); } returnCollections.emptyList(); }); } publicstaticStringtranslateSubjectPath(Stringcode) { returntranslate(findDictByKindCode(KIND_SUBJECT_PATH), true, code); } publicstaticStringtranslate(List<DictDTO>dict, booleanisc2d, Stringv) { DictDTOd=dict.stream().filter(e->Objects.equals(isc2d?e.getCode() : e.getDetail(), v)).findFirst().orElse(null); if (ObjectUtil.isNull(d)) { returnnull; } returnisc2d?d.getDetail() : d.getCode(); } }
4、构建类别路径代码实现
publicList<DictDTO>getSubjectPathDict() { List<DictDTO>dictDTOList=newArrayList<>(); List<Subject>subjectList=lambdaQuery().eq(BaseModel::getDeleteFlag, DeleteFlagEnum.NORMAL.getCode()).list(); subjectList.forEach(e->buildSubjectPath(dictDTOList, subjectList, e.getId(), e.getParentId(), e.getName())); returndictDTOList; } privatevoidbuildSubjectPath(List<DictDTO>dictDTOList, List<Subject>subjectList, Stringid, StringparentId, Stringpath) { if (ROOT_NODE.equals(parentId)) { dictDTOList.add(newDictDTO().setCode(id).setDetail(path)); } else { Optional<Subject>parent=subjectList.stream().filter(e->StrUtil.equals(e.getId(), parentId)).findFirst(); if (parent.isPresent() &&path.split("/").length<=10) { path=parent.get().getName() +"/"+path; buildSubjectPath(dictDTOList, subjectList, id, parent.get().getParentId(), path); } else { dictDTOList.add(newDictDTO().setCode(id).setDetail(path)); } } }