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 文件
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
com.springbootblog
blog
0.0.1-SNAPSHOT
jar
blog
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
2.0.4.RELEASE
UTF-8
UTF-8
1.8
2.9.2
3.0-RC1
1.1.10
5.1.38
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-cache
org.springframework.boot
spring-boot-starter-validation
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-configuration-processor
true
org.apache.velocity
velocity-engine-core
2.0
org.springframework.boot
spring-boot-starter-freemarker
org.springframework.boot
spring-boot-starter-test
test
mysql
mysql-connector-java
${mysql-connector-java.version}
com.alibaba
druid
${druid.version}
org.springframework.boot
spring-boot-devtools
true
io.springfox
springfox-swagger2
${swagger.version}
io.springfox
springfox-swagger-ui
${swagger.version}
com.baomidou
mybatis-plus-boot-starter
${mybatis-plus-boot-starter.version}
com.baomidou
mybatis-plus-boot-starter
${mybatis-plus-boot-starter.version}
junit
junit
4.12
org.springframework
spring-test
5.0.8.RELEASE
org.springframework.boot
spring-boot-test
org.springframework.boot
spring-boot-maven-plugin
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 执行
*
*
*/
@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添加相关依赖:
<dependency>
<groupId>io.springfoxgroupId> <artifactId>springfox-swagger2artifactId> <version>2.2.2version> dependency> <dependency> <groupId>io.springfoxgroupId> <artifactId>springfox-swagger-uiartifactId> <version>2.2.2version> 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 pages = new Page();
pages = userService.findUserPageList(page);
IPage userListPage = userService.page(new Page(1, 5), new QueryWrapper());
System.err.println("total=" + userListPage.getTotal() + ", current list size=" + userListPage.getRecords().size());
userListPage = userService.page(new Page(1, 5), new QueryWrapper().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 的时候,就发现获得一篇文章,需要从三个表(文章信息/文章内容/评论)去获取信息并封装返回前端,这就需要自己在后台另外写一个实体类去封装这些信息,这无疑增加了我们的代码工作量,有没有什么好的方法解决呢?