【SpringBoot搭建个人博客】- 框架搭建(三)

以你之姓@ 2021-09-21 07:48 598阅读 0赞

博客地址:ONESTARの客栈

源码领取方式一:

  • 扫一扫文末二维码,关注公众号,后台回复【博客】,即可领取源码

源码领取方式二:

  • 前端页面源码地址:https://github.com/oneStarLR/myblog-page
  • 以jpa为持久层源码地址:https://github.com/oneStarLR/myblog-jpa
  • 以mybatis为持久层源码地址:https://github.com/oneStarLR/myblog-mybaits

欢迎给star以鼓励(^_−)☆

本文将从构建SpringBoot框架异常处理日志处理来讲述个人博客系统后台框架搭建,技术需求可以查看上一篇文章:【SpringBoot搭建个人博客】- 技术需求(二)

一、构建SpringBoot框架

1.使用idea快速搭建SpringBoot框架

使用idea新建项目,这里使用的是jdk1.8,选择相应组件,这里选择如下,点击下一个,选择保存路径创建好项目(对于SpringBoot不是很清楚的伙伴可以看我之前的博客:SpringBoot 框架入门、SpringBoot框架原理分析)

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L09uZV9MX1N0YXI_size_16_color_FFFFFF_t_70

创建好项目后,很根据刚选择的组件自动添加依赖,当然,这里只有我们需要的一部分,还有一些依赖等用到的时候再来添加

2.配置yml文件

创建好SpringBoot项目后,框架自带的是properties文件,这里使用yml文件进行配置,所以将application.properties改为application.yml文件,并进行相关配置。 我们在开发项目的时候,一般开发环境和部署环境会不一样,为了加以区分,可以在yml配置文件中体现出来,所以分为application-dev.yml(开发环境)、application-pro.yml(部署环境),而为了能够让SpringBoot知道用的是哪个配置文件,需要在application.yml配置文件中加以说明,并且开发和部署中相同的配置也可以在application.yml中进行配置,详细配置如下:

  • application.yml:公共配置和表明当前配置文件
  • application-dev.yml:开发环境配置文件
  • application-pro.yml:部署环境配置文件

application.yml

  1. spring:
  2. thymeleaf:
  3. mode: HTML
  4. profiles:
  5. active: pro
  6. mybatis:
  7. type-aliases-package: com.star.entity
  8. mapper-locations: classpath:mapper/*.xml
  9. configuration:
  10. map-underscore-to-camel-case: true

application-dev.yml

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.cj.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/myblog?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
  5. username: root
  6. password: 111111
  7. logging:
  8. level:
  9. root: info
  10. com.star: debug
  11. file: log/blog-dev.log

application-pro.yml

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.cj.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/myblog?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC&serverTimezone=GMT%2B8
  5. username: root
  6. password: 111111
  7. logging:
  8. level:
  9. root: warn
  10. com.star: info
  11. file: log/blog-pro.log

在上面的配置中,可以看到,公共部分application.yml配置了thymeleaf模板、当前配置活跃文件、数据持久层的配置,开发环境和部署环境配置了数据库(两个环境的数据库用户名和密码一般会不一样),然后就是日志文件的相关配置,可以在指定文件夹下生成日志文件。

SpringBoot中有日志默认的生成以及切分,在这里我们可以重写SpringBoot默认日志配置,自定义日志大小和名称等等,在资源文件夹下添加logback-spring.xml进行配置,配置如下:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <configuration>
  3. <!--包含Spring boot对logback日志的默认配置-->
  4. <include resource="org/springframework/boot/logging/logback/defaults.xml" />
  5. <property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
  6. <include resource="org/springframework/boot/logging/logback/console-appender.xml" />
  7. <!--重写了Spring Boot框架 org/springframework/boot/logging/logback/file-appender.xml 配置-->
  8. <appender name="TIME_FILE"
  9. class="ch.qos.logback.core.rolling.RollingFileAppender">
  10. <encoder>
  11. <pattern>${FILE_LOG_PATTERN}</pattern>
  12. </encoder>
  13. <file>${LOG_FILE}</file>
  14. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  15. <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i</fileNamePattern>
  16. <!--保留历史日志一个月的时间-->
  17. <maxHistory>30</maxHistory>
  18. <!--
  19. Spring Boot默认情况下,日志文件10M时,会切分日志文件,这样设置日志文件会在100M时切分日志
  20. -->
  21. <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
  22. <maxFileSize>10MB</maxFileSize>
  23. </timeBasedFileNamingAndTriggeringPolicy>
  24. </rollingPolicy>
  25. </appender>
  26. <root level="INFO">
  27. <appender-ref ref="CONSOLE" />
  28. <appender-ref ref="TIME_FILE" />
  29. </root>
  30. </configuration>
  31. <!--
  32. 1、继承Spring boot logback设置(可以在appliaction.yml或者application.properties设置logging.*属性)
  33. 2、重写了默认配置,设置日志文件大小在10MB时,按日期切分日志
  34. -->

3.运行

由于本系列博客只讲述博客后端的开发,前端不会细说,页面直接拿来用,可以直接从GitHub中下载前端页面(欢迎star):https://github.com/oneStarLR/myblog-page ,将前端页面导入进项目中,注意目录别放错了

至此,基本框架搭建完成,可以运行一下了,可以看到在dev和pro配置下打印的log日志以及在项目中自动生成一个log文件夹,存放dev和pro日志文件。

dev日志打印:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L09uZV9MX1N0YXI_size_16_color_FFFFFF_t_70 1

pro日志打印:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L09uZV9MX1N0YXI_size_16_color_FFFFFF_t_70 2

开发的时候为了能看到更多的信息,使用dev配置环境

二、异常处理

在页面访问的时候,会有一些比较常见的异常报错信息,比如路径无法访问404异常、服务器错误500异常以及自己定义的错误页面等等,SpringBoot框架提供了处理错误页面的方法,在这里,咱们对404、500、error异常页面进行处理。

1.定义错误页面

可以在前端页面templates目录下有error文件夹,SpringBoot可以通过文件夹的名称和错误文件命名的方式找到异常页面,所以文件名称只能固定,有以下异常页面:

  • 404.html
  • 500.html
  • error.html

可以通过控制器来测试一下,在com.star文件夹下面新建controller包,创建IndexController控制器作为首页控制器,代码如下:

  1. package com.star.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. /**
  5. * @Description: 首页控制器
  6. * @Date: Created in 21:01 2020/5/20
  7. * @Author: ONESTAR
  8. * @QQ群: 530311074
  9. * @URL: https://onestar.newstar.net.cn/
  10. */
  11. @Controller
  12. public class IndexController {
  13. //通过get方式请求路径
  14. @GetMapping("/")
  15. public String index(){
  16. return "index";
  17. }
  18. }

404页面测试:可以在在浏览器输入:http://localhost:8080/ ,可以访问到博客的首页,可以通过改变路径,如加一个无效的后缀,访问后发现跳转到我自己写的404页面

500页面测试:可以在IndexController控制器中加一句错误代码,人为的让服务器出错,如加一句:int a = 9/0; (分母不能为零,这样服务器就会出错),然后访问:http://localhost:8080/, 发现跳转到了500页面,这就说明没有问题(记得把导致500的错误注释掉)

2.全局异常处理

对于404和500错误页面,SpringBoot可以根据页面的命名方式找到对应的文件,而自定义的错误就需要我们自己来拦截了,让代码出现问题的时候跳转到我们自己定义的错误页面,这里就需要自定义拦截器。

在com.star文件夹下面新建hander包,创建ControllerExceptionHandler错误页面拦截器,通过定义这个类来拦截所有的异常,代码如下:

  1. package com.star.controller.hander;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.core.annotation.AnnotationUtils;
  5. import org.springframework.web.bind.annotation.ControllerAdvice;
  6. import org.springframework.web.bind.annotation.ExceptionHandler;
  7. import org.springframework.web.bind.annotation.ResponseStatus;
  8. import org.springframework.web.servlet.ModelAndView;
  9. import javax.servlet.http.HttpServletRequest;
  10. /**
  11. * @Description: 拦截异常处理
  12. * @Date: Created in 21:40 2020/5/20
  13. * @Author: ONESTAR
  14. * @QQ群: 530311074
  15. * @URL: https://onestar.newstar.net.cn/
  16. */
  17. @ControllerAdvice
  18. public class ControllerExceptionHandler {
  19. // 将异常记录到日志
  20. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  21. /**
  22. * @Description: 处理错误信息
  23. * @Auther: ONESTAR
  24. * @Date: 21:52 2020/5/20
  25. * @Param: request:访问的异常URL
  26. * @Param: e:异常参数
  27. * @Return: 返回错误信息页面
  28. */
  29. @ExceptionHandler(Exception.class)
  30. public ModelAndView exceptionHander(HttpServletRequest request, Exception e) throws Exception {
  31. // 记录异常信息:请求的URL,异常信息
  32. logger.error("Requst URL : {},Exception : {}", request.getRequestURL(),e);
  33. // 当标识了状态码的时候就不拦截
  34. if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
  35. throw e;
  36. }
  37. // 将记录的异常信息返回到error页面
  38. ModelAndView mv = new ModelAndView();
  39. mv.addObject("url",request.getRequestURL());
  40. mv.addObject("exception", e);
  41. mv.setViewName("error/error");
  42. return mv;
  43. }
  44. }

分析:

  • @ControllerAdvice表示拦截掉所有带有@Controller注解的控制器
  • @ExceptionHandler表明是异常处理方法
  • ModelAndView:返回一个页面信息
  • 通过拦截异常信息,在日志中记录,并返回给error页面
  • 标识了状态码的时候就不拦截,如资源找不到异常

3.资源找不到异常处理

对于资源找不到异常,一般也是要跳转到404页面的,这里就需要自定义一个异常类,专门用来应对资源找不到,在com.star文件夹下面新建NotFoundException类。

  1. package com.star;
  2. import org.springframework.http.HttpStatus;
  3. import org.springframework.web.bind.annotation.ResponseStatus;
  4. /**
  5. * @Description: 自定义异常
  6. * @Author: ONESTAR
  7. * @Date: Created in 16:03 2020/3/25
  8. * @QQ群: 530311074
  9. * @URL: https://onestar.newstar.net.cn/
  10. */
  11. @ResponseStatus(HttpStatus.NOT_FOUND)
  12. public class NotFoundException extends RuntimeException{
  13. public NotFoundException() {
  14. }
  15. public NotFoundException(String message) {
  16. super(message);
  17. }
  18. public NotFoundException(String message, Throwable cause) {
  19. super(message, cause);
  20. }
  21. }

分析:

  • 继承RuntimeException,实现继承RuntimeException的构造函数
  • @ResponseStatus(HttpStatus.NOT_FOUND)注解表示资源找不到的状态码,标识了状态码的时候就不拦截

三、日志处理

采用SpringBoot中的AOP进行日志处理,AOP可以以切面的形式拦截,将日志内容记录下来,这里记录以下日志信息:

  • 访问的URL
  • 访问者的IP
  • 访问时调用的方法
  • 访问时传递的参数
  • 访问时返回的内容

1.添加依赖

在pom.xml中添加AOP的依赖

  1. <!--AOP-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-aop</artifactId>
  5. </dependency>

2.切面处理

在com.star文件夹下面新建aspect包,创建LogAspect日志切面处理类,在这里对日志进行处理,代码如下:

  1. package com.star.aspect;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.annotation.*;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.stereotype.Component;
  7. import org.springframework.web.context.request.RequestContextHolder;
  8. import org.springframework.web.context.request.ServletRequestAttributes;
  9. import javax.servlet.http.HttpServletRequest;
  10. import java.util.Arrays;
  11. /**
  12. * @Description: 日志切面处理
  13. * @Date: Created in 23:31 2020/5/21
  14. * @Author: ONESTAR
  15. * @QQ群: 530311074
  16. * @URL: https://onestar.newstar.net.cn/
  17. */
  18. @Aspect
  19. @Component
  20. public class LogAspect {
  21. //获取日志信息
  22. private final Logger logger = LoggerFactory.getLogger(this.getClass());
  23. //定义切面,申明log()是一个切面
  24. @Pointcut("execution(* com.star.controller.*.*(..))")
  25. public void log() {}
  26. //在切面之前执行
  27. @Before("log()")
  28. public void doBefore(JoinPoint joinPoint) {
  29. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  30. HttpServletRequest request = attributes.getRequest();
  31. //获取URL、IP
  32. String url = request.getRequestURL().toString();
  33. String ip = request.getRemoteAddr();
  34. //获取请求方法
  35. String classMethod = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
  36. //获取请求参数
  37. Object[] args = joinPoint.getArgs();
  38. RequestLog requestLog = new RequestLog(url, ip, classMethod, args);
  39. logger.info("Request : {}", requestLog);
  40. }
  41. //在切面之后执行
  42. @After("log()")
  43. public void doAfter() {
  44. // logger.info("--------doAfter--------");
  45. }
  46. //返回之后拦截
  47. @AfterReturning(returning = "result",pointcut = "log()")
  48. public void doAfterRuturn(Object result) {
  49. logger.info("Result : {}", result);
  50. }
  51. // 封装请求参数
  52. private class RequestLog {
  53. private String url;
  54. private String ip;
  55. private String classMethod;
  56. private Object[] args;
  57. public RequestLog(String url, String ip, String classMethod, Object[] args) {
  58. this.url = url;
  59. this.ip = ip;
  60. this.classMethod = classMethod;
  61. this.args = args;
  62. }
  63. @Override
  64. public String toString() {
  65. return "{" +
  66. "url='" + url + '\'' +
  67. ", ip='" + ip + '\'' +
  68. ", classMethod='" + classMethod + '\'' +
  69. ", args=" + Arrays.toString(args) +
  70. '}';
  71. }
  72. }
  73. }

分析:

  • @Aspect注解:AOP切面作用
  • @Component注解:开启组件扫描,通过注解找到要扫描的对象
  • @Pointcut(“execution(* com.star.controller..(..))”):定义切面,申明log()是一个切面,通过execution来表示需要拦截的类,这里表示拦截控制器下面的所有类所有方法
  • RequestLog:将请求的参数封装成一个内部类
  • 在访问页面(controller)之前,拦截请求的URL、IP、调用的方法、传递的参数、返回的内容,并记录到日志

运行项目,访问http://localhost:8080/,可以在控制台看到打印日志信息,记录了请求的URL、IP、调用的方法、传递的参数、返回的内容,并输入到了日志

20200522170054306.jpeg

至此,配合前端页面,框架就搭建完成


watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L09uZV9MX1N0YXI_size_16_color_FFFFFF_t_70 3

发表评论

表情:
评论列表 (有 0 条评论,598人围观)

还没有评论,来说两句吧...

相关阅读