Mr丶冷文

文章 分类 评论
125 10 8391

站点介绍

冷文学习者(KEVINLU98.COM),记录一个北漂小码农的日常业余生活
友链申请地址(直接评论即可): 传送门

(十四)缓存处理及首页文章的列表展示

MR丶冷文 2022-10-11 1062 0条评论 个人博客项目视频教程 javaspringboot个人博客博客系统

首页 / 正文
Freewind主题v1.5版本已发布,下载请移步Freewind 1.5,同时还有主题伴生插件Freewind Markdown,下载请移步 Freewind Markdown,有问题请在留言板,交换友链请直接在友链留言,我创建了一个主题交流群,有兴趣可以加下: 点此加入
报毒我说明一下,是因为我把主题的版权信息做了加密,其中用了eval,杀毒软件认为eval函数是一个危险的操作,这点介意的话请勿下载,我也没有强迫任何人去下载,也没有向大家收取一分钱的主题费用,所以也犯不着因为这些事情来喷我,喜欢就用,不喜欢就不用,就这么简单

发布于2022-10-28

说明

上节课我们已经将前台页面公共部分替换成了动态的,但这个时候就有问题了,因为我们每次访问其它页面的时候都会去查多次数据库,这样会加大数据库的压力,也会拖慢我们网站的速度,因为我们知道一个程序的性能瓶颈大部分情况下是由IO性能决定的,而内存的性能比磁盘快了不知多少倍,所以我们这节课会为我们的项目加上一个缓存

缓存处理

我们项目使用的缓存是ehcache这个缓存中间件,可以看下百度词条对其的描述https://baike.baidu.com/item/ehcache/6036099?fr=aladdin

我们可以利用springboot-cache帮我们集成ehcache,这样我们只用简单的做几个配置就可以了

准备工作

  • 引入springboot-cache的依赖及ehcache的依赖
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
  <groupId>net.sf.ehcache</groupId>
  <artifactId>ehcache</artifactId>
</dependency>
  • application.yml中配置springboot-cache让其使用ehcache,并指定ehcache的配置文件路径
cache:
  type: ehcache
  ehcache:
      config: classpath:/ehcache-spring.xml
  • 创建ehcache的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false">

  <!-- 磁盘缓存位置 -->
  <diskStore path="./cache"/>

  <!-- maxElementsInMemory: 在内存中缓存的element的最大数目。-->
  <!-- eternal:elements是否永久有效,如果为true,timeouts将被忽略,element将永不过期 -->
  <!-- timeToIdleSeconds:发呆秒数,发呆期间未访问缓存立即过期,当eternal为false时,这个属性才有效,0为不限制 -->
  <!-- timeToLiveSeconds:总存活秒数,当eternal为false时,这个属性才有效,0为不限制 -->
  <!-- overflowToDisk: 如果内存中数据超过内存限制,是否要缓存到磁盘上 -->
  <!-- statistics:是否收集统计信息。如果需要监控缓存使用情况,应该打开这个选项。默认为关闭(统计会影响性能)。设置statistics="true"开启统计 -->

  <!--
        默认缓存
        无过期时间,但 3600 秒内无人访问缓存立即过期
    -->
  <cache name="blog-cache"
         maxElementsInMemory="1000"
         timeToIdleSeconds="3600"
         timeToLiveSeconds="0"
         overflowToDisk="true">
  </cache>
</ehcache>

自定义缓存Key的生成策略

spring cache缓存的key默认是通过KeyGenerator生成的,其默认生成策略如下

  • 如果方法没有参数,则使用0作为key
  • 如果只有一个参数的话则使用该参数作为key
  • 如果参数多于一个的话则使用所有参数的hashCode作为key

可以看出默认的key生成策略中并没有涉及方法名称和类,这就意味着如果我们有两个参数列表相同的方法,我们用相同的参数分别调用两个方法,当调用第二个方法的时候,spring cache将会返回缓存中的第一个方法的缓存值,因为他们的key是一样的

  • 我们可以去自定一个CacheConfig的类用作缓存的配置,继承一下CachingConfigurerSupport类,覆写它的keyGenerator方法
  • 然后我们去定义一个BlogCacheKey的内部类,把调用缓存的类名、方法名、参数列表传过来,在这个类里覆写一下它的equals方法,综合考虑上述参数完成key的定义
package cn.kevinlu98.config;

import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.util.StringUtils;

import java.util.Arrays;

/**
 * Author: Mr丶冷文
 * Date: 2022/10/11 16:37
 * Email: kevinlu98@qq.com
 * Description:
 */
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> new BlogCacheKey(target.getClass().getName(),method.getName(),params);
    }

    static class BlogCacheKey {
        private final String className;
        private final String methodName;
        private final Object[] params;

        private final int hashCode;

        public BlogCacheKey(String className, String methodName, Object[] params) {
            this.className = className;
            this.methodName = methodName;
            this.params = params;
            String sign = className + "_" + methodName + "_" + Arrays.deepHashCode(params);
            this.hashCode = sign.hashCode();
        }

        @Override
        public int hashCode() {
            return this.hashCode;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == this) return true;
            if (!(obj instanceof BlogCacheKey)) return false;
            BlogCacheKey other = (BlogCacheKey) obj;
            if (this.hashCode == other.hashCode) return true;
            return StringUtils.equals(this.className, other.className)
                    && StringUtils.equals(this.methodName, other.methodName)
                    && Arrays.deepEquals(this.params, other.params);
        }
    }
}

启用缓存

  • 在启动类上加上EnableCaching的注解开启缓存
  • 给我们要缓存的对象对应类实现下Serializable接口
  • 我们在查询友情链接及导航栏还有轮播图这里先加上缓存注解CacheConfig,然后在需要加缓存的方法上加上Cacheabel
  • 在更新及删除操作时清空缓存CacheEvict
package cn.kevinlu98.service;

import cn.kevinlu98.mapper.FriendlyMapper;
import cn.kevinlu98.pojo.Friendly;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * Author: Mr丶冷文
 * Date: 2022/10/6 22:20
 * Email: kevinlu98@qq.com
 * Description:
 */
@CacheConfig(cacheNames = {"blog-cache"})
@Service
public class FriendlyService {

  private final FriendlyMapper mapper;

  public FriendlyService(FriendlyMapper mapper) {
    this.mapper = mapper;
  }

  /**
     * 查询所有的友情链接
     *
     * @return 友情链接列表
     */
  @Cacheable
  public List<Friendly> list() {
    return mapper.findAll();
  }

  @CacheEvict(allEntries = true)
  public void save(Friendly friendly) {
    mapper.save(friendly);
  }

  /**
     * 根据id从数据库表中删除数据
     *
     * @param id 主键
     */
  @CacheEvict(allEntries = true)
  public void delete(Integer id) {
    mapper.deleteById(id);
  }
}

首页文章列表的展示

  • index方法加上pn的参数用于标识是分页
  • 我们可以直接调用之前我们写的search方法来进行搜索
@GetMapping("/")
public String index(@RequestParam(required = false, defaultValue = "1") Integer pn, Model model) {
  pn = pn < 1 ? 1 : pn;
  model.addAttribute("articlePage", articleService.search(ArticleSearch.builder().pageSize(2).pageNum(pn).status(Article.STATUS_PUBLISH).type(Article.TYPE_ARTICLE).build()));
  model.addAttribute("banners", bannerService.list());
  return "index";
}
  • 补充评论数
// commentMapper
long countByArticle(Article article);
//search
return PageHelper.<Article>builder()
  .rows(articlePage.getContent().stream().peek(x -> x.setCommentCount(commentMapper.countByArticle(x))).collect(Collectors.toList()))
  .total(articlePage.getTotalElements())
  .current(search.getPageNum())
  .totalPage(articlePage.getTotalPages())
  .build();
  • 首页文章列表展示
<article th:each="article:${articlePage.rows}" class="lw-article-item lw-posr">
  <div class="lw-article-cover lw-posa lw-xs-hidden">
    <img th:src="${@defaultImage.cover(article.cover)}" alt="">
  </div>
  <div class="lw-article-info">
    <h2>
      <a class="lw-xs-hidden"><span class="lw-category" th:text="${article.category.name}"></span></a>
      <a th:href="@{/{id}.html(id=${article.id})}" th:text="${article.title}"></a>
    </h2>
    <p class="lw-desc" th:text="${article.summary()}"></p>
    <p class="lw-text-hidden lw-article-more">
      <i class="fa fa-clock-o lw-mr5"></i> <th:block th:text="${#dates.format(article.created,'yyyy-MM-dd HH:mm')}"></th:block>
      <i class="fa fa-eye lw-mr5 lw-ml10"></i><th:block th:text="${article.views}"></th:block>
      <i class="fa fa-comment lw-ml10 lw-mr5"></i><th:block th:text="${article.commentCount}"></th:block>
    </p>
  </div>
</article>
  • 分页部分处理
<ul th:if="${articlePage.totalPage>1}" class="lw-pagenation">
  <li th:if="${articlePage.current != 1}"><a th:href="@{/}">首页</a></li>
  <li th:each="num:${#numbers.sequence(1,articlePage.totalPage)}">
    <a th:class="${articlePage.current eq num}?'lw-active':''" th:href="@{/(pn=${num})}" th:text="${num}"></a>
  </li>
  <li th:if="${articlePage.current != articlePage.totalPage}">
    <a th:href="@{/(pn=${articlePage.totalPage})}">尾页</a>
  </li>
</ul>

评论(0)

热门文章

最新评论

  • 1

    看看

  • eeee

    多谢大佬分享

  • asdasd

    强强强

  • asdasd

    感谢作者!

  • asdasd

    感谢!

日历

2024年11月

     12
3456789
10111213141516
17181920212223
24252627282930

文章目录

站点公告
Freewind主题v1.5版本已发布,下载请移步Freewind 1.5,同时还有主题伴生插件Freewind Markdown,下载请移步 Freewind Markdown,有问题请在留言板,交换友链请直接在友链留言,我创建了一个主题交流群,有兴趣可以加下: 点此加入
报毒我说明一下,是因为我把主题的版权信息做了加密,其中用了eval,杀毒软件认为eval函数是一个危险的操作,这点介意的话请勿下载,我也没有强迫任何人去下载,也没有向大家收取一分钱的主题费用,所以也犯不着因为这些事情来喷我,喜欢就用,不喜欢就不用,就这么简单
点击小铃铛关闭