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

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

Spring MVC
- 服务端三层结构:表现层(MVC)、业务层Service、数据层Repository
MVC:设计模式,解决表现层的问题,Model建立联系,View负责渲染
- Model,Controller会将service处理返回的数据,封装到model
- View,Controller将model传给view,view利用model生成html,返回给C
- Controller,负责处理请求

- 核心组件:前端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层去实现
前端页面和静态资源拷贝

@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