牛客网高级项目实战1-1

351 阅读5分钟

HTTP

http——hyper text transfer protocol,用来传输html等内容的应用层协议,规定了C和S间如何通信,以及通信时的数据格式

HTML

浏览器拿到服务器返回的html文件后会解析,发现引用了js/css/图片等文件,发现了文件就向服务器再做请求,返回其依赖的文件,来加载这些文件

Spring MVC

  1. 服务端三层结构:表现层(MVC)、业务层Service、数据层Repository
    MVC:设计模式,解决表现层的问题,Model建立联系,View负责渲染
  • Model,Controller会将service处理返回的数据,封装到model
  • View,Controller将model传给view,view利用model生成html,返回给C
  • Controller,负责处理请求
  1. 核心组件:前端front控制器DispatcherServlet,负责调度
    HandleMapping负责管理RequestMapping的映射
    controller封装数据为Model,返回给front controller。viewtemplate文档值,有一些可以用model里的值进行动态的替换,替换完之后就可以生成一个动态的网页。
    其中需要我们编码的是:controller+view template+model这三部分(MVC)

模板引擎Thymeleaf

Thymeleaf本身以HTML文件为模板
模板文件包括网页的基本结构/表达式,再利用动态的model数据替换,拼成一个动态的HTML
常用语法:

  • 标准表达式:哪些被替换
  • 判断与循环
  • 模板布局,复用一样的区域

MyBatis 核心组件

  • MyBatis-Spring
  • 使用MyBatis操作表,只需要写接口,不需要写实现类
  • SqlSessionFactory
  • SqlSession 向db执行sql
  • XML配置文件 可以整合到application.properties 连接池参数
  • Mapper接口 即Dao接口,声明一些crud的方法
  • Mapper映射器 对应的sql配置文件
    • 用于编写SQL,这样底层可以根据SQL,实现接口,查到的结果和实体类匹配
    • 可以写在的位置 classpath:mapper/*.xml,即将xml文件放在resources下mapper目录里,编译后mapper就会到target的classes文件夹下,即classpath类路径

User类
salt属性是在密码后面拼上5位随机字符串,对拼好的字符串再加密
type 用户类型 0普通 1超级管理员 2版主
status是用户状态,0未激活,1已激活,2已注销

DiscussPost

@Mapper
public interface DiscusssPostMapper{
    //根据userId用来查找某个人的所有博客,根据userId是否为0(首页上不需要就为0),建立动态sql
    //分页:offset起始行,limit页限定大小
    List<DiscussPost> selectDiscussPosts(int userId, int offeset,int limit );
    //分页要知道一共可能多少页,总的记录是多少
    //@Param给写到sql的参数起一个别名
    //当需要动态地拼一个条件,即在<if>中用,并且该方法只有这一个参数,此时必须加别名
    int selectDiscussPostRows(@Param("userId")int userId);
}

注意: 在resource中相应地配置discusspost-mapper.xml,实现上面定义的方法。user_id=#{userId} 前一个是字段,后一个是参数。List中对于List可以解析,不用声明,需要声明的ResultType就是DiscussPost这个自定义类。

<sql id="selectFields">
    id,user_id,title,content,type,status
</sql>
<select id="selectDiscussPosts" resultType="DiscussPost">
    select <include refid="selectFileds"></include>
    from discuss_post
    where status!=2
    <if test="userId!=0">
        and user_id=#{userId}
    </if>
    order by type desc,create_time desc
    limit #{offset},#{limit}
</select>

在test里新建一个MapperTests,用@Test来测试方法
在service层调mapper层,可以根据外键userId查user,把user和DiscussPost组合在一起,返回给页面,因此需要加一个根据userId查user的方法,建一个UserService,调用UserMapper。这样视图层可以基于service层去实现
前端页面和静态资源拷贝

首页查询十条数据的HomeController

@Controller
public class HomeController{
    //跳转到templates下的index.html
    @RequsetMapping(path="/index",method=RequestMethod.GET)
    //传入分页条件,用page封装
    //DispatcherServlet会自动把Page注入给Model
    //所以无需Model.Page,在Thymeleaf里可以直接访问Page对象中的数据
    public String getIndexPage(Model model,Page page){
        //总行数
        page.setRows(discussPostService.findDiscussPostRows(0));
        page.setPath("/index");
        
        List<DiscussPost> list=discusspostService.findDiscussPosts(0,page.getOffset(),page.getLimit());
        //遍历其中每一个,根据userId查到user,然后组装数据放到新的集合里,这个集合封装了DiscussPost和User对象的,返回给页面
        List<Map<String,Object>> discussPosts=new ArrayList<>();
        if(list!=null){
            for(DiscussPost post:list){
                Map<String,Object> map=new HashMap<>();
                map.put("post",post);
                User user=userService.findUserById(post.getUserId());
                map.put("user",user);
                discussPosts.add(map);
            }
        }
        
        //将结果装到model里
        model.addAttribute("discussposts",discussPosts);
        return "/index"
    }
}

处理页面中的相对路径
让thymeleaf去static下面找该资源,以免发生歧义 th:href="@{/css/global.css}" th:src="@{/js/global.js}"
将静态页面转为动态页面,以一个<li></li>为模板,对数据进行循环遍历输出

<!--使用的是discussPosts这个集合,返回的是map对象-->
th:each="map:${discussPosts}"
<!--头像传入动态值 -->
th:src="${map.user.headerUrl}"
<!--发布的标题 -->
th:utext="${map.post.title}"
<!--type==1时候的置顶帖子,span标签 -->
th:if="${map.post.type==1}"
<!--status==1 精华 -->
th:if="${map.post.status==1}"
<!--帖子作者 -->
th:utext="${map.user.username}"
<!--帖子发布时间 -->
th:text="${map.post.createTime}"
<!--指定日期和时间的格式-->
th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}"

注意:

  • 在thymeleaf中,map.user其实就是map.get("user"),写的是点,调用的是get
  • 用th:utext会自动识别转义字符,将<自动写成<
  • ${}里面的引用变量,不仅可以访问,还可以运算

封装分页的组件

  • 属性
    s接收c传来的信息:当前的页码current,显示的上限limit
    s查的数据总数,用于计算总的页数rows
    处理页面的查询路径path,返回给页面,用于复用分页链接

  • 方法
    getOffSset()根据current算出起始行offset,即current*limit-limit
    getTotal()为了显示页码做边界判断,需要总页数,rows/limit[+1]
    getFrom() int from=current-2; return from<1? 1:from;
    getTo() int to=current+2; return to>getTotal()? getTotal():to;

first page: /index?current=1&limit=5
th:ref="@{${page.path}(current=1,limit=5)}"
判断上一页能不能点
两条竖线表示静态的值,后面拼上变量

th:class="|page-item ${page.current==1?'disabled':''}|"

上一页

th:ref="@{${page.path}(current=${page.current-1})}"

页码循环,#调用thymeleaf自带的工具

th:class="|page-item ${i==page.current? 'active':''}|"
th:each=${i:#numbers.sequence(page.from,page.to)}
<a class="page-link" th:href="@{${page.path}(current=${i})}" th:text="${i}">1</a>

下一页

th:class="|page-item ${page.current==page.total? 'disabled':''}|"  
th:ref="@{${page.path}(current=${page.current+1})}"

末页:

th:ref="@{${page.path}(current=${page.total})}"

项目流程图

技术栈

  • Spring Boot, mybatis + spring, Spring MVC, Spring Security
  • 权限@会话管理
    • 注册、登录、退出、状态、设置、授权
    • Spring Email、Interceptor
  • 核心@敏感词、@事务Advice、aop、Transaction
  • 性能@数据结构Redis
  • 通知 消息队列Kafka
  • 搜索 ElasticSearch
  • 线程池
  • 排行、上传
  • 服务器缓存、Caffeine
  • Quartz