学会主流的 Java Web 开发技术和框架(Spring、SpringBoot、Spring MVC、MyBatis、MySQL、Redis、Kafka、Elasticsearch、Spring Security 等)
了解一个真实的 Web 项目从开发到部署的整个流程(本项目配套有大量图例和详细教程,以帮助小伙伴快速上手)
掌握本项目中涉及的核心技术点以及常见面试题和解析
项目托管在 Github 和 Gitee:
在线体验:项目已经部署到腾讯云服务器,各位小伙伴们可直接线上体验:http://1.15.127.74/
已内置三种不同身份的用户:
username | password | 特殊权限 | |
---|---|---|---|
管理员 | admin | admin | 数据统计、删除帖子 |
版主 | master | master | 置顶帖子、加精帖子 |
普通用户 | user | user |
文档地址:文档通过 Docsify + Gitee Pages 生成,在线访问地址:https://veal98.gitee.io/echo
后端:
Spring
Spring Boot 2.1.5 RELEASE
Spring MVC
ORM:MyBatis
数据库:MySQL 5.7
分布式缓存:Redis
本地缓存:Caffeine
消息队列:Kafka 2.13-2.7.0
搜索引擎:Elasticsearch 6.4.3
安全:Spring Security
邮件任务:Spring Mail
分布式定时任务:Spring Quartz
日志:SLF4J(日志接口) + Logback(日志实现)
前端:
Thymeleaf
Bootstrap 4.x
Jquery
Ajax
操作系统:Windows 10
构建工具:Apache Maven
集成开发工具:Intellij IDEA
应用服务器:Apache Tomcat
接口测试工具:Postman
压力测试工具:Apache JMeter
版本控制工具:Git
Java 版本:8
首页:
登录页:
帖子详情页:
个人主页:
朋友私信页:
私信详情页:
系统通知页:
通知详情页:
账号设置页:
数据统计页:
搜索详情页:
用户注册成功,将用户信息存入 MySQL,但此时该用户状态为未激活
向用户发送激活邮件,用户点击链接则激活账号(Spring Mail)
进入登录界面,动态生成验证码,并将验证码短暂存入 Redis(60 秒)
用户登录成功(验证用户名、密码、验证码),生成登录凭证且设置状态为有效,并将登录凭证存入 Redis 注意:登录凭证存在有效期,在所有的请求执行之前,都会检查凭证是否有效和是否过期,只要该用户的凭证有效并在有效期时间内,本次请求就会一直持有该用户信息(使用 ThreadLocal 持有用户信息)
勾选记住我,则延长登录凭证有效时间
用户登录成功,将用户信息短暂存入 Redis(1 小时)
用户登出,将凭证状态设为无效,并更新 Redis 中该用户的登录凭证信息
修改头像
将用户选择的头像图片文件上传至七牛云服务器
修改密码
发布帖子(过滤敏感词),将其存入 MySQL
分页显示所有的帖子
支持按照 “发帖时间” 显示
支持按照 “热度排行” 显示(Spring Quartz)
查看帖子详情
权限管理(Spring Security + Thymeleaf Security)
未登录用户无法发帖
“版主” 可以看到帖子的置顶和加精按钮并执行相应操作
“管理员” 可以看到帖子的删除按钮并执行相应操作
“普通用户” 无法看到帖子的置顶、加精、删除按钮,也无法执行相应操作
发布对帖子的评论(过滤敏感词),将其存入 MySQL
分页显示评论
发布对评论的回复(过滤敏感词)
权限管理(Spring Security)
未登录用户无法使用评论功能
发送私信(过滤敏感词)
私信列表
查询当前用户的会话列表
每个会话只显示一条最新的私信
支持分页显示
私信详情
查询某个会话所包含的所有私信
访问私信详情时,将显示的私信设为已读状态
支持分页显示
权限管理(Spring Security)
未登录用户无法使用私信功能
普通请求异常
异步请求异常
支持对帖子、评论/回复点赞
第 1 次点赞,第 2 次取消点赞
首页统计帖子的点赞数量
详情页统计帖子和评论/回复的点赞数量
详情页显示当前登录用户的点赞状态(赞过了则显示已赞)
统计我的获赞数量
权限管理(Spring Security)
未登录用户无法使用点赞相关功能
关注功能
取消关注功能
统计用户的关注数和粉丝数
我的关注列表(查询某个用户关注的人),支持分页
我的粉丝列表(查询某个用户的粉丝),支持分页
权限管理(Spring Security)
未登录用户无法使用关注相关功能
通知列表
显示评论、点赞、关注三种类型的通知
通知详情
分页显示某一类主题所包含的通知
进入某种类型的系统通知详情,则将该页的所有未读的系统通知状态设置为已读
未读数量
分别显示每种类型的系统通知的未读数量
显示所有系统通知的未读数量
导航栏显示所有消息的未读数量(未读私信 + 未读系统通知)
权限管理(Spring Security)
未登录用户无法使用系统通知功能
发布事件
发布帖子时,通过消息队列将帖子异步地提交到 Elasticsearch 服务器
为帖子增加评论时,通过消息队列将帖子异步地提交到 Elasticsearch 服务器
搜索服务
从 Elasticsearch 服务器搜索帖子
从 Elasticsearch 服务器删除帖子(当帖子从数据库中被删除时)
显示搜索结果
独立访客 UV
存入 Redis 的 HyperLogLog
支持单日查询和区间日期查询
日活跃用户 DAU
存入 Redis 的 Bitmap
支持单日查询和区间日期查询
权限管理(Spring Security)
只有管理员可以查看网站数据统计
使用本地缓存 Caffeine 缓存热帖列表以及所有用户帖子的总数
以下是我个人发现的本项目存在的问题,但是暂时没有头绪无法解决,集思广益,欢迎各位小伙伴提 PR 解决:
以下是我觉得本项目还可以添加的功能,同样欢迎各位小伙伴提 issue 指出还可以增加哪些功能,或者直接提 PR 实现该功能:
各位如果需要将项目部署在本地进行测试,以下环境请提前备好:
Java 8
MySQL 5.7
Redis
Kafka 2.13-2.7.0
Elasticsearch 6.4.3
然后修改配置文件中的信息为你自己的本地环境,直接运行是运行不了的,而且相关私密信息我全部用 xxxxxxx 代替了。
本地运行需要修改的配置文件信息如下:
1)application-develop.properties
:
MySQL
Spring Mail(邮箱需要开启 SMTP 服务)
Kafka:consumer.group-id(该字段见 Kafka 安装包中的 consumer.proerties,可自行修改, 修改完毕后需要重启 Kafka)
Elasticsearch:cluster-name(该字段见 Elasticsearch 安装包中的 elasticsearch.yml,可自行修改)
七牛云(需要新建一个七牛云的对象存储空间,用来存放上传的头像图片)
2)logback-spring-develop.xml
:
LOG_PATH:日志存放的位置
每次运行需要打开:
MySQL
Redis
Elasticsearch
Kafka
另外,还需要事件建好数据库表,详细见下文。
用户 user
:
DROP TABLE IF EXISTS `user`; SET character_set_client = utf8mb4 ; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(50) DEFAULT NULL, `password` varchar(50) DEFAULT NULL, `salt` varchar(50) DEFAULT NULL, `email` varchar(100) DEFAULT NULL, `type` int(11) DEFAULT NULL COMMENT '0-普通用户; 1-超级管理员; 2-版主;', `status` int(11) DEFAULT NULL COMMENT '0-未激活; 1-已激活;', `activation_code` varchar(100) DEFAULT NULL, `header_url` varchar(200) DEFAULT NULL, `create_time` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `index_username` (`username`(20)), KEY `index_email` (`email`(20)) ) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;
讨论帖 discuss_post
:
DROP TABLE IF EXISTS `discuss_post`; SET character_set_client = utf8mb4 ; CREATE TABLE `discuss_post` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) DEFAULT NULL, `title` varchar(100) DEFAULT NULL, `content` text, `type` int(11) DEFAULT NULL COMMENT '0-普通; 1-置顶;', `status` int(11) DEFAULT NULL COMMENT '0-正常; 1-精华; 2-拉黑;', `create_time` timestamp NULL DEFAULT NULL, `comment_count` int(11) DEFAULT NULL, `score` double DEFAULT NULL, PRIMARY KEY (`id`), KEY `index_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
评论(回复)comment
:
CREATE TABLE `comment` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) DEFAULT NULL, `entity_type` int(11) DEFAULT NULL COMMENT '评论目标的类别:1 帖子;2 评论 ', `entity_id` int(11) DEFAULT NULL COMMENT '评论目标的 id', `target_id` int(11) DEFAULT NULL COMMENT '指明对谁进行评论', `content` text, `status` int(11) DEFAULT NULL COMMENT '状态:0 正常;1 禁用', `create_time` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `index_user_id` (`user_id`), KEY `index_entity_id` (`entity_id`) ) ENGINE=InnoDB AUTO_INCREMENT=247 DEFAULT CHARSET=utf8;
私信 message
:
DROP TABLE IF EXISTS `message`; SET character_set_client = utf8mb4 ; CREATE TABLE `message` ( `id` int(11) NOT NULL AUTO_INCREMENT, `from_id` int(11) DEFAULT NULL, `to_id` int(11) DEFAULT NULL, `conversation_id` varchar(45) NOT NULL, `content` text, `status` int(11) DEFAULT NULL COMMENT '0-未读;1-已读;2-删除;', `create_time` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `index_from_id` (`from_id`), KEY `index_to_id` (`to_id`), KEY `index_conversation_id` (`conversation_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
我每个都只部署了一台,以下是理想的部署架构:
画了一些不是那么严谨的图帮助各位小伙伴理清思绪。
单向绿色箭头:
前端模板 -> Controller:表示这个前端模板中有一个超链接是由这个 Controller 处理的
Controller -> 前端模板:表示这个 Controller 会像该前端模板传递数据或者跳转
双向绿色箭头:表示 Controller 和前端模板之间进行参数的相互传递或使用
单向蓝色箭头: A -> B,表示 A 方法调用了 B 方法
单向红色箭头:数据库或缓存操作
用户注册成功,将用户信息存入 MySQL,但此时该用户状态为未激活
向用户发送激活邮件,用户点击链接则激活账号(Spring Mail)
进入登录界面,动态生成验证码,并将验证码短暂存入 Redis(60 秒)
用户登录成功(验证用户名、密码、验证码),生成登录凭证且设置状态为有效,并将登录凭证存入 Redis
注意:登录凭证存在有效期,在所有的请求执行之前,都会检查凭证是否有效和是否过期,只要该用户的凭证有效并在有效期时间内,本次请求就会一直持有该用户信息(使用 ThreadLocal 持有用户信息)
勾选记住我,则延长登录凭证有效时间
用户登录成功,将用户信息短暂存入 Redis(1 小时)
用户登出,将凭证状态设为无效,并更新 Redis 中该用户的登录凭证信息
下图是登录模块的功能逻辑图,并没有使用 Spring Security 提供的认证逻辑(我觉得这个模块是最复杂的,这张图其实很多细节还没有画全)
支持按照 “发帖时间” 显示
支持按照 “热度排行” 显示(Spring Quartz)
将热帖列表和所有帖子的总数存入本地缓存 Caffeine(利用分布式定时任务 Spring Quartz 每隔一段时间就刷新计算帖子的热度/分数 — 见下文,而 Caffeine 里的数据更新不用我们操心,它天生就会自动的更新它拥有的数据,给它一个初始化方法就完事儿)
修改头像(异步请求)
将用户选择的头像图片文件上传至七牛云服务器
修改密码
此处只画出修改头像:
评论部分前端的名称显示有些缺陷,有兴趣的小伙伴欢迎提 PR 解决~
关于评论模块需要注意的就是评论表的设计,把握其中字段的含义,才能透彻了解这个功能的逻辑。
评论 Comment 的目标类型(帖子,评论) entityType 和 entityId 以及对哪个用户进行评论/回复 targetId 是由前端传递给 DiscussPostController 的
一个帖子的详情页需要封装的信息大概如下:
将点赞相关信息存入 Redis 的数据结构 set 中。其中,key 命名为 like:entity:entityType:entityId
,value 即点赞用户的 id。比如 key = like:entity:2:246
value = 11
表示用户 11 对实体类型 2 即评论进行了点赞,该评论的 id 是 246
某个用户的获赞数量对应的存储在 Redis 中的 key 是 like:user:userId
,value 就是这个用户的获赞数量
若 A 关注了 B,则 A 是 B 的粉丝 Follower,B 是 A 的目标 Followee
关注的目标可以是用户、帖子、题目等,在实现时将这些目标抽象为实体(目前只做了关注用户)
将某个用户关注的实体相关信息存储在 Redis 的数据结构 zset 中:key 是 followee:userId:entityType
,对应的 value 是 zset(entityId, now)
,以关注的时间进行排序。比如说 followee:111:3
对应的value (20, 2020-02-03-xxxx)
,表明用户 111 关注了实体类型为 3 即人(用户),该帖子的 id 是 20,关注该帖子的时间是 2020-02-03-xxxx
同样的,将某个实体拥有的粉丝相关信息也存储在 Redis 的数据结构 zset 中:key 是 follower:entityType:entityId
,对应的 value 是 zset(userId, now)
,以关注的时间进行排序
类似的,置顶、加精也会触发发帖事件,就不再图里面画出来了。
每次发生点赞(给帖子点赞)、评论(给帖子评论)、加精的时候,就将这些帖子信息存入缓存 Redis 中,然后通过分布式的定时任务 Spring Quartz,每隔一段时间就从缓存中取出这些帖子进行计算分数。
帖子分数/热度计算公式:分数(热度) = 权重 + 发帖距离天数
// 计算权重 double w = (wonderful ? 75 : 0) + commentCount * 10 + likeCount * 2; // 分数 = 权重 + 发帖距离天数 double score = Math.log10(Math.max(w, 1)) + (post.getCreateTime().getTime() - epoch.getTime()) / (1000 * 3600 * 24);
想要自己从零开始实现这个项目或者深入理解的小伙伴,可以扫描下方二维码关注公众号『飞天小牛肉』,第一时间获取配套教程, 不仅会详细解释本项目涉及的各大技术点,还会汇总相关的常见面试题,目前尚在更新中。
并推荐我的开源教程类项目 『 CS-Wiki 』,Gitee 推荐项目,目前已 0.9k star: 致力打造完善的 Java 后端知识体系,不仅仅帮助各位小伙伴快速且系统的准备面试(秋招/社招),更指引学习的方向
有什么问题也可以添加我的微信,记得备注来意:格式 (学校或公司 - 姓名或昵称 - 来意)
博主水平有限,本项目参考牛客网 — Java 高级工程师课程,感谢老师和平台。
6.2. 社区资源 寻求实时帮助,可以在Freenode IRC服务器的#go-nuts频道里找到其他的用户或是开发人员。 Go语言的官方邮件列表是Go Nuts。 报告Bug可以使用Go问题追踪器。 对于想及时了解开发进度的读者,可以加入另一个邮件列表golang-chenkins,这样在有人往Go代码库中checkin新代码时就会收到一封简要的邮件。
3.9. 社区资源 在 Freenode IRC 上,可能有很多#go-nuts的开发人员和用户,你可以获取即时的帮助。 还可以访问Go语言的官方邮件列表 Go Nuts。 Bug可以在 Go issue tracker 提交。 对于开发Go语言用户,有令一个专门的邮件列表 golang-checkins。 这里讨论的是Go语言仓库代码的变更。 如果是中文用户,请访问:Go语言中文论坛。
Play Community 是一个基于 Play Framework 开发的社区系统,宗旨是提供一套知识学习的闭环系统。系统主要分为学习,问答,交流和搜索四个板块。 学习板块 社区的维护者可以在学习板块维护一系列电子书,供初学者系统化地学习基础知识。 问答板块 初学者在学习过程中遇到问题可以到问答板块提问,社区维护者或成员及时解答。 交流板块 中高级成员可以交流板块分享一些学习或实战经验。 搜索
C.2.1 国际社区 下面是在线参考的一些国际网址: http://partners.trolltech.com/ http://lists.trolltech.com/qt-interest/ http://doc.trolltech.com/qq/ 可以从 http://doc.trolltech.com 中获取 Qt 的当前版和一些早期版本的在线参考文 档。这个网站也选摘了 Qt 季刊(Qt
社区能源共享 功能描述 本 合约 以纽约实验性的能源微电网为例,作为一个简单的案例进行实现。 “在总统大道的一边,五户家庭通过太阳能板发电;在街道的另一边的五户家庭可以购买对面家庭不需要的电力。而连接这项交易的就是区块链网络,几乎不需要人员参与就可以管理记录交易。”但是这个想法是非常有潜力的,能够代表未来社区管理能源系统。” 布鲁克林微电网开发商 LO3 创始人 Lawrence Orsini 说
联系我们 Nacos Gitter-https://gitter.im/alibaba/nacos Nacos 微博-https://weibo.com/u/6574374908 Nacos segmentfault-https://segmentfault.com/t/nacos 邮件列表 邮件列表建议讨论任何与Nacos有关的事情。具体请看参考手册描述如何订阅我们的邮件列表。 dev-naco
关于 MOSN 社区。 MOSN 是一个开源项目,于 2018 年 7 月由蚂蚁集团开源,使用 Apache 2.0 协议,任何人都可以使用和参与改进。MOSN 社区期待您的加入! 关于 MOSN 社区的详细资料请访问 Community 仓库。 工作组 目前 MOSN 包含以下工作组: Istio 工作组 Dubbo 工作组 选择加入您感兴趣的工作组,开始您的 MOSN 之旅吧! 社区会议 MO
PHP 社区多元化并且规模庞大,成员们也乐意并随时准备好帮助新人。你可以考虑加入当地的 PHP 使用者社区 (PUG) 或者参加教大型的 PHP 会议,从中学习更多最佳实践。你也可以使用 IRC 逛逛 irc.freenode.com 上的 #phpc 频道,也可以关注 @phpc 的Twitter 账号。试着去多结交一些新的开发者,学习新的东西,总之,交一些新朋友!其他的社区资源包含 Googl