SpringBoot技術棧搭建個人博客【項目准備】
原型設計
事實上,我是直接先去找的原型,去參考了一下大概我需要做成什么樣子...
前端原型參考
在這里先給大家推薦一個設計網站吧,找素材啊之類的還挺方便的:
站酷:http://www.zcool.com.cn/
所以我在里面找到了我想要的前端原型,大概就像這個樣子:
項目搭建
先來介紹一下這次想要使用的一些技術:
- SpringBoot2.04 來編寫后台
- Vue 來寫頁面,准備拋棄一下JSP,雖然現在Vue還啥都不懂,學唄
- MyBatis 用於ORM,喜歡這玩意兒的逆向工程
- RESTful API / JSON 交互
Redis 可能還會使用這個來緩存一下md轉換之后的html源碼
SpringBoot 工程搭建
SpringBoot 項目搭建過程就不再贅述了,不熟悉的童鞋戳這邊:https://www.jianshu.com/p/70963ab49f8c,這里就簡單給一下配置信息:
后台肯定是需要加安全驗證的,要簡單點我可以搞一個攔截器來簡單弄弄,也可以用現有的安全框架,這里暫時就不加入這方面的東西了,把基本的弄進來就OK,然后它默認加入的東西不能夠支持我們的業務,所以還需要手動添加進一些包:
step 1 項目結構如下:

熱部署還是要的呀,然后再在【resrouces】下新建一個【banner.txt】文件,修改一下SpringBoot啟動的提示信息:
__ __ __
/\ \ __/\ \ /\ \ \ \ \/\ \ \ \ ___ ___ __ __ ____\ \ \/'\ __ _ ____ \ \ \ \ \ \ \ /' __` __`\ /\ \/\ \ /',__\\ \ , < /\ \/'\/\_ ,`\ \ \ \_/ \_\ \/\ \/\ \/\ \\ \ \_\ \ /\__, `\\ \ \\`\ \/> </\/_/ /_ \ `\___x___/\ \_\ \_\ \_\\/`____ \\/\____/ \ \_\ \_\/\_/\_\ /\____\ '\/__//__/ \/_/\/_/\/_/ `/___/> \\/___/ \/_/\/_/\//\/_/ \/____/ /\___/ \/__/
下面對這些目錄進行一些簡要的說明:
- controller:控制器
- dao:實際上這個包可以改名叫mapper,因為里面放的應該是MyBatis逆向工程自動生成之后的mapper類,還是叫dao吧,傳統...
- entity:實體類,還會有一些MyBatis生成的example
- generator:MyBatis逆向工程生成類
- interceptor:SpringBoot 攔截器
- service:Service層,里面還有一層impl目錄
- util:一些工具類可以放在里面
- mapper:用於存放MyBatis逆向工程生成的.xml映射文件
- static:這個目錄存放一些靜態文件,簡單了解了一下Vue的前后端分離,前台文件以后也需要放在這個目錄下面
Step2 然后我使用application.yml文件代替了application.yml,這個東西結構清晰一點兒,反正用哪個都無所謂,配置好就OK了:
################設置服務端口號##############################
server:
port: 8888
################### spring配置 ###################
spring:
profiles:
active: dev
http:
converters:
preferred-json-mapper: fastjson
devtools:
restart:
enabled: false #是否開啟開發者工具(true/false)
additional-paths: src/main/java
aop:
proxy-target-class: true #false為啟用jdk默認動態代理,true為cglib動態代理
---
spring:
profiles: dev
datasource:
url: jdbc:mysql://127.0.0.1:3306/net_car?characterEncoding=UTF-8
username: root
password: 1
driver-class-name: com.mysql.jdbc.Driver
#Druid連接池配置相關
druid:
# 初始大小,最大,最小
initial-size: 5
min-idle: 5
max-active: 200
# 配置獲取連接等待超時的時間
max-wait: 60000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閑連接,單位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一個連接在池中最小生存的時間,單位是毫秒
min-evictable-idle-time-millis: 300000
## 該配置節點為獨立的節點,有很多同學容易將這個配置放在spring的節點下,導致配置無法被識別
#mybatis:
# mapper-locations: classpath:/mapper/**/*Mapper.xml #注意:一定要對應mapper映射xml文件的所在路徑
# type-aliases-package: com.springbootblog.entity # 注意:對應實體類的路徑
#mybatis
mybatis-plus:
mapper-locations: classpath:/mapper/**/*Mapper.xml
#實體掃描,多個package用逗號或者分號分隔
typeAliasesPackage: com.springbootblog.entity
typeEnumsPackage: com.springbootblog.entity.enums
global-config:
# 數據庫相關配置
db-config:
#主鍵類型 AUTO:"數據庫ID自增", INPUT:"用戶輸入ID",ID_WORKER:"全局唯一ID (數字類型唯一ID)", UUID:"全局唯一ID UUID";
id-type: id_worker
#字段策略 IGNORED:"忽略判斷",NOT_NULL:"非 NULL 判斷"),NOT_EMPTY:"非空判斷"
field-strategy: not_empty
#駝峰下划線轉換
column-underline: true
#數據庫大寫下划線轉換
#capital-mode: true
#邏輯刪除配置
logic-delete-value: 0
logic-not-delete-value: 1
db-type: mysql
#刷新mapper 調試神器
refresh: true
# 原生配置
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
---
##########################################################
################### 開發環境的profile ###################
##########################################################
spring:
profiles: dev
logging:
level.root: info
path: logs/
file: springboot.log
level.com.springbootroot: info
logging.level.org.springframework.web: trace
logging.level.org.apache: trace
####################讀取配置文件信息#####################
jdbc.pwd: test
my-props: #自定義的屬性和值
simpleProp: simplePropValue
arrayProps: 1,2,3,4,5
listProp1:
- name: abc
value: abcValue
- name: efg
value: efgValue
listProp2:
- config2Value1
- config2Vavlue2
mapProps:
key1: value1
key2: value2
step 3 主要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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.springbootblog</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.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--版本配置參數start-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<swagger.version>2.9.2</swagger.version>
<mybatis-plus-boot-starter.version>3.0-RC1</mybatis-plus-boot-starter.version>
<druid.version>1.1.10</druid.version>
<mysql-connector-java.version>5.1.38</mysql-connector-java.version>
</properties>
<!--版本配置參數end-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 支持 @ConfigurationProperties 注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--MyBatis-->
<!--MyBatis逆向工程-->
<!-- 模板引擎 -->
<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity-engine-core -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<!-- 模板引擎,需要指定 mpg.setTemplateEngine(new FreemarkerTemplateEngine()); -->
<!--<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.9</version>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--SpringBoot測試支持-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--MySQL-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java.version}</version>
</dependency>
<!--mysql end-->
<!--<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>-->
<!-- druid阿里巴巴數據庫連接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--SpringBoot熱部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 這個需要為 true 熱部署才有效 -->
</dependency>
<!--Swagger2支持-->
<!--swagger start-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--swagger end-->
<!--mybatis-plus 集成 -->
<!-- mybatis-plus begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<!--<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<!-- mybatis-plus end -->
<!--<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>-->
<!--<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
MyBatis 逆向工程 Generator 工具類生成代碼
step4
package com.springbootblog.generator;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
/**
*代碼生成器
* @author zhangyh
* @date 2018/9/2 11:02
* @param
* @return
*
*/
public class MpGenerator {
/**
* 測試 run 執行
* <p>
* </p>
*/
@Test
public void generateCode() {
String packageName = "com.springbootblog";
boolean serviceNameStartWithI = false;//user -> UserService, 設置成true: user -> IUserService
generateByTables(serviceNameStartWithI, packageName, "l_sys_user");
}
private void generateByTables(boolean serviceNameStartWithI, String packageName, String... tableNames) {
GlobalConfig config = new GlobalConfig();
String dbUrl = "jdbc:mysql://127.0.0.1:3306/net_car?characterEncoding=utf8";
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setTypeConvert(new MySqlTypeConvert(){
// 自定義數據庫表字段類型轉換【可選】
@Override
public DbColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
String t = fieldType.toLowerCase();
//如果是datetime類型,轉換成Date字段類型
if(t.contains("datetime")){
return DbColumnType.DATE;
}
return super.processTypeConvert(globalConfig,fieldType);
}
});
dataSourceConfig.setDbType(DbType.MYSQL)
.setUrl(dbUrl)
.setUsername("root")
.setPassword("1")
.setDriverName("com.mysql.jdbc.Driver");
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig
.setCapitalMode(true)
.setEntityLombokModel(false)
.setDbColumnUnderline(true)
.setNaming(NamingStrategy.underline_to_camel)
.setInclude(tableNames);//修改替換成你需要的表名,多個表名傳數組
config.setActiveRecord(false)
.setAuthor("zhangyh@")
.setOutputDir("d:\\TestCodeGen")
.setFileOverride(true);
if (!serviceNameStartWithI) {
config.setServiceName("%sService");
}
new AutoGenerator().setGlobalConfig(config)
.setDataSource(dataSourceConfig)
.setStrategy(strategyConfig)
.setPackageInfo(
new PackageConfig()
.setParent(packageName)
.setController("controller")
.setEntity("entity")
).execute();
}
private void generateByTables(String packageName, String... tableNames) {
generateByTables(true, packageName, tableNames);
}
}
RESTful API 設計
為了實現前后端分離,好的RESTful API是離不開的,正好前一段時間學習了這方面的知識,所以決定先來設計一套RESTful API,之前學習的文章鏈接在這里:https://www.jianshu.com/p/91600da4df95
1)引入Swagger2來構造RESTful API:
既然想弄一下前后端分離,那就徹底一點兒,寫后台完全不管前台,前后台的交互靠一套RESTful API和JSON數據來弄,所以需要一個文檔來瞅瞅,首先在pox.xml添加相關依賴:
<!--Swagger2支持--> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.2.2</version> </dependency>
2)創建Swagger2配置類:
在SpringBoot啟動類的同級目錄下創建Swagger2的配置類【Swagger2】:
/** * Swagger2 配置類 * * @author:wmyskxz * @create:2018-06-14-上午 10:40 */ @Configuration @EnableSwagger2 public class Swagger2 { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("cn.wmyskxz.blog")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("zhangyh個人博客RESTful APIs") .description("原文地址鏈接:http://blog.didispace.com/springbootswagger2/") .termsOfServiceUrl("http://blog.didispace.com/") .contact("@zhangyh") .version("1.0") .build(); } }
這樣,就可以在我們啟動項目之后,訪問http://localhost:8888/swagger-ui.html
地址來查看當前項目中的RESTful風格的API:
package com.springbootblog.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.api.ApiController;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.springbootblog.entity.LSysUser;
import com.springbootblog.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*@title : JavaClass
*@author:zyh
*@createDate:2018/8/31 22:31
*
**/
@Api(value = "RESTful API ", description = "RESTful API ")
@RestController
@RequestMapping(value = "/api")
public class UserController extends ApiController {
@Autowired
UserService userService;
@ApiOperation(value = "RESTful API info", produces = "application/json")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "用戶名", required = false,
dataType = "String", defaultValue = "zyh")
})
@RequestMapping(value = "userTest", method = RequestMethod.POST)
@ResponseBody
public Object userTest(String name) {
LSysUser lSysUser = userService.selectByPrimaryKey(1L);
return success(lSysUser);
}
/**
*執行 userList 分頁查詢操作
* @author zhangyh
* @date 2018/9/1 21:57
* @param
* @return
*
*/
@ApiOperation(value = "用戶分頁列表", produces = "application/json")
@ApiImplicitParams({
@ApiImplicitParam(name = "pageNum", value = "頁碼", required = false,
dataType = "long", defaultValue = "1"),
@ApiImplicitParam(name = "pageSize", value = "頁面大小", required = false,
dataType = "long", defaultValue = "10")
})
@PostMapping(value = "/userList")
public Object findUserPageList(
HttpServletRequest request
, HttpServletResponse response
, Long pageNum
, Long pageSize) {
System.out.println("");
logger.info("");
Page page = new Page(pageNum, pageSize);
Page<LSysUser> pages = new Page<LSysUser>();
pages = userService.findUserPageList(page);
IPage<LSysUser> userListPage = userService.page(new Page<LSysUser>(1, 5), new QueryWrapper<LSysUser>());
System.err.println("total=" + userListPage.getTotal() + ", current list size=" + userListPage.getRecords().size());
userListPage = userService.page(new Page<LSysUser>(1, 5), new QueryWrapper<LSysUser>().orderByDesc("name"));
System.err.println("total=" + userListPage.getTotal() + ", current list size=" + userListPage.getRecords().size());
return success(userListPage);
}
/**
*
* @author zhangyh
* @date 2018/9/1 23:05
* @param []
* @return java.lang.Object
*
*/
@ApiOperation(value = "用戶信息保存", produces = "application/json")
@PostMapping(value = "/saveSysUser")
public Object ssaveSysUser() {
LSysUser lSysUser = new LSysUser();
lSysUser = userService.selectByPrimaryKey(1L);
lSysUser.setUserId(null);
userService.saveSysUser(lSysUser);
return success(lSysUser);
}
}
簡單介紹一下這些Swagger2的注解吧:
- @ApiOperation:用於給API設置提示信息,就上圖中右邊顯示的那些,默認不寫的情況下是value屬性,還可以多寫一個notes屬性,用於詳細的描述API,這里就不需要了,都還比較簡單;
- @ApiImplicaitParam:用於說明API的參數信息,加了s的注解同理,寫了這個之后呢,我們就可以利用Swagger2給我們的信息頁面進行測試了,當然這里沒有具體實現,也可以來看一下(下圖);
這里沒有具體實現所以就不足以完成測試,等到后台編寫的時候再進行測試吧...
總結
至此呢,我們項目所需要的准備就差不多完成了,想要去做一個東西必須要清楚的知道要的是一個什么東西,這樣才能更加好的完成我們的產品,這也是我喜歡和堅信的事情:方向永遠比努力重要!(強行有聯系..hhhh)
另外一個問題: 我在想文章信息和內容分成了兩個表的問題,這樣的設計我覺得是沒有問題的,但是作為前端並不關心這些數據庫的設計,他只要能拿到對象就可以了,在設計 API 的時候,就發現獲得一篇文章,需要從三個表(文章信息/文章內容/評論)去獲取信息並封裝返回前端,這就需要自己在后台另外寫一個實體類去封裝這些信息,這無疑增加了我們的代碼工作量,有沒有什么好的方法解決呢?