• 仿牛客网项目第三章:开发社区核心功能(详细步骤和思路)


    1. 过滤敏感词

    1)可以使用JDK自带的replace方法替换敏感词,但在实际应用中敏感词比较多、字符串可能比较长(发布的一篇文章)这种情况下用replace去替换性能就比较差
    2)使用前缀树来实现过滤敏感词的算法。但前缀树方法也有一些局限性,不能实现对停顿词重复词的检查,考虑到此可以优化为DFA算法过滤敏感词

    1.1 目的

    在发帖子的过程中,可能会有人发布一些敏感词信息,在提交到系统中需要先进行一次过滤,故需要开发过滤敏感词功能

    1.2 实现方法

    通过前缀树,然后书写过滤敏感词方法,能够实现敏感词的过滤功能。

    1.3 前缀树

    1. 名称:Trie,字典树,查找树
    2. 特点:查找效率高,消耗内存大(以空间换时间)
    3. 应用:字符串检索,词频统计和字符串排序

    1.4 敏感词过滤步骤(为发帖子做准备)

    1. 定义前缀树
      1)敏感词文件:在resource中添加文件sensitive-words.txt
      2)添加工具类sensitivefilter:(核心搞懂前缀树和过滤功能实现说明
      (1):新建前缀树:TrieNode(),私有
      (2):初始化前缀树:init() ,私有。用@PostConstruct标注,说明只是在类加载的时候运行一次。
      (3):将一个敏感词添加到前缀树中,addKeyword(String keyword),私有
      (4):根据前缀树,对输入的字符串进行过滤,filter(String text),公有,需要考虑符号穿插。
      (5):判断输入字符串是否为符号(符号课采取跳过原则,例如“★开★票★★”,还是能够被检测出来)
    2. 进行测试敏感词过滤效果
      1)新建一个敏感词的测试类SensitiveTests,然后进行测试文本过滤效果
      2)调用公有的铭感次过滤函数sensitiveFilter.filter(text),实现功能

    2. 发布帖子

    2.1 AJAX介绍

    1. Astnchronous JavaScript and XML
    2. 异步的JavaScipt与XML,不是一门新技术,只是一个新的术语
    3. 使用AJAX,网页能够将增量更新呈现在页面上面,而不需要刷新整个页面
    4. 虽然X代表XML,但是现在使用JSON比XML更加广泛。

    2.2 AJAX使用实例

    1. 导包:导入包fastjson(用于)
    2. Util层:在CommunityUtil类中实现获取JSON字符串的三个构造方法:getJSONString()
      1) 直接在CommunityUtil类中测试获取json字符串的方法,
    3. Dao层(把接收到的信息传入数据库):
      1)在DiscussPostMapper中增加插入数据的方法insertDiscussPost。
      2)在mapper文件中增加insert方法的实现。
    4. Service层:在DiscussPostService中增加调用的实现findDiscussPosts。
    5. Controller层:增加DiscussPostController类,添加add访问请求,并修改index.js。
      1)在add方法中,返回json信息而不是文本,故需要添加注解@ResponseBody
      2)add的作用是添加帖子的功能,返回的是json信息,被前端读取后,能够在前端自己完成刷新功能,而不是整体刷新。
      3)修改index.js使得前端能够完成一定的功能。(减轻服务器的压力,负载均衡)
      4)修改index.html中弹出框提示框的内容

    3. 帖子详情

    3.1 实现功能

    通过点击帖子栏中帖子的标题,能够获取用户帖子的详情

    3.2 实现过程

    1. Dao层:在DiscussPostMapper中添加根据帖子id查询到帖子对象功能的sql方法申明,并在mapper.xml书写sql语句。
    2. Service层:在DiscussPostService中添加根据帖子id查询到帖子对象功能方法(直接调用)
    3. Controller层:根据用户id获取帖子对象用户对象,传入model中方便在页面进行调用,然后页面跳转至详情页。
    4. index.html:修改点击帖子标题,跳转到详情页面的连接
    5. discuss-detail.html
      1)处理静态资源的访问路径
      2)复用index.html的heade区域
      3)根据得到帖子的对象,在页面显示标题,作者,发布时间和帖子正文内容

    4. 事务管理

    4.1 事务介绍

    事务是由N步数据库操作序列组成的逻辑执行单元,这系列操作要么全执行,要么全放弃执行

    4.2 事务四大特性

    1. 原子性(Atomicity):事务是应用中不可再分的最小执行体。
    2. 一致性(Consistency):事务执行的结果,须使数据从一个一致性状态,变为另一个一致性状态。
    3. 隔离性(Isolation):各个事务的执行互不干扰,任何事务的内部操作对其他的事务都是隔离的。
    4. 持久性(Durability):事务一旦提交,对数据所做的任何改变都要记录到永久存储器

    4.3 事务的隔离性

    1. 并发异常
      读取未提交都不能解决
      1)第一类丢失更新:一个查询回滚,一个查询更新,前者慢一点,会导致后者的更新丢失
      2)脏读:一个事务读取另一个事务未提交的数据(读取已提交能够解决
      3)第二类丢失更新:两个都进行查询更新,会使得前者更新值丢失
      4)不可重复度:一个事务进行数据更新,另一个事务进行多次访问出现不一致。(可重复读能够解决
      5)幻读:对数据的添加和删除,使得另一个事务在多次查询的时候感觉出现了幻觉(类似不可重复读,不过前者是数据更新,后者是数据修改或者删除)(序列化能够解决

    4.4 事务的实现机制

    1. 悲观锁(数据库自带)
      1)共享锁(S锁):事务A对某数据加上共享锁后,其他事务只能对数据加共享锁,不能加排他锁
      2)排它锁(X锁):事务A对某数据加上排他锁后,其他事务既不能给数据加上排他锁,也不能加上共享锁。
    2. 乐观锁(自定义)
      1)版本号,时间戳等
      2)版本号工作原理:在更新数据之前,检查版本号是否发生变化,若变化则取消本次更新,否则就更新内容,且版本号+1。

    4.4 事务的管理(Spring Boot)

    1. 申明式事务(XML+注解+方法)(更常用,编写简单
      1)通过XML配置,申明某方法的事务特征
      2)通过注解,申明某方法的事务特征
    2. 编程式事务(TransactionTemplate+方法内部)
      1)通过TransactionTemplate管理事务
      2)并通过她执行数据库的操作
    3. 两者区别
      1)更简单:申明式事务只能申明方法的整体,不能对局部代码进行事务管理;但是事务管理非常方便,只需要添加注解
      2)更精细:编程式事务形成一个TransactionTemplate对象,把需要管理的代码可以局部圈起来。
    4. 管理事务的两个参数
      1)隔离级别:在上述已经说明了(一般用可重复度)
      2)关联方式:REQUIRED;REQUIRES_NEW;NESTED
      (1)REQUIRED:支持当前事务(外部事务),如果不存在则创建新事务.
      (2)REQUIRES_NEW创建一个新事务,并且暂停当前事务(外部事务).
      (3)NESTED:如果当前存在事务(外部事务),则嵌套在该事务中执行(独立的提交和回滚),否则就会REQUIRED一样.
    5. 两种管理事务方式例子
      1)在AlphaService里面书写两种事务管理方式事例。
      2)申明式事务:在方法上加上注解:@Transactional,里面有两个参数
      3)新增用户,新增帖子,然后加上一个会报错的语句
      4)如果有事务管理,会因为存在报错,而进行回滚,其他语句也不会执行。
      5)新建一个测试类DiscusspostTests,执行两种方法,查看数据库的改变。数据库就没变。

    5. 显示评论

    1. 评论表对象:存在非常多种类型的评论,用参数entity_type进行区分。
    2. 实体类:定义评论entity:一实体一表,添加评论表。
    3. Dao层:创建CommentMapper
      1)根据评论类型和帖子id,查询一页评论的数据(需进行翻页展示):
      (1):根据四个参数,从帖子数据库中获取所需帖子:
      selectCommentsByEntity(int entityType, int entityId, int offset, int limit)
      2)根据实体查询评论的数量:
      (1):根据评论类型和帖子id,获取评论的数量:
      selectCountByEntity(int entityType, int entityId)
      3)新建comment-mapper.xml,对Dao层进行实现
    4. 业务层Service:创建CommentService
      1)处理查询评论的业务:直接调用Dao方法
      2)处理查询评论数量的业务,直接调用Dao方法
    5. 实现层Controller:这一层与帖子详情功能共享,在同一个页面
      1)根获取不同类型评论对象:评论对象回复对象(评论的评论,回复对象还有两种),传值给model。(思路简单,步骤繁琐了点)
    6. .xtml
    1. 有两类评论,评论和回复两种类型,需要分别进行展示。通过循环操作。
    2. 在帖子详情里面,复用首页分页的功能

    6. 添加评论(事务管理)

    1. Dao层:
      (1)增加评论数据:在comment里书写:insertComment(Comment comment);
      (2)修改帖子的评论数量:在discussPost里书写:updateCommentCount
      (3)对应书写mapper.xml的文件
    2. Service层:
      1)处理添加评论的功能:
      2)先增加评论,再更新帖子的评论数量(事务操作,两者连在一起):在DiscussPostService先书写更新帖子数量,然后再CommentService里面书写帖子个数。
    3. Controller层:
      1)处理添加评论数据的请求:书写方法:addComment
    4. html层:
    1. 设置添加评论的表单(有两个地方需要放入表单)

    7. 私信列表+私信详情

    7.0 功能说明

    1. 私信列表:通过私信列表,能够查询到最近有谁给你发了消息,显示最新的一条消息(类似首页帖子)
    2. 私信详情:点击消息,能够直到消息的详情,把所有交流的内容都放出来。
    3. 两者都支持分页展示功能

    7.1 私信列表+私信详情

    1. 查询当前用户的会话列表和私信详情列表
      1)Entity实体类:创建Message实体类
        private int id;                    //自动生成的id
        private int fromId;                //发送者id
        private int toId;                  //接收者id
        private String conversationId;     //会话id(第几次会话了)
        private String content;           //会话内容
        private int status;                //状态(0未读,1已读,2删除)
        private Date createTime;           //交流时间
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2)Dao层:创建MessageMapper
    书写五个查询方法
    (1)私信列表:查询当前用户的会话列表,针对每个对话只返回最新一条的私信(sql注意)
    (2)私信列表:查询当前用户的会话数量
    (3)私信详情:查询某个会话所包含的私信列表
    (4)私信详情:查询某个会话所包含的私信数量
    (5)私信列表:查询未读私信的数量
    3)Dao层:创建对应的。xml文件书写sql语句
    4)Service层:创建MessageService:直接调用对应Dao方法(比较简单,核心逻辑放到表现层);在UserService中添加方法:根据用户的名字查找用户对象。
    5)Controller层:创建MessageController
    (1)私信列表请求:分页信息,私信列表信息,私信数量,发送私信对象,未读私信数量,把以上几个数据都查找出来,然后通过写入Model中,传递给前端页面。
    (2)私信详情请求:分页信息,私信详情列表,私信详情发送对象,设置已读(把在私信详情列表中出现过的数据)
    (3)私信发送请求:发送私信,从前端得到的消息,使用异步方式,故需要注解 @ResponseBody。功能交给前端处理。
    (4):小方法一:根据消息的conversationId,结合当前用户判断收信人是谁
    (5):小方法二:根据消息,返回消息发送人的id。(用于设置消息的未读转为已读)
    6)修改html
    (1)修改index.html文件:点击消息,能够跳转页面。
    (2)修改letter.html文件:因为是异步的方式,所以还需要修改letter.js文件,设置响应方式。
    (3)修改letter_detail.html文件

    8. 发送私信

    8.1 发送私信+设置已读

    1. 采用异步的方式发送私信
      1)Dao层:添加MessageMapper
      (1)添加方法:新增消息
      (2)添加方法:修改消息状态。出现在显示名单里的标记为已读状态。
      2)**Dao层:**添加Message.xml书写对应sql文件
      3)**Service层:**直接调用Dao的查询方法
      4)Controller层:添加add请求,采用异步通信方法,返回json格式,在前端的message.js中进行逻辑控制。
      5)修改对应的.html文件。

    9. 统一处理异常

    9.1 处理异常的四种方式

    1. @ControllerAdvice**(最常用)**
    • 用于修饰类,表示该类是Controller的全局配置类。
    • 在此类中,可以对Controller进行如下三种全局配置:异常处理方案、绑定数据方案、绑定参数方案。
    1. @ExceptionHandler
    • 用于修饰方法,该方法会在Controller出现异常后被调用,用于处理捕获到的异常。
    1. @ModelAttribute
    • 用于修饰方法,该方法会在Controller方法执行前被调用,用于为Model对象绑定参数。
    1. @DataBinder
    • 用于修饰方法,该方法会在Controller方法执行前被调用,用于绑定参数的转换器。

    9.2 统一处理异常步骤:

    1. 在template目录下新建一级文件
      (1)把最容易出错的两个.xml文件存入:404(请求资源不存在)和500(服务端发生错误)
    2. 在Controller中新建文件夹advice,然后新建ExceptionAdvice
      (1)记录发生错误处的日志信息,并且逐行写下原因是什么
      (2)根据发生错误处请求的方式,分为同步请求和异步请求,分别返回错误页面和错误消息json。
    3. 一旦系统中发送了错误,那么就会自动运行这个方法,进行统一异常的处理机制,非常方便,这就是框架的强大之处。

    10. 统一记录日志

    10.1 需求

    比如一些请求执行的过程中,需要对其进行日志记录,但是一个个在求情中添加不是很方便,故使用功能切面编程技术去实现。

    1. 在Controller运行的时候,可以通过拦截器进行日志记录
    2. 在发生异常的时候,可以通过统一异常管理进行处理
    3. 在业务运行的时候,Service,需要通过AOP进行统一的管理。

    10.2 AOP概念和术语

    详情见我以前写的文章记录。

    10.3 AOP的实现

    1. AspectJ
    (1)语言级的实现,拓展了java语言,定义了AOP语法(一种新的语言)
    (2)在编译器植入代码,有一个专门的编译器,用来生成遵守Java字节码规范的class文件。
    2. Spring AOP
    (1)纯java实现,不需要专门的编译过程,不需要特殊的类装载器
    (2)在运行时通过代理的方式植入代码,只支持方法类型的连接点
    (3)支持对 AspectJ的集成
    3. 两者区别:
    (1):功能:AspectJ功能更强大,但是需要额外语言和编译期;Spring AOP使用更方便,但是有一定局限性,比如只支持方法类型的连接点。
    (2):植入代码的时期:AspectJ为编译期,Spring AOP为运行期。

    10.4 动态代理的种类(Spring AOP)

    1. JDK动态代理
      (1)Java提供的动态代理技术,可以在运行时创建接口的代理实例
      (2)Spring AOP 默认采用此种方式,在接口的代理实例中置入代码。
    2. CGLib动态代理
      (1)采用底层的字节码技术,在运行时创建子类代理实例
      (2)当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中植入代码

    10.5 项目实现

    1. 在一级目录下面新建文件夹aspect,存放切片文件
    2. 在上述文件夹下新建文件ServiceLogAspect
    3. 文件主要啷个部分:
      1)申明该切片作用的对象,即运行哪些业务需要service。
      2)申请业务那一部分添加切片,@Before,@After,@Round,@AfterReturning,@AfterThrowing,并书写需要额外实现的功能。
      3)特点:使用起来非常方便,只需要新建一个文件就好,不需要额外修改其他的原始文件。

    文章参考:
    1. 从零开始—仿牛客网讨论社区项目(二)
    5. 关于介绍AJAX的文章

  • 相关阅读:
    ARM接口编程—IIC总线(exynos 4412平台)
    VBA实现全文件快速替换
    [汇编语言]更灵活的定位内存地址的方法
    Django--ORM 多表查询
    基于springboot的共享汽车管理系统源码数据库
    Latice CPLD jed转VME文件简介
    八股文随笔2
    使用html+css+js实现一个静态页面(含源码)
    POI Excel单元格样式超过最大数(4000或64000)的解决方案
    Hadoop如何进行分布式存储和处理大数据?
  • 原文地址:https://blog.csdn.net/qq_42974034/article/details/126023226