基于SpringBoot开发blog
2020-9-29
个人博客介绍
博客地址:http://8.129.69.60/
技术栈:
- 后端:Spring Boot 2 + JPA + thymeleaf
- 数据库:MySQL
- 前端UI:Semantic UI
开发环境:
- IDEA 2020
- Maven 3
- JDK 8
需求分析
**角色:**访客和管理员(后面会添加普通用户)
具体功能:
*管理员:*登录博客后台管理和博客主页
- 管理博客 :
- 发布新博客
- 对博客进行分类
- 对博客打标签
- 修改博客
- 删除博客
- 根据标题,分类,标签查询博客
- 管理博客分类
- 新增分类
- 修改分类
- 删除分类
- 根据分类名称查询分类
- 管理标签
- 新增标签
- 修改标签
- 删除标签
- 根据名称查询标签
*访客和普通用户:*浏览博客主页
-
分页查看所有的博客
-
快速查看博客数最多的6个分类
-
查看所有的分类
-
查看某个分类下的博客列表
-
快速查看标记博客最多的10个标签
-
查看所有的标签
-
查看某个标签下的博客列表
-
根据年度时间线查看博客列表
-
快速查看最新的推荐博客
-
用关键字全局搜索博客
-
查看单个博客内容
-
对博客内容进行评论
-
赞赏博客内容
-
微信扫码阅读博客内容
-
在首页扫描公众号二维码关注我
前端页面设计与开发
**静态资源:**resources/static
- 引用和自定义样式 :css/
- 内容排版 typo.css :typo.css
- 动画 animate.css :animate.css
- 图片资源 :images/
- 插件资源 :lib/
- 编辑器 Markdown :/editormd
- 代码高亮 prism :/prism
- 二维码生成 qrcode.js :/qrcode
- 目录生成 Tocbot :/tocbot
- 滚动侦测 waypoints : /waypoints
博客主页: resources/templates
- 首页 index.html
- 详情页 blog.html
- 分类 types.html
- 标签 tags.html
- 归档 archives.html
- 关于我 about.heml
后台管理: resources/templates/admin
- 首页 index.html
- 分类 types.html
- 分类增加和修改 types-input.html
- 标签 tags.html
- 登录 login.html
thymeleaf布局: _fragments.html
错误处理: resources/templates/error
- 404 : 404.html
- 500 : 500.html
- error : error.html
2020-9-30
后台开发
构建框架和配置
1·创建工程(不需要骨架)和pox.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lj</groupId>
<artifactId>blog</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>blog</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- commonmark:markdown转换成text,html-->
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.10.0</version>
</dependency>
<!-- <h >标签id处理-->
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-heading-anchor</artifactId>
<version>0.10.0</version>
</dependency>
<!-- table处理-->
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-gfm-tables</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope><!–由JDK或者容器提供–>
</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.4.2</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.springboot配置
springboot启动器**
package com.lj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication
public class BlogApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(BlogApplication.class, args);
}
//打成war包时要重写这个方法
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(BlogApplication.class);
}
}
application.yml
spring:
thymeleaf:
mode: HTML
profiles:
active: dev #测试时的配置
comment.avatar: /images/avatar.png
开发 application-dev.yml
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
open-in-view: true
logging: #日志级别
level:
root: info
com.lj: debug
file: log/blog-dev.log
server:
port: 8080
部署 application-pro.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf-8
username: root
password: Root.123456
jpa:
hibernate:
ddl-auto: update
show-sql: true
open-in-view: true
logging:
level:
root: warn
com.lj: info
file: log/blog-pro.log
server:
port: 80
整体结构
日志配置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时,按日期切分日志,切分后目录:
blog.2017-08-01.0 80MB
blog.2017-08-01.1 10MB
blog.2017-08-02.0 56MB
blog.2017-08-03.0 53MB
......
-->
3.异常处理
定义错误页面:
- 404
- 500
- error
统一处理异常:
package lj.cn.handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
/**
* @author lj
* @date 2020-10-01 17:13
* @QQ 851477108
*/
//拦截有Controller注解的控制器
@ControllerAdvice
public class ControllerExceptionHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 定义异常处理
* @param request
* @param e
* @return
* @throws Exception
*/
@ExceptionHandler(Exception.class)
public ModelAndView exceptionHander(HttpServletRequest request, Exception e) throws Exception {
logger.error("Requst URL : {},Exception : {}", request.getRequestURL(),e);
// 当标识了状态码的时候就不拦截
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
throw e;
}
ModelAndView mv = new ModelAndView();
mv.addObject("url",request.getRequestURL());
mv.addObject("exception", e);
mv.setViewName("error/error");
return mv;
}
}
自定义异常配置类:
package lj.cn;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @author lj
* @date 2020-10-01 17:33
* @QQ 851477108
*/
//自定义异常
@ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException{
public NotFoundException() {
}
public NotFoundException(String message) {
super(message);
}
public NotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
4.日志处理
记录日志切面类:
package lj.cn.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* @Description: 日志切面处理
* @author lj
* @date 2020-10-01 18:27
* @QQ 851477108
* @Before: 前置通知, 在方法执行之前执行
* @After: 后置通知, 在方法执行之后执行 。
* @AfterRunning: 返回通知, 在方法返回结果之后执行
* @AfterThrowing: 异常通知, 在方法抛出异常之后
* @Around: 环绕通知, 围绕着方法执行
*/
@Aspect
@Component
public class LogAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//拦截controller控制器
@Pointcut("execution(* lj.cn.controller.*.*(..))")
public void log(){}
/**
* 前置通知
*/
@Before("log()")
public void doBefore(JoinPoint joinPoint){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String url = request.getRequestURL().toString();
String ip = request.getRemoteAddr();
String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
RequestLog requestLog = new RequestLog(url,ip,classMethod,args);
logger.info("Request : {}", requestLog);
}
/**
* 后置通知
*/
@After("log()")
public void doAfter(){
// logger.info("---doAfter---");
}
/**
* 返回通知
* @param result
*/
@AfterReturning(returning = "result",pointcut = "log()")
public void doAfterReturn(Object result){
logger.info("Result : {}",result);
}
/**
* 封装的具体信息
*/
private class RequestLog{
private String url;
private String ip;
private String classMethod;
private Object[] args;
public RequestLog(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 "{" +
"url='" + url + '\'' +
", ip='" + ip + '\'' +
", classMethod='" + classMethod + '\'' +
", args=" + Arrays.toString(args) +
'}';
}
}
}
b站上Spring Boot开发小而美的个人博客,感谢李仁密老师。博主是一个小白,漏洞勿喷。blog开发流程一,开发环境准备和功能简介以及整体框架的选择