仿牛客项目

  • 技术架构与功能模块
  • -----------------------------------------------------
  • 项目调试技巧(Logger)
  • 发送邮件(Email)
  • Cookie 和 Session(会话管理)
  • Kaptcha生成验证码
  • 拦截器(HandlerInterceptor)
  • ThreadLocal使多用户登录不冲突
  • 过滤敏感词
  • AJAX异步请求(页面不刷新)
  • 防止脚本注入
  • 事务管理(ACID)
  • 统一异常处理
  • 统一记录日志(AOP)
  • 集成Redis
  • Redis对登录模块的优化
  • Kakfa
  • SpringBoot 集成 Kafka
  • ElasticSearch + 与SpringBoot整合
  • 页面生成CSRF
  • 置顶、加精、删除
  • Redis高级数据类型
  • -----------------------------------------------------
  • 功能架构
  • ⭐记录一些错误
  • 记录一些改错方法
  • 后记

技术架构与功能模块

仿牛客项目地址https://gitee.com/Sher-Locked/niuke

技术架构

  • SpringBoot
  • Spring、SpringMVC、MyBatis、Thymeleaf
  • MySQL、Hikari
  • Redis、Kafka、ElasticSearch
  • SpringSecurity、SpringActuator

功能模块

用户:

  • 注册登录
  • 个人主页
  • 更换头像
  • 修改密码

论坛:

  • 发布帖子
  • 评论回复
  • 敏感词过滤
  • 点赞功能
  • 搜索功能
  • 置顶、加精、删除

交友:

  • 私信聊天
  • 关注、粉丝
  • 查看他人主页

-----------------------------------------------------

项目调试技巧(Logger)

断点调试和日志功能。
日志:

//properties
logging.level.com.example.niuke = debug
logging.file.name=d:/log/niuke/xxx.log //存放日志路径//java
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
logger.debug("debug");
logger.info("info");
logger.warn("warn");
logger.error("error");

发送邮件(Email)

  1. 启用邮箱SMTP服务(略)
  2. 加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId>
</dependency>
  1. 配置properties
spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.username=xxxx@qq.com
spring.mail.password=xx.xx.xx
spring.mail.protocol=smtp
spring.mail.properties.mail.smtp.ssl.enable = true
  1. email工具类
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message);
messageHelper.setFrom(from);
messageHelper.setTo(to);
messageHelper.setSubject(subject);
messageHelper.setText(content, true);
mailSender.send(messageHelper.getMimeMessage());
  1. 结合Thymeleaf发送html
Context content = new Context();//Thymeleaf的
content.setVariable("username", "愿你被世界温柔以待!");
String process = templateEngine.process("/email/demo", content);
mailClient.sendMail(to, subject, process);

Cookie 和 Session(会话管理)

Cookie
可以通过@CookieValue("code") String code获取对应的cookie值

//创建cookie
public void setCookie(HttpServletResponse res) {Cookie cookie = new Cookie("code", CommunityUtil.generateUUID());cookie.setPath("/test");//cookie作用范围cookie.setMaxAge(60*10);//cookie存活时间res.addCookie(cookie);//添加cookiereturn "set Cookie";
}

Session
通过session.getAttribute(" ");取值

public String test(HttpSession session){session.setAttribute("id", 1);session.setAttribute("naem","dongfang");return "set session";
}

分布式不用Session的原因:
多服务器时可能在服务端有不同的session,产生session共享问题
解决方案:Session一致性(我的另一篇博文)

Kaptcha生成验证码

  1. 导包
<dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version>
</dependency>
  1. 配置
@Bean
public Producer kaptchaProducer() {Properties properties = new Properties();properties.setProperty("kaptcha.image.width", "100");properties.setProperty("kaptcha.iamge.height", "40");properties.setProperty("kaptcha.textproducer.font.size", "32");properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQ");properties.setProperty("kaptcha.textproducer.char.length", "4");properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");DefaultKaptcha kaptcha = new DefaultKaptcha();Config config = new Config(properties);kaptcha.setConfig(config);return kaptcha;
}
  1. 使用
String text = kaptchaProducer.createText();
BufferedImage image = kaptchaProducer.createImage(text);//验证码存入session
session.setAttribute("kaptcha",text);
//给浏览器声明返回的类型
response.setContentType("image/png");
try {OutputStream os = response.getOutputStream();ImageIO.write(image, "png", os);
} catch (IOException e) {e.printStackTrace();
}

拦截器(HandlerInterceptor)

拦截器的应用:

  • 请求时开始查询登录用户
  • 本次请求中持有用户数据
  • 模板视图上显示用户数据
  • 请求结束时清理用户数据
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginTicketInterceptor).excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");}
}

ThreadLocal使多用户登录不冲突

@Component
public class HostHolder {private ThreadLocal<User> users = new ThreadLocal<>();public void setUser(User user) {users.set(user);}public User getUser() {return users.get();}public void clear() {users.remove();}
}

过滤敏感词

前缀树:

  • 名称:Trie、字典树、查找树
  • 特点:查找效率高,消耗内存大
  • 应用:字符串检索、词频统计、字符串排序

敏感词过滤器:

  • 定义前缀树
  • 根据敏感词,初始化前缀树
  • 编写过滤敏感词的方法

AJAX异步请求(页面不刷新)

οnclick="send();"function send() {$.post("/xxx/xxx",{"name":"xxx","age":"xxx"},function(data) {console.log(data);})
}

防止脚本注入

discussPost.setTitle(HtmlUtils.htmlEscape(discussPost.getTitle()));//防止注入
discussPost.setContent(HtmlUtils.htmlEscape(discussPost.getContent()));//防止注入

事务管理(ACID)

ACID

  • 悲观锁:1.共享锁 2.排他锁
  • 乐观锁

Spring事务管理

  • 声明式事务
    @Transaction(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)

  • 编程式事务

@Autowired
private TransactionTemplate transactionTemplate;
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITED);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);return transactionTemplate.execute(new TransactionCallback<Object>(){ 重写方法 });

统一异常处理

@ControllerAdvice 修饰类
@ExceptionHandler 修饰方法
@ModelAttribute 修饰方法
@DataBinder 修饰方法

统一记录日志(AOP)

AOP的实现:

  • AspectJ(编译时增强)(字节码)
  • SpringAOP(运行时增强)(代理)

SpringAOP:

  • JDK动态代理
  • CGLib动态代理

集成Redis

Redis编程式事务

@Test
public void testTransactional() {Object obj = redisTemplate.execute(new SessionCallback() {@Overridepublic Object execute(RedisOperations redisOperations) throws DataAccessException {String redisKey = "test";//启用事务redisOperations.multi();redisOperations.opsForSet().add(redisKey, "dongfang");redisOperations.opsForSet().add(redisKey, "wangquan");redisOperations.opsForSet().add(redisKey, "tushan");return redisOperations.exec();}});
}

Redis对登录模块的优化

  • 使用Redis存储验证码
    验证码需要频繁的访问与刷新,对性能要求较高
    验证码不需永久保存,通常在很短的时间后就会失效
    分布式部署时,存在Session共享的问题
  • 使用Redis存储登录凭证
    处理每次请求时,都要查询用户的登录凭证,访问的频率非常高
  • 使用Redis缓存用户信息
    处理每次请求时,都要根据凭证查询用户信息,访问的频率非常高

Kakfa

Kafka 是一个分布式流媒体平台。

应用:消息系统、日志收集、用户行为追踪、流式处理。

特点:高吞吐量、消息持久化、高可靠性、高扩展性。

zookeeper-server-start.bat config\zookeeper.properties 开启zookeeper
kafka-server-start.bat config\server.properties 开启kafka
kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test 新建类别
kafka-console-producer.bat --broker-list localhost:9092 --topic test 开启生产者
kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test --from-beginning 开启消费者

阻塞队列

  • BlockingQueue
    解决线程通信的问题
    阻塞方法:put、take

  • 生产者消费者模式
    生成者:产生数据的线程
    消费者:使用数据的线程

  • 实现类
    ArrayBlockingQueue
    LinkedBlockingQueue
    PriorityBlockingQueue

SpringBoot 集成 Kafka

  1. 导包
<dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId>
</dependency>
  1. zookeeper-server-start.bat config\zookeeper.properties 开启zookeeper
    kafka-server-start.bat config\server.properties 开启kafka
  2. 配置Properties
#kafka
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=niuke-consumer-group
spring.kafka.consumer.enable-auto-commit=true
#ms
spring.kafka.consumer.auto-commit-interval=3000
  1. springboot调用
@SpringBootTest
public class KafkaTest {@Resourceprivate KafkaProducer kafkaProducer;@Testpublic void testKafka() {kafkaProducer.sendMessage("test", "你好");kafkaProducer.sendMessage("test", "在吗");try {Thread.sleep(1000 * 10);} catch (InterruptedException e) {e.printStackTrace();}}
}
@Component
class KafkaProducer{@Resourceprivate KafkaTemplate kafkaTemplate;public void sendMessage(String topic, String content) {kafkaTemplate.send(topic, content);}
}
@Component
class KafkaConsumer {@KafkaListener(topics = {"test"})public void handleMessage(ConsumerRecord record) {System.out.println(record.value());}
}

ElasticSearch + 与SpringBoot整合

分布式RestFul 风格的搜索引擎,各种类型的数据的检索,实时搜索服务,PB级数据

术语:
索引(数据库)、类型(表)、文档(行)(Json)、字段(列)。
集群、节点、分片(shards)、副本(replicas)(备份)。

配置:

  1. 修改elasticsearch.yml
  2. 配置bin环境变量
  3. 下载ik分词器(版本对应),解压在/plugins/ik

curl -X GET "localhost:9200/_cat/health?v" 查看健康状况
curl -X GET "localhost:9200/_cat/nodes?v" 查看连接状况
curl -X GET "localhost:9200/_cat/indices?v" 查看索引
curl -X PUT "localhost:9200/test" 新建索引
curl -X DELETE "localhost:9200/test" 删除索引

  1. 导包
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
  1. 配置properties
# ElasticSearch
spring.elasticsearch.rest.username=niuke
# 9200http 9300tcp
spring.elasticsearch.rest.uris=http://localhost:9200
  1. 配置entity
@Document(indexName = "discusspost", indexStoreType = "_doc", shards = 6, replicas = 3)
public class DiscussPost {@Idprivate int id;@Field(type = FieldType.Integer)private int userId;@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String title;@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String content;@Field(type = FieldType.Integer)private int type;//0-普通,1-置顶@Field(type = FieldType.Integer)private int status;//0-正常,1-精华,2-拉黑@Field(type = FieldType.Date)private Date createTime;@Field(type = FieldType.Integer)private int commentCount;@Field(type = FieldType.Double)private double score;
  1. 配置dao
@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {}
  1. 使用
NativeSearchQuery build = new NativeSearchQueryBuilder().withQuery(QueryBuilders.multiMatchQuery(keyword, "title", "content")).withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC)).withPageable(PageRequest.of(0, 10)).withHighlightFields(new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")).build();

页面生成CSRF

<meta name="_csrf" th:content="${_csrf.token}">
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (e, xhr, options){xhr.setRequestHeader(header, token)
});

置顶、加精、删除

  1. 导包
<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
  1. 使用
<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security">对字段 sec:authorize="hasAnyAuthority('moderator')

一些JS代码

$("#wonderfulButton").attr("disabled","disabled"); 设置其不可用
location.href = "/index";  跳转页面

Redis高级数据类型

HyperLogLog(合并和去重)

  • 采用一种基数算法,用于完成独立总数的统计
  • 占据空间小,无论多少数据,只占12K内存
  • 不精确,标准误差:0.81%

Bitmap(对位的运算)

  • 不是一种独立的数据结构,实际上就是字符串
  • 支持按位存取数据,可以将其看成byte数组
  • 适合存储索引大量的连续的数据的布尔值

-----------------------------------------------------

功能架构

用户:

  • 注册:MD5加密密码,邮箱激活账号
  • 登录:Session + Kaptcha + Cookie +ThreadLocal(重构后:Cookie + Redis + Cookie | ThreadLocal + Redis
  • 个人主页
  • 更换头像:图像URL与本地路径关联 JavaIO流 (重构后:云存储
  • 登录状态(防止未登录进入setting等页面)(使用:自定义注解+拦截器
  • 修改密码

论坛:

  • 发布帖子:AJAX + FastJSON实现异步
  • 评论回复
  • 敏感词过滤:Trie树
  • 点赞功能:Redis实现(set)

交友:

  • 私信聊天:MySQL存储
  • 关注、粉丝:Redis实现(zset)
  • 查看他人主页

⭐记录一些错误

  1. 当把一些用户删除的时候,他们的推文还在,导致展示推文的时候会查不到这个用户以及推文。
    报错信息:
    There was an unexpected error (type=Internal Server Error, status=500). An error happened during template parsing (template: "class path resource [templates//index.html]") org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates//index.html]")

  2. 同样:我将149号用户删除了呜呜呜呜,找不到用户的名字,一直报错

记录一些改错方法

  1. 查看报错信息,定位报错位置
  2. 根据报错信息,找出报错原因
  3. 根据报错原因, 纠正相关代码
  4. 找不到原因时:
    4.1 断点调试
    4.2 logger打印日志信息
    技巧:输出错误相关关键语句,细心查看日志

后记

@Autowired和@Resource的区别

Hikari和Druid的区别

Spring
Impl实现同一个接口时,实现IOC时对于容器难以判断 1.加@Primary注解2.@Repository时加name,注入时加@Qualifier

HTTP:request.getMethod .getServletPath .getHeadernames 对应发送HTTP的头文本
HTTP:response.setContentType(“text/html;charset=utf-8”) 对应返回HTTP的头文本

@RequestParam的一些参数 name=" " 对应前端name,required=" " ,defaultValue=" " 默认值

仿牛客项目(持续更新)相关推荐

  1. 仿牛客社区项目(第一章)

    文章目录 第一章:初始 SpringBoot,开发社区首页 仿牛客社区项目开发首页功能 一. 实体引入 1. User类 2. DiscussPost 类 3. Page类 二. 配置文件 三. da ...

  2. 仿牛客论坛项目(下)

    代码仓库:https://gitee.com/qiuyusy/community 仿牛客论坛项目(上) 仿牛客论坛项目 15.kafka 1.阻塞队列 2.Kafka入门 简介 术语解释 下载 配置 ...

  3. 仿牛客社区项目笔记-帖子模块(核心)

    仿牛客社区项目笔记-帖子模块(核心) 1. 帖子模块 1.1 过滤敏感词 1.2 发布帖子 1.3 帖子详情 1.4 显示评论 1.5 添加评论 1.6 私信列表 1.7 发送私信 1. 帖子模块 分 ...

  4. 从零开始—仿牛客网讨论社区项目(一)

    主要技术架构: SpringBoot Spring SpringMVC MyBatis Redis Kakfa Elasticsearch Spring Security Spring Actator ...

  5. 从零开始—仿牛客网讨论社区项目(六)

    主要技术架构: SpringBoot Spring SpringMVC MyBatis Redis Kakfa Elasticsearch Spring Security Spring Actator ...

  6. 仿牛客论坛项目(上)

    代码仓库:https://gitee.com/qiuyusy/community 仿牛客论坛项目(下) 仿牛客论坛项目上 1. Spring 在测试类中使用Spring环境 @Primary的作用 @ ...

  7. 仿牛客论坛项目(5)

    仿牛客论坛项目 一.SpringSecurity入门案例 1.1 添加依赖 1.2 配置文件 1.3 工具类 CommunityUtil 1.4 配置类 SecurityConfig 1.5 实体类 ...

  8. 仿牛客论坛项目(4)

    仿牛客论坛项目 一.Elasticsearch入门 1.1 elasticsearch安装 1.2 修改config目录下的elasticsearch.yml配置文件 1.3 配置环境变量 1.4 下 ...

  9. 仿牛客网社区项目 全栈总结

    学习仿牛客网社区项目 代码&资源 各章节总结 第一章 第二章 第三章 第四章 第五章 第六章 第七章 第八章 争取让每个知识点都有链接可点 项目总结 网站架构图 常见面试题 MySQL Red ...

最新文章

  1. python3编译exe_编译 – 如何将我的Python 3应用程序编译到.exe?
  2. [Ubuntu 12.10] Openstack 多节点安装--前期准备网络拓扑
  3. hub设备_USB不够用,一个HUB全部搞定!ORICO 群控USB扩展器
  4. P4170-[CQOI2007]涂色【区间dp】
  5. intellij 快捷键_IntelliJ中的键盘快捷键
  6. ubuntu 14.04 LTS(64bit) Anacoda2环境下安装gensim
  7. dnn学习:数据访问(1)
  8. SpringBoot自定义注解接收json参数
  9. WindowsServers2019上手体验
  10. php mysql异常捕获_PHP中try{}catch{}的用法及异常处理.对数据库的事物支持
  11. 萦绕在头脑中的思路_我的编程梦们 【更新至2010.06.03】
  12. [java]房屋出租系统
  13. Unity 5.x游戏开发指南笔记(一)
  14. EPLAN液压气动流体元件符号库导入
  15. 闰月算法c语言,公历转农历算法-C语言
  16. 注册微信公众号需要哪些材料?
  17. python编程控制机器人_python程序控制NAO机器人行走
  18. 《数学建模简明教程--基于python》学习笔记-第四章-微分方程-课后习题解答
  19. visio直线交叉相交跨线修改
  20. 《途客圈创业记:不疯魔,不成活》一一2.4 与iWeekend再续前缘

热门文章

  1. 【web】HTTP(s)协议详解(重点:HTTPS 的加密过程浏览器中输入网址后,发生了什么?)
  2. (三)Latex的字体字号设置
  3. python 设置excel单元格式,Python xlwt设置excel单元格字体及格式
  4. RLS,LMS以及NLMS三种自适应均衡matlab仿真
  5. Linux运维工程师学习大纲
  6. OpenMeetings安装
  7. 数字图像 - 图像隐写
  8. spring事务管理总结
  9. zookeeper节点类型,整合代码实现服务器动态监听
  10. 高中数学40分怎么办_新高一第一次考试数学只考了40分,还有救吗?