Spring Boot开发的个人博客
Spring Boot开发小而美的个人博客
个人博客功能:

技术组合:
- 后端:基于Spring Boot的框架体系中采用JPASpring Boot和thymeleaf模板技术进行前后端分离开发;数据库:MySQL;前端UI:采用Semantic UI框架进行界面设计
工具与环境:
- IDEA
- Maven 3
- JDK 8
- Axure RP 8
课程内容模块:
需求调研及功能方案制定
你能学得什么?
- 基于Spring Boot构建的完整且全栈的开发框架
- 采用Semantic UI框架进行界面设计的技术路径
- 提供一套完整的博客系统源代码及详细设计文档
1、需求与功能
1.1 用户故事
用户故事是敏捷框架中的一种开发手段。有助于开发者换位思考,并从用户体验出发更好地把握需求,从而创造具有商业价值的功能。
用户故事最好是用户团队编写
用户故事模板 :
- As a (role of user), I can perform (some feature) actions in order to achieve (some business value).
- 充当(某种角色),我能够进行(某种功能)行动以获取(某种商业价值)的好处。
关键点 :角色、功能、商业价值
举例 :
- 在线招聘平台上的注册人员希望浏览过去三天内的职位信息 ,以便掌握最新的招聘信息。
- 对于企业而言 ,可以在平台上发布新的招聘信息 。
个人博客系统的用户故事:
角色:普通访客 ,管理员(我)
-
访客用户可分页浏览所有博客
-
访客用户可快速获取前6大热门分类
-
访客用户可全面了解所有分类
-
访客用户可查看特定分类下的详细文章
-
访客用户可快速筛选出最多10个高关注度标签
-
访客用户可深入了解各类标签信息
-
访客用户可通过年度时间线追踪全部文章
-
访客用户可迅速获取最新推荐文章
-
访客用户可通过关键字实现全文搜索
-
访客用户可完整阅读任意一篇独立文章
-
访客用户可在评论区与作者互动交流
-
访客用户可通过赞赏功能支持内容创作
-
访客用户可通过微信端便捷阅读文章
-
首页入口处附有二维码引导关注我公众号
-
我作为用户名密码认证的后台操作者
-
我拥有管理博客的权限与能力
- 我能新增及发布新文章内容
- 我能对文章进行精准分类设置
- 我能为文章设置丰富多样的标签标记
- 我能实时更新文章相关信息资料
- 我能有效管理各项基础属性数据
- 我能通过关键词精准搜索定位目标文章
-
我能维护博客的分类系统
-
我能创建一个新的标签
-
我能调整一类内容
-
我能取消一类内容
-
我能通过输入类别名称找到对应的类目
-
我能够对所有类别中的数据进行分类整理与管理
- 我能够新增数据类别并建立新的分类标准
- 我能够根据需求重新定义现有数据分类标准
- 我能够删除不需要的数据分类信息并优化分类体系
- 通过输入关键词或字段名即可快速定位到对应的分类系统信息
1.2 功能规划

2、页面设计与开发
2.1 设计
页面规划:
前端展示:首页、详情页、分类、标签、归档、关于我
后台管理:模板页
2.2 页面开发
2.3 插件集成
3、框架搭建
3.1 构建与配置
1、引入Spring Boot模块:
- web
- Thymeleaf
- JPA
- MySQL
- Aspects
- DevTools
2、application.yml配置
使用 thymeleaf 3
pom.xml:
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>
12
application.yml:
1
spring:
thymeleaf:
mode: HTML
123
AI写代码xml
- 数据库连接配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf-8
username: root
password: root
jpa:
hibernate:
ddl-auto: update
show-sql: true
12345678910
AI写代码yaml

日志配置
application.yml:
logging:
level:
root: info
com.imcoding: debug
file: log/imcoding.log
12345
AI写代码yaml
logback-spring.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--包含Spring boot对logback日志的默认配置-->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<!--重写了Spring Boot框架 org/springframework/boot/logging/logback/file-appender.xml 配置-->
<appender name="TIME_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i</fileNamePattern>
<!--保留历史日志一个月的时间-->
<maxHistory>30</maxHistory>
<!--
Spring Boot默认情况下,日志文件10M时,会切分日志文件,这样设置日志文件会在100M时切分日志
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="TIME_FILE" />
</root>
</configuration>
<!--
1、继承Spring boot logback设置(可以在appliaction.yml或者application.properties设置logging.*属性)
2、重写了默认配置,设置日志文件大小在100MB时,按日期切分日志,切分后目录:
my.2017-08-01.0 80MB
my.2017-08-01.1 10MB
my.2017-08-02.0 56MB
my.2017-08-03.0 53MB
......
-->
1234567891011121314151617181920212223242526272829303132333435363738394041424344
AI写代码xml

- 生产环境与开发环境配置
- application-dev.yml
- application-pro.yml
3.2 异常处理
1、定义错误页面 * error
2、全局处理异常
统一处理异常:
@ControllerAdvice
public class ControllerExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);
/** * 异常处理
* @param request
* @param e
* @return
*/
@ExceptionHandler({Exception.class})
public ModelAndView handleException(HttpServletRequest request, Exception e) throws Exception {
logger.error("Request URL : {} , Exception : {}", request.getRequestURL(), e);
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
ModelAndView mav = new ModelAndView();
mav.addObject("url", request.getRequestURL());
mav.addObject("exception", e);
mav.setViewName("error/error");
return mav;
}
}
1234567891011121314151617181920212223242526
AI写代码java
运行

错误页面异常信息显示处理:
<div>
<div th:utext="'<!--'" th:remove="tag"></div>
<div th:utext="'Failed Request URL : ' + ${url}" th:remove="tag"></div>
<div th:utext="'Exception message : ' + ${exception.message}" th:remove="tag"></div>
<ul th:remove="tag">
<li th:each="st : ${exception.stackTrace}" th:remove="tag"><span th:utext="${st}" th:remove="tag"></span></li>
</ul>
<div th:utext="'-->'" th:remove="tag"></div>
</div>
123456789
AI写代码html

3、资源找不到异常
@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundExcepiton extends RuntimeException {
public NotFoundExcepiton() {
}
public NotFoundExcepiton(String message) {
super(message);
}
public NotFoundExcepiton(String message, Throwable cause) {
super(message, cause);
}
}
1234567891011121314
AI写代码java
运行

3.3 日志处理
1、记录日志内容
- 请求 url
- 访问者 ip
- 调用方法 classMethod
- 参数 args
- 返回内容
2、记录日志类:
@Aspect
@Component
public class LogAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/** * 定义切面
*/
@Pointcut("execution(* com.imcoding.web.*.*(..))")
public void log() {
}
@Before("log()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
ReqeustLog reqeustLog = new ReqeustLog(
request.getRequestURL().toString(),
request.getRemoteAddr(),
classMethod,
joinPoint.getArgs()
);
logger.info("Rquest ----- {}",reqeustLog);
}
@After("log()")
public void doAfter() {
//logger.info("---------- doAfter 2 ----------");
}
@AfterReturning(returning = "result",pointcut = "log()")
public void doAtfertRturning(Object result) {
logger.info("Return ------ {}",result );
}
private class ReqeustLog {
private String url;
private String ip;
private String classMethod;
private Object[] args;
public ReqeustLog(String url, String ip, String classMethod, Object[] args) {
this.url = url;
this.ip = ip;
this.classMethod = classMethod;
this.args = args;
}
@Override
public String toString() {
return "ReqeustLog{" +
"url='" + url + '\'' +
", ip='" + ip + '\'' +
", classMethod='" + classMethod + '\'' +
", args=" + Arrays.toString(args) +
'}';
}
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
AI写代码java
运行

3.4 页面处理
1、静态页面导入project
2、thymeleaf布局
- 定义fragment
- 使用fragment布局
3、错误页面美化
4、设计与规范
4.1 实体设计
实体类:
- 博客 Blog
- 博客分类 Type
- 博客标签 Tag
- 博客评论 Comment
- 用户 User
实体关系:

评论类自关联关系:

Blog类:
该平台提供的外链图片无法正常转存(img-myqJaCbm-1582203013144)。请确保您已按照要求将图像文件保存后再进行上传操作以避免被系统拦截或无法识别的问题。
Type类:
外部链接中的图片存储出现了问题
Tag类:
外链图片转存失败,源站可能有防盗链机制,建议您手动保存图片后再重新上传
Comment类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xWsMOwIz-1582203013151)(http://on91wk3hn.bkt.clouddn.com/17-10-14/77104424.jpg)]
User类:
由于图片无法显示,请提醒用户检查来源站点是否存在防盗链保护机制,并确保图片保存后直接上传
4.2 应用分层
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NnhzTY13-1582203013153)(http://on91wk3hn.bkt.clouddn.com/17-10-14/64528736.jpg)]
4.3 命名约定
Service/DAO层命名约定:
- 使用get作为前缀来获取单一对象的方法。
- 通过list作为前缀的方式来获取多个对象。
- 使用count作为前缀来获取统计数据。
- 采用save(推荐)或insert作为前缀来进行插入操作。
- 采用remove(推荐)或delete作为前缀来进行删除操作。
- 通过update作为前缀来进行数据更新操作。

5、后台管理功能实现
5.1 登录
1、构建登录页面和后台管理首页
2、UserService和UserRepository
3、LoginController实现登录
4、MD5加密
5、登录拦截器
5.2 分类管理
1、分类管理页面
2、分类列表分页
{
"content":[
{"id":123,"title":"blog122","content":"this is blog content"},
{"id":122,"title":"blog121","content":"this is blog content"},
{"id":121,"title":"blog120","content":"this is blog content"},
{"id":120,"title":"blog119","content":"this is blog content"},
{"id":119,"title":"blog118","content":"this is blog content"},
{"id":118,"title":"blog117","content":"this is blog content"},
{"id":117,"title":"blog116","content":"this is blog content"},
{"id":116,"title":"blog115","content":"this is blog content"},
{"id":115,"title":"blog114","content":"this is blog content"},
{"id":114,"title":"blog113","content":"this is blog content"},
{"id":113,"title":"blog112","content":"this is blog content"},
{"id":112,"title":"blog111","content":"this is blog content"},
{"id":111,"title":"blog110","content":"this is blog content"},
{"id":110,"title":"blog109","content":"this is blog content"},
{"id":109,"title":"blog108","content":"this is blog content"}],
"last":false,
"totalPages":9,
"totalElements":123,
"size":15,
"number":0,
"first":true,
"sort":[{
"direction":"DESC",
"property":"id",
"ignoreCase":false,
"nullHandling":"NATIVE",
"ascending":false
}],
"numberOfElements":15
}
1234567891011121314151617181920212223242526272829303132
AI写代码javascript
运行

3、分类新增、修改、删除
5.3 标签管理
5.4 博客管理
1、博客分页查询
2、博客新增
3、博客修改
4、博客删除
6、前端展示功能实现
6.1 首页展示
1、博客列表
2、top分类
3、top标签
4、最新博客推荐
5、博客详情
1、Markdown 转换 HTML
- commonmark-java https://github.com/atlassian/commonmark-java
- pom.xml引用commonmark和扩展插件
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-heading-anchor</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-gfm-tables</artifactId>
<version>0.10.0</version>
</dependency>
123456789101112131415
AI写代码xml

2、评论功能
- 评论信息提交与回复功能
- 评论信息列表展示功能
- 管理员回复评论功能
