赞
踩
项目原型为李仁密老师的小而美的个人博客作品,视频中所采用的持久层ORM框架为JPA,这里我将其改成了mybatis
项目原型地址.
项目总体为前后端整合型开发
前端: JQuery、semanticUI、thymeleaf 等
后端: SSM、SpringBoot 、MySQL 等
前端部分就跟着李仁密老师的B站视频 P1 - P19 从头至尾敲了下来,框架选用了semanticUI,其中集成了很多插件。如:markdown编辑器、代码高亮、二维码生成、滚动侦测、平滑滚动、生成目录、中文排版、animate过渡动画等,由于本人专注于后端,前端部分就不多做赘述了。
开发工具及依赖 | 版本 |
---|---|
JDK | 1.8 |
IDE | IntelliJ IDEA 2020.3.2 |
SpringBoot | 2.6.4 |
MySQL驱动 | 8.0+ |
<!--依赖-->
<dependencies>
<!--thymeleaf模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mybatis整合springboot-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- AOP 织入包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
spring:
thymeleaf:
cache: false
mode: HTML
profiles:
active: dev
server:
port: 8080
spring:
datasource:
username: root
password: root
url: https://jdbc:mysql://localhost:3306/blog?&useSSL=false&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
type-aliases-package: com.yy.blog.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
logging:
level:
root: info
com.yy: debug
file:
name: log/blog-dev.log
server:
port: 8081
spring:
datasource:
username: root
password: root
url: https://jdbc:mysql://localhost:3306/blog?&useSSL=false&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
type-aliases-package: com.yy.blog.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
logging:
level:
root: warn
com.yy: info
file:
name: log/blog-pro.log
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>404 Not Found</title>
<style>
body {
background: #000;
height: 100vh;
overflow: hidden;
display: flex;
font-family: 'Anton', sans-serif;
justify-content: center;
align-items: center;
-webkit-perspective: 1000px;
perspective: 1000px;
}
div {
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
}
.rail {
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
-webkit-transform: rotateX(-30deg) rotateY(-30deg);
transform: rotateX(-30deg) rotateY(-30deg);
}
.rail .stamp {
position: absolute;
width: 200px;
height: 200px;
display: flex;
justify-content: center;
align-items: center;
background: #141414;
color: #fff;
font-size: 7rem;
}
.rail .stamp:nth-child(1) {
-webkit-animation: stampSlide 40000ms -2300ms linear infinite;
animation: stampSlide 40000ms -2300ms linear infinite;
}
.rail .stamp:nth-child(2) {
-webkit-animation: stampSlide 40000ms -4300ms linear infinite;
animation: stampSlide 40000ms -4300ms linear infinite;
}
.rail .stamp:nth-child(3) {
-webkit-animation: stampSlide 40000ms -6300ms linear infinite;
animation: stampSlide 40000ms -6300ms linear infinite;
}
.rail .stamp:nth-child(4) {
-webkit-animation: stampSlide 40000ms -8300ms linear infinite;
animation: stampSlide 40000ms -8300ms linear infinite;
}
.rail .stamp:nth-child(5) {
-webkit-animation: stampSlide 40000ms -10300ms linear infinite;
animation: stampSlide 40000ms -10300ms linear infinite;
}
.rail .stamp:nth-child(6) {
-webkit-animation: stampSlide 40000ms -12300ms linear infinite;
animation: stampSlide 40000ms -12300ms linear infinite;
}
.rail .stamp:nth-child(7) {
-webkit-animation: stampSlide 40000ms -14300ms linear infinite;
animation: stampSlide 40000ms -14300ms linear infinite;
}
.rail .stamp:nth-child(8) {
-webkit-animation: stampSlide 40000ms -16300ms linear infinite;
animation: stampSlide 40000ms -16300ms linear infinite;
}
.rail .stamp:nth-child(9) {
-webkit-animation: stampSlide 40000ms -18300ms linear infinite;
animation: stampSlide 40000ms -18300ms linear infinite;
}
.rail .stamp:nth-child(10) {
-webkit-animation: stampSlide 40000ms -20300ms linear infinite;
animation: stampSlide 40000ms -20300ms linear infinite;
}
.rail .stamp:nth-child(11) {
-webkit-animation: stampSlide 40000ms -22300ms linear infinite;
animation: stampSlide 40000ms -22300ms linear infinite;
}
.rail .stamp:nth-child(12) {
-webkit-animation: stampSlide 40000ms -24300ms linear infinite;
animation: stampSlide 40000ms -24300ms linear infinite;
}
.rail .stamp:nth-child(13) {
-webkit-animation: stampSlide 40000ms -26300ms linear infinite;
animation: stampSlide 40000ms -26300ms linear infinite;
}
.rail .stamp:nth-child(14) {
-webkit-animation: stampSlide 40000ms -28300ms linear infinite;
animation: stampSlide 40000ms -28300ms linear infinite;
}
.rail .stamp:nth-child(15) {
-webkit-animation: stampSlide 40000ms -30300ms linear infinite;
animation: stampSlide 40000ms -30300ms linear infinite;
}
.rail .stamp:nth-child(16) {
-webkit-animation: stampSlide 40000ms -32300ms linear infinite;
animation: stampSlide 40000ms -32300ms linear infinite;
}
.rail .stamp:nth-child(17) {
-webkit-animation: stampSlide 40000ms -34300ms linear infinite;
animation: stampSlide 40000ms -34300ms linear infinite;
}
.rail .stamp:nth-child(18) {
-webkit-animation: stampSlide 40000ms -36300ms linear infinite;
animation: stampSlide 40000ms -36300ms linear infinite;
}
.rail .stamp:nth-child(19) {
-webkit-animation: stampSlide 40000ms -38300ms linear infinite;
animation: stampSlide 40000ms -38300ms linear infinite;
}
.rail .stamp:nth-child(20) {
-webkit-animation: stampSlide 40000ms -40300ms linear infinite;
animation: stampSlide 40000ms -40300ms linear infinite;
}
@-webkit-keyframes stampSlide {
0% {
-webkit-transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(130px);
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(130px);
}
100% {
-webkit-transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(-3870px);
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(-3870px);
}
}
@keyframes stampSlide {
0% {
-webkit-transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(130px);
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(130px);
}
100% {
-webkit-transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(-3870px);
transform: rotateX(90deg) rotateZ(-90deg) translateZ(-200px) translateY(-3870px);
}
}
.world {
-webkit-transform: rotateX(-30deg) rotateY(-30deg);
transform: rotateX(-30deg) rotateY(-30deg);
}
.world .forward {
position: absolute;
-webkit-animation: slide 2000ms linear infinite;
animation: slide 2000ms linear infinite;
}
.world .box {
width: 200px;
height: 200px;
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
-webkit-animation: roll 2000ms cubic-bezier(1, 0.01, 1, 1) infinite;
animation: roll 2000ms cubic-bezier(1, 0.01, 1, 1) infinite;
}
.world .box .wall {
position: absolute;
width: 200px;
height: 200px;
background: rgba(10, 10, 10, 0.8);
border: 1px solid #fafafa;
box-sizing: border-box;
}
.world .box .wall::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 7rem;
}
.world .box .wall:nth-child(1) {
-webkit-transform: translateZ(100px);
transform: translateZ(100px);
}
.world .box .wall:nth-child(2) {
-webkit-transform: rotateX(180deg) translateZ(100px);
transform: rotateX(180deg) translateZ(100px);
}
.world .box .wall:nth-child(3) {
-webkit-transform: rotateX(90deg) translateZ(100px);
transform: rotateX(90deg) translateZ(100px);
}
.world .box .wall:nth-child(3)::before {
-webkit-transform: rotateX(180deg) rotateZ(90deg) translateZ(-1px);
transform: rotateX(180deg) rotateZ(90deg) translateZ(-1px);
-webkit-animation: zeroFour 4000ms -2000ms linear infinite;
animation: zeroFour 4000ms -2000ms linear infinite;
}
.world .box .wall:nth-child(4) {
-webkit-transform: rotateX(-90deg) translateZ(100px);
transform: rotateX(-90deg) translateZ(100px);
}
.world .box .wall:nth-child(4)::before {
-webkit-transform: rotateX(180deg) rotateZ(-90deg) translateZ(-1px);
transform: rotateX(180deg) rotateZ(-90deg) translateZ(-1px);
-webkit-animation: zeroFour 4000ms -2000ms linear infinite;
animation: zeroFour 4000ms -2000ms linear infinite;
}
.world .box .wall:nth-child(5) {
-webkit-transform: rotateY(90deg) translateZ(100px);
transform: rotateY(90deg) translateZ(100px);
}
.world .box .wall:nth-child(5)::before {
-webkit-transform: rotateX(180deg) translateZ(-1px);
transform: rotateX(180deg) translateZ(-1px);
-webkit-animation: zeroFour 4000ms linear infinite;
animation: zeroFour 4000ms linear infinite;
}
.world .box .wall:nth-child(6) {
-webkit-transform: rotateY(-90deg) translateZ(100px);
transform: rotateY(-90deg) translateZ(100px);
}
.world .box .wall:nth-child(6)::before {
-webkit-transform: rotateX(180deg) rotateZ(180deg) translateZ(-1px);
transform: rotateX(180deg) rotateZ(180deg) translateZ(-1px);
-webkit-animation: zeroFour 4000ms linear infinite;
animation: zeroFour 4000ms linear infinite;
}
@-webkit-keyframes zeroFour {
0% {
content: '4';
}
100% {
content: '0';
}
}
@keyframes zeroFour {
0% {
content: '4';
}
100% {
content: '0';
}
}
@-webkit-keyframes roll {
0% {
-webkit-transform: rotateZ(0deg);
transform: rotateZ(0deg);
}
85% {
-webkit-transform: rotateZ(90deg);
transform: rotateZ(90deg);
}
87% {
-webkit-transform: rotateZ(88deg);
transform: rotateZ(88deg);
}
90% {
-webkit-transform: rotateZ(90deg);
transform: rotateZ(90deg);
}
100% {
-webkit-transform: rotateZ(90deg);
transform: rotateZ(90deg);
}
}
@keyframes roll {
0% {
-webkit-transform: rotateZ(0deg);
transform: rotateZ(0deg);
}
85% {
-webkit-transform: rotateZ(90deg);
transform: rotateZ(90deg);
}
87% {
-webkit-transform: rotateZ(88deg);
transform: rotateZ(88deg);
}
90% {
-webkit-transform: rotateZ(90deg);
transform: rotateZ(90deg);
}
100% {
-webkit-transform: rotateZ(90deg);
transform: rotateZ(90deg);
}
}
@-webkit-keyframes slide {
0% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
100% {
-webkit-transform: translateX(-200px);
transform: translateX(-200px);
}
}
@keyframes slide {
0% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
100% {
-webkit-transform: translateX(-200px);
transform: translateX(-200px);
}
}
</style>
</head>
<body>
<div class="rail">
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
<div class="stamp four">4</div>
<div class="stamp zero">0</div>
</div>
<div class="world">
<div class="forward">
<div class="box">
<div class="wall"></div>
<div class="wall"></div>
<div class="wall"></div>
<div class="wall"></div>
<div class="wall"></div>
<div class="wall"></div>
</div>
</div>
</div>
</body>
</html>
@ControllerAdvice :是Spring 3.2 提供的新注解,它是一个Controller增强器,可对controller中被 @RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理。
package com.yy.blog.interceptor;
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;
//会拦截所有带 @Controller 的控制器 做异常处理
@ControllerAdvice
public class ControllerExceptionInterceptor {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//标识这个方法可以做异常处理 , 拦截掉所有Exception
@ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(HttpServletRequest request,Exception e) throws Exception {
//记录日志信息
logger.error("Request URL : {},Exception : {}",request.getRequestURL(),e);
/*对标识了 @ResponseStatus() 的异常 单独做处理!
* 不会返回到error,而是交给springboot自己来处理,去404
* */
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;
}
}
在这个异常处理类中,当在请求controller时发生异常时,会自动调用exceptionHandler方法对异常做处理,处理方式为:创建一个ModelAndView ,携带获取请求的URL和Exception,设置视图名称跳转到error页面并返回。
其中对自定义的异常作了单独处理,当抛出的异常为这个自定义异常时,不会做此异常处理,而是正常抛出,让SpringBoot来处理。
if(AnnotationUtils.findAnnotation(e.getClass(),ResponseStatus.class) != null){
throw e;
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>错误</title>
</head>
<body>
<h1>错误</h1>
<!--在控制台打印异常信息-->
<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>
</body>
</html>
error页面会使用thymeleaf ,获取到异常处理后传过来的请求URL以及Exception并显示在浏览器源代码里
package com.yy.blog.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
//所有抛出该异常都状态标识为 NOT_FOUND
@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);
}
}
【当正常抛出异常时】
@Controller
public class IndexController {
@GetMapping("/")
public String index(){
int i = 1 / 0;
return "index";
}
}
测试结果:携带URL和Exception跳转到error页面,并将错误信息输出在浏览器源代码中
【当抛出自定义异常时】
@Controller
public class IndexController {
@GetMapping("/")
public String index(){
String blog = null;
//如果查询出来的博客为空,则抛出自定义异常!
if (blog == null){
throw new NotFoundException("博客不存在!");
}
return "index";
}
}
测试结果:当抛出自定义异常时,正常抛出,不做异常处理,跳转到 404
<!-- AOP 织入包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
package com.yy.blog.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;
@Aspect
@Component
public class LogAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
//定义切面,log()可以理解为切面的名称
@Pointcut("execution(* com.yy.blog.controller.*.*(..))")
public void log(){}
@Before("log()")
public void doBefore(JoinPoint joinPoint){
//获取请求的URL
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String url = request.getRequestURL().toString();
//获取请求的ip地址
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()-----------------");
}
@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 "\n请求的URL = '" + url + '\'' + '\n' +
"请求者的ip地址 = '" + ip + '\'' + '\n' +
"请求的方法 = '" + classMethod + '\'' + '\n' +
"请求的参数 = '" + Arrays.toString(args);
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。