【项目】开发社区核心功能

社区项目中的社区核心功能开发。主要包含内容:前缀树(过滤敏感词),AJAX(发布帖子,发送私信),事务管理(添加评论),AOP(统一处理异常,统一记录日志)。

过滤敏感词

  • 前缀树

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

    • 定义前缀树

      在util包下新建SensitiveFilter.class,在该类中新建TrieNode类:

    • 根据敏感词,初始化前缀树

      SensitiveFilter.class中的成员变量:

      加载敏感词文件:

      将敏感词添加到前缀树中:

    • 编写过滤敏感词的方法

发布帖子

  • AJAX

    • (Asynchronous JavaScript and XML)异步的JavaScript与XML,不是一门新技术,只是一个新术语
    • 使用AJAX,网页能够将增量更新呈现在页面上,而不需要刷新整个页面
    • 虽然X代表XML,但是目前JSON的使用比XML更加普遍
  • 示例

    • 使用JQuery发送AJAX请求

      在Controller包下的AlphaController中写一个测试方法:

      在html文件中使用JQuery发送请求:

      可以在网页的控制台提交表单后看见数据。

  • 实践

    • 采用AJAX请求,实现发布帖子的功能

      在dao下的DiscussPostMapper类中新建增加帖子的方法:

      对应的resource下的discusspost-mapper.xml中的sql语句进行增加:

      在service下的DiscussPostService类下新建增加帖子的方法,判断传入的参数不能为空后对帖子内容进行转义和过滤:

      在controller下新建DiscussPostController类,声明访问路径为"/discuss",并在类中新建新增帖子的方法,判断客户端数据无误后传到服务器:

      最后在index.js文件中编写JQuery方法发送异步请求。如果点击我要发贴后不刷新界面,显示发帖框。发帖成功会显示提示框,2s后刷新界面:

帖子详情

  • DiscussPostMapper

  • DiscussPostService

  • DiscussPostController

  • index.html

    • 在帖子标题上增加访问详情页面的链接

  • discuss-detail.html

    • 处理静态资源的访问路径

    • 复用index.html的header区域

    • 显示标题、作者、发布时间、帖子正文等内容

事务管理

  • 什么是事务

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

  • 事务的特性(ACID)

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

    • 常见并发异常:

      • 第一类丢失更新,第二类丢失更新

        第一类丢失更新:某一个事务回滚,导致另外一个事务已更新的数据丢失。

        时刻 事务1 事务2
        T1 Read:N=10
        T2 Read:N=10
        T3 Write:N=9
        T4 Commit:N=9
        T5 Write:N=11
        T6 Rollback:N=10

        第二类丢失更新:某一个事务提交,导致另外一个事务已更新的数据丢失。

        时刻 事务1 事务2
        T1 Read:N=10
        T2 Read:N=10
        T3 Write:N=9
        T4 Commit:N=9
        T5 Write:N=11
        T6 Commit:N=11
      • 脏读、不可重复读、幻读

        脏读:某一个事务,读取了另外一个事务未提交的数据。

        时刻 事务1 事务2
        T1 Read:N=10
        T2 Write:N=11
        T3 Read:N=11
        T4 Rollback:N=10

        不可重复读:某一个事务,对同一个数据前后读取的结果不一致。

        时刻 事务1 事务2
        T1 Read:N=10
        T2 Read:N=10
        T3 Write:N=11
        T4 Commit:N=11
        T5 Read:N=11

        幻读:某一个事务,对同一个表前后查询到的行数不一致。

        时刻 事务1 事务2
        T1 Select:id<10(1,2,3)
        T2 Insert:id=4
        T3 Commit:id=(1,2,3,4)
        T4 Select:id<10(1,2,3,4)
    • 常见隔离级别:

      • Read Uncommitted:读取未提交的数据
      • Read Committed:读取已提交的数据
      • Repeatable Read:可重复读
      • Serializable:串行化
      隔离级别 第一类丢失更新 脏读 第二类丢失更新 不可重复读 幻读
      Read Uncommitted Y Y Y Y Y
      Read Committed N N Y Y Y
      Repeatable Read N N N N Y
      Serializable N N N N N
  • 实现机制

    • 悲观锁(数据库)

      • 共享锁(S锁)

        事务A对某数据加了共享锁之后,其他事务只能对该数据加共享锁,但不能加排他锁。

      • 排他锁(X锁)

        事务A对某数据加了排他锁之后,其他事务对该数据既不能加共享锁,也不能加排他锁。

    • 乐观锁(自定义)

      • 版本号,时间戳等

        在更新数据前,检查版本号是否发生变化。若变化则取消本次更新,否则就更新数据(版本号+1)。

  • Spring事务管理

    • 声明式事务

      • 通过XML配置,声明某方法的事务特征。

      • 通过注解,声明某方法的事务特征

    • 编程式事务

      • 通过TransactionTemplate管理事务,并通过它执行数据库的操作。

显示评论

  • 数据层

    • 根据实体查询一页评论数据

    • 根据实体查询评论的数量

      首先在dao包下实现CommentMapper类:

      并在resources包下的mapper包肿新建comment-mapper.xml实现sql。

  • 业务层

    • 处理查询评论的业务

    • 处理查询评论数量的业务

      在service包下新建CommentService类,实现两个查询函数

  • 表现层

    • 显示帖子详情数据时,同时显示该帖子所有的评论数据

      在controller包下的DiscussPostController类中补充getDiscussPost函数:

      显示帖子详情数据:

      评论数据:

添加评论

  • 数据层

    • 增加评论数据

      在dao包下的CommentMapper类中增加方法insertComment来增加评论数据,并在相应的comment-mapper中增加sql语句:

    • 修改帖子的评论数量

      在dao包下的DiscussPostMapper类中增加方法updateCommentCount来更新帖子评论数量:

  • 业务层

    • 处理添加评论的业务:先增加评论,再更新帖子的评论数量

      先在service包下的DiscussPostService类中增加方法updateCommentCount来增加评论在表中:

      然后在CommentService类中增加方法addComment调用updateCommentCount方法增加评论,然后更新帖子评论数量。这里需要用事务管理保证新增帖子操作前后一致性:

  • 表现层

    • 处理添加评论数据的请求

      在controller包下新建CommentController类,利用路径传入帖子ID,进行增加评论操作后,提交并重定向回原帖子:

    • 设置添加评论的表单

      利用隐藏输入框提交回复、评论的实体类型和实体id:

私信列表

  • 私信列表

    • 查询当前用户的会话列表,每个会话只显示一条最新的私信

      在dao层下新建MessageMapper类,并在对应resources/mapper包下新建message-mapper.xml写对应的查询语句。

      MessageMapper:

      在Service层下新建MessageServer类,并向上层提供对应的查询服务:

      在Controller层下新建MessageController类,显示当前登录用户的会话列表:

    • 支持分页显示

      SQL语句添加限制:

  • 私信详情

    • 查询某个会话所包含的私信

      MessageController类下新增方法getLetterDetail:

      其中查询target的方法是利用conversationId的特点来查询的。conversationId由两个私信用户的id组成,类似于:id1_id2的形式,小的id在前面。如果id1==当前登录用户,那么id2就是目标用户,否则id1是目标用户:

    • 支持分页显示

发送私信

  • 发送私信

    • 采用异步的方式发送私信

      异步:异步任务主要的应用场景为发送短信、邮件、异步结果的通知、积分的修改等等业务。指一个动作(类似于发给别人消息之类的动作)完成之后不等待直接进入下一个任务。

      在dao层的MessageMapper类中新增方法用于新增一条私信:

      在Service层的MessageService类中新增方法,过滤敏感词并插入新消息:

      在Controller层中的MessageController类中新增方法:

      letter.js文件中利用JQuery函数处理异步:

    • 发送成功后刷新私信列表

      letter-detail.html文件中新增方法:

  • 设置已读

    • 访问私信详情时,将显示的私信设置为已读状态

      在dao层的MessageMapper类中新增函数更新消息已读后的状态:

      在Service层增加方法:

      在Controller层中的MessageController类中的getLetterDetail方法中新增代码,因为调用此方法时代表已经点开了和某人的私信列表,即已读:

统一处理异常

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

全局配置类:在Controller包下新建advice包,在addvice包下新建ExceptionAdvice类用来做全局配置类:

在HomeController类下配置error页面:

统一记录日志

需求:帖子模块、评论模块、消息模块

AOP的概念

  • Aspect Oriented Programing,即面向方面(切面)编程
  • AOP是一种编程思想,是对OOP的补充,可以进一步提高编程的效率。

AOP术语

AOP的实现

  • AspectJ

    • AspectJ是语言级的实现,它扩展了Java语言,定义了AOP语法
    • AspectJ在编译期织入代码,它有一个专门的编译器,用来生成遵守Java字节码规范的class文件
  • Spring AOP

    • Spring AOP使用纯Java实现,它不需要专门的编译过程,也不需要特殊的类装载器。
    • Spring AOP在运行时通过代理的方式织入代码,只支持方法类型的连接点。
    • Spring AOP支持对AspectJ的集成。
  • JDK动态代理

    • java提供的动态代理技术,可以在运行时创建接口的代理实例
    • Spring AOP默认采用此种方式,在接口的代理实例中织入代码
  • CGlib动态代理

    • 采用底层的字节码技术,在运行时创建子类代替实例
    • 当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码
  • 使用方式

    • 在com.nowcoder.community下新建包aspect,在aspect包下新建类,示例为AlphaAspect类。类上添加注解@Component和@Aspect。pointcut方法上添加注解@Pointcut(),用于声明在哪些切点上操作。

    • @Before表示在切点前运行@After表示在切点后运行@AfterReturning表示在返回值之后运行@AfterThrowing表示在抛出异常后运行@Around表示在切点前和后运行,其中JoinPoint是目标对象上织入代码的位置,用Object承载,然后在这行代码之前或者之后写代码就可以了。

0%