SpringBoot学习项目-个人博客系统-part7
2021/9/21 23:14:22
本文主要是介绍SpringBoot学习项目-个人博客系统-part7,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章详情
- 根据文档可知请求地址、请求方式、请求参数。
ArticleController
- 因为是文章的,所以还是使用关于文章的ArticleController
@PostMapping("/view/{id}") public Result findArticleById(@PathVariable("id") Long id){ return articleService.findArticleById(id); }
- 根据返回数据形式,返回的是查到的文章详情。
ArticleService
/** * 查看文章详情 * @param articleId * @return */ Result findArticleById(Long articleId);
ArticleServiceImpl
- 因为在根据Id查找文章的时候,不仅需要显示文章信息,同时也需要标签信息,作者信息、内容信息以及类别信息,所以涉及到的表:文章表、标签表、用户表、内容表、以及类别表
- 将需要的Service进行注入
- 因为返回的是一个ArticleVo类,因为在文章详情涉及的信息很多,封装一个类专门用来表示
ArticleVo
@Data public class ArticleVo { // @JsonSerialize(using = ToStringSerializer.class) private String id; private String title; private String summary; private Integer commentCounts; private Integer viewCounts; private Integer weight; /** * 创建时间 */ private String createDate; private String author; private ArticleBodyVo body; private List<TagVo> tags; private CategoryVo category; }
CategoryVo
@Data public class CategoryVo { private String id; private String avatar; private String categoryName; // private String description; }
@Override public Result findArticleById(Long articleId) { // 1. 根据id查询文章信息 // 2. 根据bodyId和categoryId去做关联查询 Article article = articleMapper.selectById(articleId); ArticleVo articleVo = copy(article,true,true,true,true); return Result.success(articleVo);
//用来将传入的Article对象的属性复制给articleVo对象中去 private ArticleVo copy(Article article, boolean isTag, boolean isAuthor, boolean isBody,boolean isCategory){ ArticleVo articleVo = new ArticleVo(); articleVo.setId(String.valueOf(article.getId())); BeanUtils.copyProperties(article,articleVo); articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-MM-dd HH:mm")); //并不是所有的接口 都需要标签 ,作者信息 if (isTag){ Long articleId = article.getId(); articleVo.setTags(tagService.findTagsByArticleId(articleId)); } if (isAuthor){ Long authorId = article.getAuthorId(); articleVo.setAuthor(sysUserService.findUserById(authorId).getNickname()); } if (isBody){ Long bodyId = article.getBodyId(); articleVo.setBody(findArticleBodyById(bodyId)); } if (isCategory){ Long categoryId = article.getCategoryId(); articleVo.setCategory(categoryService.findCategoryById(categoryId)); } return articleVo; }
TagService
List<TagVo> findTagsByArticleId(Long articleId);
TagServiceImpl
SysUserService
SysUser findUserById(Long id);
CategoryService
CategoryVo findCategoryById(Long categoryId);
CategoryServiceImpl
@Override public SysUser findUserById(Long id) { SysUser sysUser = sysUserMapper.selectById(id); if(sysUser == null){ sysUser = new SysUser(); sysUser.setNickname("lbj"); } return sysUser; }
CategoryServiceImpl
@Override public CategoryVo findCategoryById(Long categoryId) { Category category = categoryMapper.selectById(categoryId); CategoryVo categoryVo = new CategoryVo(); BeanUtils.copyProperties(category,categoryVo); return categoryVo; }
文章阅读次数更新
- 使用线程池
线程池配置
@Configuration @EnableAsync // 开启多线程 public class ThreadPoolConfig { @Bean("taskExecutor") public Executor asyncServiceExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 设置核心线程数 executor.setCorePoolSize(5); // 设置最大线程数 executor.setMaxPoolSize(20); //配置队列大小 executor.setQueueCapacity(Integer.MAX_VALUE); // 设置线程活跃时间(秒) executor.setKeepAliveSeconds(60); // 设置默认线程名称 executor.setThreadNamePrefix("lbj博客项目"); // 等待所有任务结束后再关闭线程池 executor.setWaitForTasksToCompleteOnShutdown(true); //执行初始化 executor.initialize(); return executor; } }
使用
@Component public class ThreadService { //此操作在线程池中执行,不会影响原有的主线程 @Async("taskExecutor") public void updateArticleViewCount(ArticleMapper articleMapper, Article article) { Integer viewCounts = article.getViewCounts(); Article articleUpdate = new Article(); articleUpdate.setViewCounts(viewCounts + 1); LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Article::getId,article.getId()); // 多线程环境下保证线程安全,类似于乐观锁,检查是否相等,相等了才进行+1操作 wrapper.eq(Article::getViewCounts, viewCounts); // update article set view_count=100 where view_count=99 and id=11 articleMapper.update(articleUpdate,wrapper); // try { // Thread.sleep(5000); // System.out.println("更新完成"); // } catch (InterruptedException e) { // e.printStackTrace(); // } } }
ArticleServiceImpl
@Override public Result findArticleById(Long articleId) { // 1. 根据id查询文章信息 // 2. 根据bodyId和categoryId去做关联查询 Article article = articleMapper.selectById(articleId); ArticleVo articleVo = copy(article,true,true,true,true); // 查看完文章,新增阅读数,有没有问题? // 查看完文章之后,本应直接返回数据,这时候做一个更新操作,更新是加写锁的,会阻塞其他的读操作,性能就会比较低 // 更新增加了此次接口的耗时,如果一旦更新出问题,不能影响查看文章的操作。 // 使用线程池,可以把更新操作扔到线程池中去执行,和主线程就不相关 threadService.updateArticleViewCount(articleMapper,article); return Result.success(articleVo); }
- 有几个注意的点:
- 线程池配置的时候开使用注解
@EnableAsync
开启多线程 - 给线程池取名
@Bean(value = "taskExecutor")
。 - 在ThreadService中要注意标识接下来的操作是在线程池中进行,不会影响原有的主线程,
@Async("taskExecutor")
。确定在线程池中进行 - 在多线程环境下,要保证线程安全,
wrapper.eq(Article::getViewCounts, viewCounts);
,这句话表明,先检查原先阅读数和当前阅读是是否一致,只有一致的时候才进行+1,类似于乐观锁
- 线程池配置的时候开使用注解
- 思考:为什么这里更新阅读数要使用一个线程进行?
- 因为如果在主方法中进行更新,因为更新是一个写锁的操作,会阻塞其他的读操作,那么同步进行的话,就会造成更新的时候无法读取到文章内容。
- 同时一旦更新出问题,使用一个线程去执行更新操作,那么也不会影响查看文章这个操作。
这篇关于SpringBoot学习项目-个人博客系统-part7的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-30java最新版本是什么,有什么特性?-icode9专业技术文章分享
- 2024-11-30[开源]27.8K star!这款 Postman 替代工具太火了!
- 2024-11-30Gzip 压缩入门教程:轻松掌握文件压缩技巧
- 2024-11-29开源工具的魅力:让文档管理更“聪明”
- 2024-11-29Release-it开发入门教程
- 2024-11-29Rollup 插件入门教程:轻松掌握模块打包
- 2024-11-29从零到一,产品经理如何玩转项目管理和团队协作
- 2024-11-29如何通过精益生产管理工具帮助项目团队实现精准进度控制?
- 2024-11-29低代码应用开发课程:新手入门与基础教程
- 2024-11-29入门指南:全栈低代码开发课程