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

博客地址:ONESTARの客棧

源碼領取方式一:

  • 掃一掃文末二維碼,關注公衆號,後臺回覆【博客】,即可領取源碼

源碼領取方式二:

歡迎給star以鼓勵(^_−)☆

本文將從構建SpringBoot框架異常處理日誌處理來講述個人博客系統後臺框架搭建,技術需求可以查看上一篇文章:【SpringBoot搭建個人博客】- 技術需求(二)

一、構建SpringBoot框架

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

使用idea新建項目,這裏使用的是jdk1.8,選擇相應組件,這裏選擇如下,點擊下一個,選擇保存路徑創建好項目(對於SpringBoot不是很清楚的夥伴可以看我之前的博客:SpringBoot 框架入門SpringBoot框架原理分析

創建好項目後,很根據剛選擇的組件自動添加依賴,當然,這裏只有我們需要的一部分,還有一些依賴等用到的時候再來添加

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

spring:
  thymeleaf:
    mode: HTML
  profiles:
    active: pro
    
mybatis:
  type-aliases-package: com.star.entity
  mapper-locations: classpath:mapper/*.xml
  configuration:
    map-underscore-to-camel-case: true

application-dev.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/myblog?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    username: root
    password: 111111

logging:
  level:
    root: info
    com.star: debug
  file: log/blog-dev.log

application-pro.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/myblog?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC&serverTimezone=GMT%2B8
    username: root
    password: 111111

logging:
  level:
    root: warn
    com.star: info
  file: log/blog-pro.log

在上面的配置中,可以看到,公共部分application.yml配置了thymeleaf模板、當前配置活躍文件、數據持久層的配置,開發環境和部署環境配置了數據庫(兩個環境的數據庫用戶名和密碼一般會不一樣),然後就是日誌文件的相關配置,可以在指定文件夾下生成日誌文件。

SpringBoot中有日誌默認的生成以及切分,在這裏我們可以重寫SpringBoot默認日誌配置,自定義日誌大小和名稱等等,在資源文件夾下添加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、重寫了默認配置,設置日誌文件大小在10MB時,按日期切分日誌
        -->

3.運行

由於本系列博客只講述博客後端的開發,前端不會細說,頁面直接拿來用,可以直接從GitHub中下載前端頁面(歡迎star):https://github.com/oneStarLR/myblog-page ,將前端頁面導入進項目中,注意目錄別放錯了

至此,基本框架搭建完成,可以運行一下了,可以看到在dev和pro配置下打印的log日誌以及在項目中自動生成一個log文件夾,存放dev和pro日誌文件。

dev日誌打印:

pro日誌打印:

開發的時候爲了能看到更多的信息,使用dev配置環境

二、異常處理

在頁面訪問的時候,會有一些比較常見的異常報錯信息,比如路徑無法訪問404異常、服務器錯誤500異常以及自己定義的錯誤頁面等等,SpringBoot框架提供了處理錯誤頁面的方法,在這裏,咱們對404、500、error異常頁面進行處理。

1.定義錯誤頁面

可以在前端頁面templates目錄下有error文件夾,SpringBoot可以通過文件夾的名稱和錯誤文件命名的方式找到異常頁面,所以文件名稱只能固定,有以下異常頁面:

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

可以通過控制器來測試一下,在com.star文件夾下面新建controller包,創建IndexController控制器作爲首頁控制器,代碼如下:

package com.star.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @Description: 首頁控制器
 * @Date: Created in 21:01 2020/5/20
 * @Author: ONESTAR
 * @QQ羣: 530311074
 * @URL: https://onestar.newstar.net.cn/
 */
@Controller
public class IndexController {
    //通過get方式請求路徑
    @GetMapping("/")
    public String index(){
        return "index";
    }
}

404頁面測試:可以在在瀏覽器輸入:http://localhost:8080/ ,可以訪問到博客的首頁,可以通過改變路徑,如加一個無效的後綴,訪問後發現跳轉到我自己寫的404頁面

500頁面測試:可以在IndexController控制器中加一句錯誤代碼,人爲的讓服務器出錯,如加一句:int a = 9/0; (分母不能爲零,這樣服務器就會出錯),然後訪問:http://localhost:8080/, 發現跳轉到了500頁面,這就說明沒有問題(記得把導致500的錯誤註釋掉)

2.全局異常處理

對於404和500錯誤頁面,SpringBoot可以根據頁面的命名方式找到對應的文件,而自定義的錯誤就需要我們自己來攔截了,讓代碼出現問題的時候跳轉到我們自己定義的錯誤頁面,這裏就需要自定義攔截器。

在com.star文件夾下面新建hander包,創建ControllerExceptionHandler錯誤頁面攔截器,通過定義這個類來攔截所有的異常,代碼如下:

package com.star.controller.hander;

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;

/**
 * @Description: 攔截異常處理
 * @Date: Created in 21:40 2020/5/20
 * @Author: ONESTAR
 * @QQ羣: 530311074
 * @URL: https://onestar.newstar.net.cn/
 */
@ControllerAdvice
public class ControllerExceptionHandler {

//    將異常記錄到日誌
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * @Description: 處理錯誤信息
     * @Auther: ONESTAR
     * @Date: 21:52 2020/5/20
     * @Param: request:訪問的異常URL
     * @Param: e:異常參數
     * @Return: 返回錯誤信息頁面
     */
    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHander(HttpServletRequest request, Exception e) throws Exception {

//        記錄異常信息:請求的URL,異常信息
        logger.error("Requst URL : {},Exception : {}", request.getRequestURL(),e);

//        當標識了狀態碼的時候就不攔截
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }

//        將記錄的異常信息返回到error頁面
        ModelAndView mv = new ModelAndView();
        mv.addObject("url",request.getRequestURL());
        mv.addObject("exception", e);
        mv.setViewName("error/error");
        return mv;
    }
}

分析:

  • @ControllerAdvice表示攔截掉所有帶有@Controller註解的控制器
  • @ExceptionHandler表明是異常處理方法
  • ModelAndView:返回一個頁面信息
  • 通過攔截異常信息,在日誌中記錄,並返回給error頁面
  • 標識了狀態碼的時候就不攔截,如資源找不到異常

3.資源找不到異常處理

對於資源找不到異常,一般也是要跳轉到404頁面的,這裏就需要自定義一個異常類,專門用來應對資源找不到,在com.star文件夾下面新建NotFoundException類。

package com.star;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * @Description: 自定義異常
 * @Author: ONESTAR
 * @Date: Created in 16:03 2020/3/25
 * @QQ羣: 530311074
 * @URL: https://onestar.newstar.net.cn/
 */
@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);
    }
}

分析:

  • 繼承RuntimeException,實現繼承RuntimeException的構造函數
  • @ResponseStatus(HttpStatus.NOT_FOUND)註解表示資源找不到的狀態碼,標識了狀態碼的時候就不攔截

三、日誌處理

採用SpringBoot中的AOP進行日誌處理,AOP可以以切面的形式攔截,將日誌內容記錄下來,這裏記錄以下日誌信息:

  • 訪問的URL
  • 訪問者的IP
  • 訪問時調用的方法
  • 訪問時傳遞的參數
  • 訪問時返回的內容

1.添加依賴

在pom.xml中添加AOP的依賴

<!--AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.切面處理

在com.star文件夾下面新建aspect包,創建LogAspect日誌切面處理類,在這裏對日誌進行處理,代碼如下:

package com.star.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: 日誌切面處理
 * @Date: Created in 23:31 2020/5/21
 * @Author: ONESTAR
 * @QQ羣: 530311074
 * @URL: https://onestar.newstar.net.cn/
 */
@Aspect
@Component
public class LogAspect {
    //獲取日誌信息
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    //定義切面,申明log()是一個切面
    @Pointcut("execution(* com.star.controller.*.*(..))")
    public void log() {}

    //在切面之前執行
    @Before("log()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //獲取URL、IP
        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--------");
    }

    //返回之後攔截
    @AfterReturning(returning = "result",pointcut = "log()")
    public void doAfterRuturn(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) +
                    '}';
        }
    }
}

分析:

  • @Aspect註解:AOP切面作用
  • @Component註解:開啓組件掃描,通過註解找到要掃描的對象
  • @Pointcut("execution(* com.star.controller..(..))"):定義切面,申明log()是一個切面,通過execution來表示需要攔截的類,這裏表示攔截控制器下面的所有類所有方法
  • RequestLog:將請求的參數封裝成一個內部類
  • 在訪問頁面(controller)之前,攔截請求的URL、IP、調用的方法、傳遞的參數、返回的內容,並記錄到日誌

運行項目,訪問http://localhost:8080/,可以在控制檯看到打印日誌信息,記錄了請求的URL、IP、調用的方法、傳遞的參數、返回的內容,並輸入到了日誌

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章