1. 过滤敏感词
1)可以使用JDK自带的replace方法替换敏感词,但在实际应用中敏感词比较多、字符串可能比较长(发布的一篇文章)这种情况下用replace去替换性能就比较差。
2)使用前缀树来实现过滤敏感词的算法。但前缀树方法也有一些局限性,不能实现对停顿词、重复词的检查,考虑到此可以优化为DFA算法过滤敏感词。
1.1 目的
在发帖子的过程中,可能会有人发布一些敏感词信息,在提交到系统中需要先进行一次过滤,故需要开发过滤敏感词功能
1.2 实现方法
通过前缀树,然后书写过滤敏感词方法,能够实现敏感词的过滤功能。
1.3 前缀树
- 名称:Trie,字典树,查找树
- 特点:查找效率高,消耗内存大(以空间换时间)
- 应用:字符串检索,词频统计和字符串排序
1.4 敏感词过滤步骤(为发帖子做准备)
- 定义前缀树
1)敏感词文件:在resource中添加文件sensitive-words.txt
2)添加工具类sensitivefilter:(核心搞懂前缀树和过滤功能实现说明)
(1):新建前缀树:TrieNode(),私有
(2):初始化前缀树:init() ,私有。用@PostConstruct标注,说明只是在类加载的时候运行一次。
(3):将一个敏感词添加到前缀树中,addKeyword(String keyword),私有
(4):根据前缀树,对输入的字符串进行过滤,filter(String text),公有,需要考虑符号穿插。
(5):判断输入字符串是否为符号(符号课采取跳过原则,例如“★开★票★★”,还是能够被检测出来) - 进行测试敏感词过滤效果
1)新建一个敏感词的测试类SensitiveTests,然后进行测试文本过滤效果
2)调用公有的铭感次过滤函数sensitiveFilter.filter(text),实现功能
2. 发布帖子
2.1 AJAX介绍
- Astnchronous JavaScript and XML
- 异步的JavaScipt与XML,不是一门新技术,只是一个新的术语
- 使用AJAX,网页能够将增量更新呈现在页面上面,而不需要刷新整个页面
- 虽然X代表XML,但是现在使用JSON比XML更加广泛。
2.2 AJAX使用实例
- 导包:导入包fastjson(用于)
- Util层:在CommunityUtil类中实现获取JSON字符串的三个构造方法:getJSONString()
1) 直接在CommunityUtil类中测试获取json字符串的方法, - Dao层(把接收到的信息传入数据库):
1)在DiscussPostMapper中增加插入数据的方法insertDiscussPost。
2)在mapper文件中增加insert方法的实现。 - Service层:在DiscussPostService中增加调用的实现findDiscussPosts。
- Controller层:增加DiscussPostController类,添加add访问请求,并修改index.js。
1)在add方法中,返回json信息而不是文本,故需要添加注解@ResponseBody
2)add的作用是添加帖子的功能,返回的是json信息,被前端读取后,能够在前端自己完成刷新功能,而不是整体刷新。
3)修改index.js使得前端能够完成一定的功能。(减轻服务器的压力,负载均衡)
4)修改index.html中弹出框和提示框的内容
3. 帖子详情
3.1 实现功能
通过点击帖子栏中帖子的标题,能够获取用户帖子的详情
3.2 实现过程
- Dao层:在DiscussPostMapper中添加根据帖子id查询到帖子对象功能的sql方法申明,并在mapper.xml书写sql语句。
- Service层:在DiscussPostService中添加根据帖子id查询到帖子对象功能方法(直接调用)
- Controller层:根据用户id获取帖子对象和用户对象,传入model中方便在页面进行调用,然后页面跳转至详情页。
- index.html:修改点击帖子标题,跳转到详情页面的连接
- discuss-detail.html
1)处理静态资源的访问路径
2)复用index.html的heade区域
3)根据得到帖子的对象,在页面显示标题,作者,发布时间和帖子正文内容
4. 事务管理
4.1 事务介绍
事务是由N步数据库操作序列组成的逻辑执行单元,这系列操作要么全执行,要么全放弃执行
4.2 事务四大特性
- 原子性(Atomicity):事务是应用中不可再分的最小执行体。
- 一致性(Consistency):事务执行的结果,须使数据从一个一致性状态,变为另一个一致性状态。
- 隔离性(Isolation):各个事务的执行互不干扰,任何事务的内部操作对其他的事务都是隔离的。
- 持久性(Durability):事务一旦提交,对数据所做的任何改变都要记录到永久存储器中
4.3 事务的隔离性
- 并发异常
读取未提交都不能解决
1)第一类丢失更新:一个查询回滚,一个查询更新,前者慢一点,会导致后者的更新丢失
2)脏读:一个事务读取另一个事务未提交的数据(读取已提交能够解决)
3)第二类丢失更新:两个都进行查询更新,会使得前者更新值丢失
4)不可重复度:一个事务进行数据更新,另一个事务进行多次访问出现不一致。(可重复读能够解决)
5)幻读:对数据的添加和删除,使得另一个事务在多次查询的时候感觉出现了幻觉(类似不可重复读,不过前者是数据更新,后者是数据修改或者删除)(序列化能够解决)
4.4 事务的实现机制
- 悲观锁(数据库自带)
1)共享锁(S锁):事务A对某数据加上共享锁后,其他事务只能对数据加共享锁,不能加排他锁
2)排它锁(X锁):事务A对某数据加上排他锁后,其他事务既不能给数据加上排他锁,也不能加上共享锁。 - 乐观锁(自定义)
1)版本号,时间戳等
2)版本号工作原理:在更新数据之前,检查版本号是否发生变化,若变化则取消本次更新,否则就更新内容,且版本号+1。
4.4 事务的管理(Spring Boot)
- 申明式事务(XML+注解+方法)(更常用,编写简单)
1)通过XML配置,申明某方法的事务特征
2)通过注解,申明某方法的事务特征 - 编程式事务(TransactionTemplate+方法内部)
1)通过TransactionTemplate管理事务
2)并通过她执行数据库的操作 - 两者区别
1)更简单:申明式事务只能申明方法的整体,不能对局部代码进行事务管理;但是事务管理非常方便,只需要添加注解
2)更精细:编程式事务形成一个TransactionTemplate对象,把需要管理的代码可以局部圈起来。 - 管理事务的两个参数
1)隔离级别:在上述已经说明了(一般用可重复度)
2)关联方式:REQUIRED;REQUIRES_NEW;NESTED
(1)REQUIRED:支持当前事务(外部事务),如果不存在则创建新事务.
(2)REQUIRES_NEW创建一个新事务,并且暂停当前事务(外部事务).
(3)NESTED:如果当前存在事务(外部事务),则嵌套在该事务中执行(独立的提交和回滚),否则就会REQUIRED一样. - 两种管理事务方式例子
1)在AlphaService里面书写两种事务管理方式事例。
2)申明式事务:在方法上加上注解:@Transactional,里面有两个参数
3)新增用户,新增帖子,然后加上一个会报错的语句
4)如果有事务管理,会因为存在报错,而进行回滚,其他语句也不会执行。
5)新建一个测试类DiscusspostTests,执行两种方法,查看数据库的改变。数据库就没变。
5. 显示评论
- 评论表对象:存在非常多种类型的评论,用参数entity_type进行区分。
- 实体类:定义评论entity:一实体一表,添加评论表。
- 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层进行实现 - 业务层Service:创建CommentService
1)处理查询评论的业务:直接调用Dao方法
2)处理查询评论数量的业务,直接调用Dao方法 - 实现层Controller:这一层与帖子详情功能共享,在同一个页面
1)根获取不同类型评论对象:评论对象和回复对象(评论的评论,回复对象还有两种),传值给model。(思路简单,步骤繁琐了点) - .xtml
- 有两类评论,评论和回复两种类型,需要分别进行展示。通过循环操作。
- 在帖子详情里面,复用首页分页的功能
6. 添加评论(事务管理)
- Dao层:
(1)增加评论数据:在comment里书写:insertComment(Comment comment);
(2)修改帖子的评论数量:在discussPost里书写:updateCommentCount
(3)对应书写mapper.xml的文件 - Service层:
1)处理添加评论的功能:
2)先增加评论,再更新帖子的评论数量(事务操作,两者连在一起):在DiscussPostService先书写更新帖子数量,然后再CommentService里面书写帖子个数。 - Controller层:
1)处理添加评论数据的请求:书写方法:addComment - html层:
- 设置添加评论的表单(有两个地方需要放入表单)
7. 私信列表+私信详情
7.0 功能说明
- 私信列表:通过私信列表,能够查询到最近有谁给你发了消息,显示最新的一条消息(类似首页帖子)
- 私信详情:点击消息,能够直到消息的详情,把所有交流的内容都放出来。
- 两者都支持分页展示功能
7.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; //交流时间
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)Dao层:添加MessageMapper
(1)添加方法:新增消息
(2)添加方法:修改消息状态。出现在显示名单里的标记为已读状态。
2)**Dao层:**添加Message.xml书写对应sql文件
3)**Service层:**直接调用Dao的查询方法
4)Controller层:添加add请求,采用异步通信方法,返回json格式,在前端的message.js中进行逻辑控制。
5)修改对应的.html文件。
9. 统一处理异常
9.1 处理异常的四种方式
- @ControllerAdvice**(最常用)**
- 用于修饰类,表示该类是Controller的全局配置类。
- 在此类中,可以对Controller进行如下三种全局配置:异常处理方案、绑定数据方案、绑定参数方案。
- @ExceptionHandler
- 用于修饰方法,该方法会在Controller出现异常后被调用,用于处理捕获到的异常。
- @ModelAttribute
- 用于修饰方法,该方法会在Controller方法执行前被调用,用于为Model对象绑定参数。
- @DataBinder
- 用于修饰方法,该方法会在Controller方法执行前被调用,用于绑定参数的转换器。
9.2 统一处理异常步骤:
- 在template目录下新建一级文件
(1)把最容易出错的两个.xml文件存入:404(请求资源不存在)和500(服务端发生错误) - 在Controller中新建文件夹advice,然后新建ExceptionAdvice
(1)记录发生错误处的日志信息,并且逐行写下原因是什么
(2)根据发生错误处请求的方式,分为同步请求和异步请求,分别返回错误页面和错误消息json。 - 一旦系统中发送了错误,那么就会自动运行这个方法,进行统一异常的处理机制,非常方便,这就是框架的强大之处。
10. 统一记录日志
10.1 需求
比如一些请求执行的过程中,需要对其进行日志记录,但是一个个在求情中添加不是很方便,故使用功能切面编程技术去实现。
- 在Controller运行的时候,可以通过拦截器进行日志记录
- 在发生异常的时候,可以通过统一异常管理进行处理
- 在业务运行的时候,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)
- JDK动态代理
(1)Java提供的动态代理技术,可以在运行时创建接口的代理实例
(2)Spring AOP 默认采用此种方式,在接口的代理实例中置入代码。 - CGLib动态代理
(1)采用底层的字节码技术,在运行时创建子类代理实例
(2)当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中植入代码
10.5 项目实现
- 在一级目录下面新建文件夹aspect,存放切片文件
- 在上述文件夹下新建文件ServiceLogAspect
- 文件主要啷个部分:
1)申明该切片作用的对象,即运行哪些业务需要service。
2)申请业务那一部分添加切片,@Before,@After,@Round,@AfterReturning,@AfterThrowing,并书写需要额外实现的功能。
3)特点:使用起来非常方便,只需要新建一个文件就好,不需要额外修改其他的原始文件。
文章参考:
1. 从零开始—仿牛客网讨论社区项目(二)
5. 关于介绍AJAX的文章