隐藏页面特效

个人博客项目笔记_09

1|01. 归档文章列表


1|11.1 接口说明


接口url:/articles

请求方式:POST

请求参数:

参数名称 参数类型 说明
year string
month string

返回数据:

{ "success": true, "code": 200, "msg": "success", "data": [文章列表,数据同之前的文章列表接口] }

1|21.2 文章列表参数


新增参数:

​ private String year;

​ private String month;

月份特殊处理:6月——>06月

public String getMonth(){
if (this.month != null && this.month.length() == 1){
return "0"+this.month;
}
return this.month;
}

package com.cherriesovo.blog.vo.params; import lombok.Data; @Data public class PageParams { private int page = 1; private int pageSize = 10; private Long categoryId; private Long tagId; private String year; private String month; public String getMonth(){ if (this.month != null && this.month.length() == 1){ return "0"+this.month; } return this.month; } }

1|31.3 使用自定义sql 实现文章列表


ArticleMapper:

listArticle():根据给定的条件查询文章,并且支持分页功能:

  • page: 这是一个分页对象,用于指定查询的页码和每页显示的数据量。
  • categoryId: 要查询的文章所属的分类 ID。
  • tagId: 要查询的文章关联的标签 ID。
  • year: 要查询的文章的发布年份。
  • month: 要查询的文章的发布月份。

这个方法会返回一个分页后的文章列表。

IPage<Article> listArticle(Page<Article> page, Long categoryId, Long tagId, String year, String month);

ArticleServiceImpl:重写方法listArticlesPage()

IPage<Article> 是 MyBatis-Plus 框架中用于分页查询结果的接口。它表示了一个分页后的文章列表,包括了查询结果的分页信息和实际的文章数据列表。

在这个接口中,可以通过以下方法获取分页信息和文章列表:

  • getRecords(): 获取当前页的文章列表。
  • getTotal(): 获取符合查询条件的总文章数。
  • getCurrent(): 获取当前页码。
  • getPages(): 获取总页数。
  • getSize(): 获取当前页的文章数量。
  • hasNext(): 是否有下一页。
  • hasPrevious(): 是否有上一页。
@Override public List<ArticleVo> listArticlesPage(PageParams pageParams) { //创建了一个用于分页查询的 Page 对象 Page<Article> page = new Page<>(pageParams.getPage(),pageParams.getPageSize()); IPage<Article> articleIPage = this.articleMapper.listArticle(//调用 listArticle 方法来查询文章 page, pageParams.getCategoryId(), pageParams.getTagId(), pageParams.getYear(), pageParams.getMonth()); List<Article> records = articleIPage.getRecords(); List<ArticleVo> articleVoList = copyList(records,true,false,true); return articleVoList; }

ArticleMapper.xml:

<resultMap id="articleMap" type="com.cherriesovo.blog.dao.pojo.Article"> <id column="id" property="id" /> <result column="author_id" property="authorId"/> <result column="comment_counts" property="commentCounts"/> <result column="create_date" property="createDate"/> <result column="summary" property="summary"/> <result column="title" property="title"/> <result column="view_counts" property="viewCounts"/> <result column="weight" property="weight"/> <result column="body_id" property="bodyId"/> <result column="category_id" property="categoryId"/> </resultMap> <select id="listArticle" resultMap="articleMap"> select * from ms_article <where> 1 = 1 <if test="categoryId != null"> and category_id = #{categoryId} </if> <if test="year != null and year.length>0 and month != null and month.length>0"> and ( FROM_UNIXTIME(create_date/1000,'%Y') = #{year} and FROM_UNIXTIME(create_date/1000,'%m') = #{month} ) </if> <if test="tagId != null"> and id in (select article_id from ms_article_tag where tag_id=#{tagId}) </if> </where> order by weight desc,create_date desc </select>

1|41.4 测试


2|02. 统一缓存处理(优化)


内存的访问速度 远远大于 磁盘的访问速度 (1000倍起)

使用AOP开始优化

切点:

package com.cherriesovo.blog.common.cache; import java.lang.annotation.*; @Target({ElementType.METHOD}) //表明这个 Cache 注解只能用于方法上 @Retention(RetentionPolicy.RUNTIME) //表示 Cache 注解的生命周期为运行时 @Documented //Cache 注解应该被 javadoc 工具记录 public @interface Cache { long expire() default 1 * 60 * 1000; //缓存的过期时间,单位为毫秒,默认值为 1 分钟 String name() default ""; //缓存标识,用于区分不同的缓存数据。默认为空字符串 }

Method method = pjp.getSignature().getDeclaringType().getMethod(methodName, parameterTypes);

通过反射获取切点方法对应的 Method 对象。

  • pjp.getSignature().getDeclaringType(): 首先通过 pjp.getSignature() 获取切点方法的签名信息,然后调用 getDeclaringType() 方法获取声明该方法的类的 Class 对象。
  • getMethod(methodName, parameterTypes): 在获取到声明该方法的类的 Class 对象后,调用 getMethod() 方法获取指定方法名和参数类型的 Method 对象。这个方法需要传入两个参数,第一个是方法名 methodName,第二个是参数类型数组 parameterTypes

这行代码的作用是获取切点方法对应的 Method 对象,可以通过该对象进行一些反射操作,比如调用方法、获取方法的修饰符等。

package com.cherriesovo.blog.common.cache; import com.alibaba.fastjson.JSON; import com.cherriesovo.blog.vo.Result; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.AliasFor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.time.Duration; //AOP定义一个切面,切面定义了切点和通知的关系 @Aspect @Component @Slf4j public class CacheAspect { @Autowired private RedisTemplate<String, String> redisTemplate; //切点,匹配带有 @Cache 注解的方法 @Pointcut("@annotation(com.cherriesovo.blog.common.cache.Cache)") public void pt(){} //切点的实际定义,名称为 pt。它没有参数和实现,因为它只是用来定义切点,而不执行任何实际的逻辑。 //通知,通知关联了切点 @Around("pt()") public Object around(ProceedingJoinPoint pjp){ try { Signature signature = pjp.getSignature(); //获取了切点方法的签名信息 //获取了切点方法所属类的简单类名 String className = pjp.getTarget().getClass().getSimpleName(); //获取了切点方法的方法名 String methodName = signature.getName(); //定义了一个数组用于存储切点方法的参数类型。pjp.getArgs() 返回的是切点方法的参数列表,通过遍历参数列表,可以逐个获取参数的类型,并存储到 parameterTypes 数组中。 Class[] parameterTypes = new Class[pjp.getArgs().length]; Object[] args = pjp.getArgs(); //获取了切点方法的参数值列表 //将切点方法的参数值转换为字符串,并且将参数的类型存储到 parameterTypes 数组中。 String params = ""; //参数 for(int i=0; i<args.length; i++) { if(args[i] != null) { params += JSON.toJSONString(args[i]); //将参数值追加到 params 字符串后面 parameterTypes[i] = args[i].getClass(); }else { parameterTypes[i] = null; } } if (StringUtils.isNotEmpty(params)) { //加密 以防出现key过长以及字符转义获取不到的情况 params = DigestUtils.md5Hex(params); } Method method = pjp.getSignature().getDeclaringType().getMethod(methodName, parameterTypes); //获取方法上的Cache注解 Cache annotation = method.getAnnotation(Cache.class); //从注解中获取缓存过期时间 long expire = annotation.expire(); //从注解中获取缓存名称 String name = annotation.name(); //先从redis获取数据 String redisKey = name + "::" + className+"::"+methodName+"::"+params; //唯一标识缓存中的数据 //通过 Redis 模板的 opsForValue() 方法获取了一个操作字符串的操作对象,并调用了 get(redisKey) 方法,尝试从 Redis 中 根据 redisKey 获取对应的缓存数据。 String redisValue = redisTemplate.opsForValue().get(redisKey); if (StringUtils.isNotEmpty(redisValue)){ log.info("走了缓存~~~,{},{}",className,methodName); //将获取到的 JSON 格式的字符串 redisValue 解析为 Result 类型的对象返回 return JSON.parseObject(redisValue, Result.class); } //在缓存未命中时,执行方法的实际逻辑并将结果存入 Redis 缓存中 Object proceed = pjp.proceed(); redisTemplate.opsForValue().set(redisKey,JSON.toJSONString(proceed), Duration.ofMillis(expire)); log.info("存入缓存~~~ {},{}",className,methodName); return proceed; } catch (Throwable throwable) { throwable.printStackTrace(); } return Result.fail(-999,"系统错误"); } }

使用(对最热文章进行缓存处理):

在想要添加缓存的接口上添加:@Cache(expire = 5 * 60 * 1000,name = "hot_article")

@PostMapping("hot") @Cache(expire = 5 * 60 * 1000,name = "hot_article") //指定了缓存的过期时间 5 分钟 public Result hotArticle(){ int limit = 5; return articleService.hotArticle(limit); }

__EOF__

本文作者CherriesOvO
本文链接https://www.cnblogs.com/zyj3955/p/18131806.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   CherriesOvO  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
历史上的今天:
2022-04-12 操作系统学习笔记_05
2021-04-12 全国疫情数据可视化展示(详细介绍,含完整源码)
点击右上角即可分享
微信分享提示