SpringBoot - 個人博客 - 前期準備


開篇名義,在此感謝李仁密老師的《Spring Boot開發小而美的個人博客》課程,本系列文章是對做該項目過程中的一種記錄,也是個人學習的一些心得體會。希望通過本課程不僅可以學會搭建個性化的個人博客,也能將之前學習的東西整合使用,從而達到融會貫通的目的~


1. 功能概覽

小而美的個人博客所提供的功能大致如下圖所示:

個人博客腦圖

功能整體上分爲兩大部分:

  • 管理員
    • 登錄
    • 博客CRUD
    • 類別CRUD
    • 標籤CRUD
  • 前端展示
    • 博客首頁
    • 博客分類
    • 博客標籤
    • 博客歸檔
    • 導航欄、底部

2. 技術選型


3. 環境搭建

3.1 依賴導入

首先在IDEA中創建Spring Boot項目,並導入項目所需的依賴項,例如Thymleaf、mysql-connector、Jpa、DevTools、Aspects、Lomlok……完整的pom.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>dyliang</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>dyliang</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
        <thymeleaf-layout-dialect.version>2.1.1</thymeleaf-layout-dialect.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>2.3.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.3.6.Final</version>
        </dependency>
         <!--實現MakeDown轉HTML-->
        <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>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.2 配置文件

這裏配置文件採用yaml格式,當然properties格式的配置文件同樣可以。首先編寫全局的配置文件application.yaml

spring:
  thymeleaf:
    mode: HTML
  # 運行環境
  profiles:
    active: dev
    # 國際化
    messages:
      basename: i18n/messages

爲了適配於不同的應用環境,創建application-dev.yaml和application-pro.yaml分別用於開發環境和生產環境

########################
# application-dev.yaml
########################
spring:
  # 配置數據源
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/blog?serverTimezone=GMT&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456
  # Jpa相關配置項
  jpa:
    hibernate:
      # 根據實體類自動創建表
      ddl-auto: update
    # 自動打印sql語句
    show-sql: true

# 日誌配置
logging:
  # 日誌級別
  level:
    root: info
    com.lrm: debug
 # 日誌文件目錄
 file:
    name: log/blog-dev.log
########################
# application-pro.yaml
########################
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/blog?serverTimezone=GMT&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456
  jpa:
    hibernate:
      ddl-auto: none
    show-sql: true

logging:
  level:
    root: warn
    com.lrm: info
  file:
    name: log/blog-pro.log
server:
  port: 8081

同時也可以添加Spring Boot默認的日誌logback的相關配置,編寫配置文件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>

3.3 異常處理

首先,定義常用的錯誤頁面404、500和error,並編寫全局的異常處理類ControllerExceptionHandler:

@ControllerAdvice
public class ControllerExceptionHandler {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @ExceptionHandler(Exception.class)
    public ModelAndView ExceptionHandler(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;
    }
}

並定義資源找不到異常類NotFoundExcepiton

@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);
    }
}

3.4 日誌處理

這裏使用Spring Boot中的AOP進行日誌管理,編寫記錄日誌類LogAspect

@Aspect  // 表示該類作爲一個切面
@Component  // 將其加入到Ioc容器中
public class LogAspect {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    // @pointcut中指定要掃描的內容對應的AspectJ表達式
    @Pointcut("execution(* dyliang.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() {}

    @AfterReturning(returning = "result",pointcut = "log()")
    public void doAfterReturn(Object result) {
        logger.info("Result : {}", result);
    }

    @AllArgsConstructor
    @ToString
    private class RequestLog {
        private String url;
        private String ip;
        private String classMethod;
        private Object[] args;

    }
}

4. 實體類設計

這裏通過Jpa來使用面向對象的思想根據實體類自動的創建表,所以並不需要在數據庫中顯式的創建表,並設置表之間的映射關係。首先分析一下博客系統所設計的實體類:

  • 博客類:Blog
  • 類別類:Type
  • 標籤類:Tag
  • 評論類:Comment
  • 用戶類:User

它們之間的關係如下所示:

Blog類圖

除了不同實體類之間的關係,由於博客評論這裏設計爲兩級結構,因此父評論和下面的子評論還存在一對多的關係。


5. 命名約定

Service/DAO層命名約定:

  • 獲取單個對象的方法用get做前綴。
  • 獲取多個對象的方法用list做前綴。
  • 獲取統計值的方法用count做前綴。
  • 插入的方法用save(推薦)或insert做前綴。
  • 刪除的方法用remove(推薦)或delete做前綴。
  • 修改的方法用update做前綴。

6. 應用分層

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