基于SpringMVC+SpringBoot+MyBatis的博客系统
一、项目简介
博客系统
本项目是一个支持 Markdown 编辑,可以记录学习笔记,生活感悟的博客平台
- 该博客系统的的平台开发语言是基于面向对象思想的Java语言,在windows环境下,集成了SpringBoot 框架作为项目的骨架,使用CSS,HTML等前端技术,并使用MySQL作为本项目的后台数据库
- 本系统有3个数据库表,用户信息表userinfo ,文章信息表articleinfo,评论表commentinfo
- 将项目部署在云服务器上: 部署
二、功能分析
网页博客系统支持以下核心功能:
- 用户注册、登录、退出功能
- 已登录用户编辑文章进行发布、修改、查看详情、删除功能,同时也可以进入其他用户的主页,查看其已发表文章
- 未登录用户可以使用博客列表页,查看所有用户的文章详情
- 文章评论功能
- 文章保存草稿功能.
- 使用 HandlerInterceptor 实现了统一登录拦截器.
- 使用 ResponseBodyAdvice 实现了统一数据格式返回
1. 注册登录
注册使用确认密码,使用MD5+盐值的方式将用户密码加密存储在数据库中
登录时在数据库里面查询对比用户名,调用解密函数成功登录后将用户信息存储在session里面
实现用户注册和登录功能,让用户能够创建个人账号并通过登录来管理自己的博客内容
2. 编写发表文章
用户登录以后,支持MarkDown语法,使用户能够以简洁的方式编辑和格式化文章内容,用户可以将编辑好的文章发布到博客平台上,让其他人阅读和评论
3. 主页列表
未登录也可以浏览博客,查看别人发表的博客
- 添加了分页功能: 分页实现
4. 修改文章
修改必须是建立在已登录的基础上,点击修改按钮,跳转到编辑页面,文章标题和内容都不为空,点击修改,即可成功
5. 密码加盐
使用MD5加密。每次加密之前,给密码加上不同的盐值(32)生成一个最终密码(32)
存储在数据库里面的密码(64)=盐值+最终密码
6. 修改密码
7. 存为草稿
草稿功能和发表文章类似,但是也有稍微的区别
- 新文章,发表和草稿一样,但是存入数据库时,将草稿文章状态state设为2
- 对于已有的文章,修改文章时,将草稿文章状态state设为2
<insert id="SaveDraft">
insert into articleinfo (title,content,uid,state) values(#{title},#{content},#{uid},2)
</insert>
三、相关技术
1. SpringBoot
- 快速集成框架,Spring Boot 提供了启动添加依赖的功能,用于集成各种框架
- 内置运行容器,无需配置 Tomcat 等 Web 容器,直接运行和部署程序
- 快速部署项目,无需外部容器即可启动并运行项目
- 抛弃繁琐的 XML,使用注解和配置的方式进行开发
- RequestMapping,是用来注册接口的路由
2. Mybatis
MyBatis 是一款优秀的持久层框架,MyBatis 去除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
MyBatis 也是一个 ORM 框架,会将数据库模型的每张表都映射为一个 Java 类,把字段映射为对象的属性,也就是说使用 MyBatis 可以像操作对象一样来操作数据库中的表
3. SpringMVC
四、核心设计代码
统一返回数据格式的封装
public class AjaxResult {
public static HashMap<String, Object> success(Object data) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("msg", "");
result.put("data", data);
return result;
}
public static HashMap<String, Object> fail(int code,String msg) {
HashMap<String, Object> result = new HashMap<>();
result.put("code", code);
result.put("msg", msg);
result.put("data", "");
return result;
}
统⼀的数据返回格式可以使用 @ControllerAdvice ,
类实现ResponseBodyAdvice 接口,重写supports() 方法和beforeBodyWrite() 方法实现
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if(body instanceof HashMap){//本身已经封装好
return body;
}
if(body instanceof String){//返回类型是String(特殊类型)
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(AjaxResult.success(body));
}
return AjaxResult.success(body);
}
}
统一异常的处理
@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执行某个方法事件
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
@ExceptionHandler(Exception.class)
public Object exceptionAdvice(Exception e){
return AjaxResult.fail(-1,e.getMessage());//程序报错的时候返回给前端的一个对象
}
}
拦截器
Spring 中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两个步骤:
- 创建自定义拦截器loginInterceptor(添加注解@Component),实现 HandlerInterceptor 接口,然后重写 preHandle方法
- 自定义拦截规则Config(加上@Configuration注解),这个类实现 WebMvcConfigurer 接口,然后重写addInterceptors 方法中,使用@Autowired注入拦截器loginInterceptor
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{
//有session 就得到,没有也不创建,和controller 里面的session 不一样
HttpSession session = request.getSession(false);
if(session!= null && session.getAttribute(Constant.SESSION_USERINFO_KEY) != null){
//从session中得到userinfo 对象,表示已经登录
//不等于空,表示当前已经登录
return true;
}
response.setStatus(401);//401表示没有登录,403表示登录但是没有权限
return false;
}
}
基本的注册功能
@RequestMapping("reg")//注册功能
public Object reg(String username,String password){//返回的是包装的数据类型
//1.非空校验
if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)){
return AjaxResult.fail(-1,"非法的参数请求");
}
//2.添加操作
int result = userService.add(username,password);
if(result == 1){
return AjaxResult.success("添加成功!",1);
}else {
return AjaxResult.fail(-1,"数据库添加出错!");
}
}
修改文章
@RequestMapping("/update")//更新文章内容
public int update(HttpServletRequest request,Integer aid,String title,String content){
//1.非空校验
if(aid == null || title == null || content == null){
return 0;
}
UserInfo userInfo = SessionUtil.getLoginUser(request);
if(userInfo != null && userInfo.getId() >0){
return articleService.update(aid,userInfo.getId(),title,content);
}
return 0;
}
密码加盐
public static String encrypt(String password ) {
//每次生成内容不同的,但长度固定的32位的盐值
String salt = UUID.randomUUID().toString().replace("-","");
//最终密码=md5(盐值+密码)
String finalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes());
return salt + finalPassword;
}
function getURLParam(key){
var params = location.search;
if(params.indexOf("?") >=0){
params = params.substring(params.indexOf("?")+1);
var paramArr = params.split('&');
for(var i=0; i<paramArr.length;i++){
var namevalues = paramArr[i].split("=");
if(namevalues[0]==key){
return namevalues[1];
}
}
}else{
return "";
}
}
五、具体实现
- 创建好一个SpringMVC项目
- application.yml,在里面配置连接字符串,配置 MyBatis 中的 XML 路径
spring:
datasource:
url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
username: root
password: 942599
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/**Mapper.xml
configuration: # 配置打印 MyBatis 执行的 SQL
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 使用MyBatis 的方式实现功能
1.添加@Mapper接口
2.添加XML实现接口的文件(具体的SQL) - 添加实体层(model)
user和article 两个实体类,使用@Data 注解,使用数据库表进行属性的创建 - 添加控制层(controller)
UserController 和ArticleController 两个控制,使用@RestController和@RestMapping 注解,负责前后端交互,调用service层返回的数据 - 添加服务层service
UserService 和ArticleService 两个服务,使用@Service 注解,本层不直接操作数据库,调用mapper层,提供controller层调用的方法 - 持久层(mapper)
这层是接口,UserMapper和ArticleMapper,不是类,使用@Mapper注解
在resource 层添加对应的mapper,此处的mapper用来编写SQL语句实现增删改查,添加MyBatis .xml模板语句,注意修改相应的mapper名称
六、基本页面展示
- 用户首先进行注册,注册成功以后跳转至登录页面
- 登录成功跳转至博客列表页面
- 在自己的博客页面,可以编写新的博客,查看已发布的博客内容详情,修改已发布的博客等
- 编写,修改博客的时候,可以直接发表,或者存为草稿(使用文章的状态 state 来区分)
- 主页浏览文章详情使得该篇文章阅读量+1