您的位置:首页 > 编程语言 > Java开发

使用Spring Boot搭建个人博客全记录

2017-08-05 18:57 633 查看

使用Spring Boot搭建个人博客

简介

后端使用Spring Boot搭建的一个博客系统,前端使用的是thymeleaf + bootstrap,集成了editormd的markdown编辑器。

本项目适合Spring初学者作为练手项目,包含的主要技术点:

从开始到项目完成的全过程,篇幅可能较长

spring data jpa的基本使用

数据绑定

拦截器

spring boot 注解配置

地址

项目的思路以及前端代码来自他的博客:

一个JavaWeb搭建的开源Blog系统,整合SSM框架

项目GitHub地址:

https://github.com/wchstrife/blog

功能展示

把工程导入本地后,在mysql中添加一个叫做blog的database,然后在配置文件中把数据库的账号密码地址修改成自己的即可运行

主界面



详情



登录



后台管理



写博客



下面我们的正式开始

一、项目搭建

我使用的是IDEA使用Maven构建一个Spring Boot工程,搭建过程请自行百度

建好工程之后手动添加一些目录,下面展示一下详细的目录

项目目录



这里主要介绍一下resources目录

static目录是spring boot 默认的一个扫描的路径,所以我们要把引用的资源放在这里。

bootstrap是我们引进的一个样式控制的组件

editormd是引入的支持MarkDown语法的编辑器

css是一些全局的样式控制

jquery是bootstrap必要的

templates目录下放的是前端的HTML页面

admin是后台的管理

common是所有页面公用的部分

front是前台的展示界面

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.wchstrife</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>demo</name>
<description>Demo project for Spring Boot</description>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>1.5.4.RELEASE</version>
</dependency>

<!--不严格检查-->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>

<!--热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional><!-- optional=true,依赖不会传递,该项目依赖devtools;之后依赖myboot项目的项目如果想要使用devtools,需要重新引入 -->
</dependency>

<!--markdown-->
<dependency>
<groupId>org.tautua.markdownpapers</groupId>
<artifactId>markdownpapers-core</artifactId>
<version>1.4.1</version>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>


实体映射

实体的映射是在entity包下

下面我们使用Spring data jpa 对我们项目需要的实体进行数据库的映射

通过这种方式,我们可以避免直接操作数据库,而是通过Spring data jpa来进行增删改查的操作

这里我们一共用到三张表:Aritcle Category User 分别对应博客,分类,用户

主键生成策略:

这里我采用的是UUID的生成方式,会生成一串字符串作为主键

@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid")
@Column(name = "id", columnDefinition = "varchar(64) binary")
private String id;


外键约束

在Article表中,我们使用了ManyToOne的外键约束

在Article中有一个Categoriy的引用,代表一个博客有对应的一个分类,而一个分类下应该有多个博客

@ManyToOne
private Category category;


数据访问层dao

在dao包下封装了一系列的对数据库增删改查的操作

Spring data jpa强大之处就是可以根据方法名进行解析。所以在dao层下的接口,大部分只有方法名和接收的参数。

如果需要自定义sql语句只需要加注解即可

这里演示一个自定义的模糊查询

@Query("from Article where title like %:title%")
public List<Article> findByTitleLike(@Param("title") String title);


controller和service

划分这两层体现了很重要的分层的思想,即每一层只针对一层提供方法,不去了解其他层的方法,这样方便维护。

所以为了体现这种分层的思想,所以针对数据库的操作都放在service层进行封装

在controller层主要负责url地址的分配,对外提供的接口,部分简单的逻辑写在这里

二、front前台展示

在前端我们使用了Thymeleaf前端模板,里面有很多类似JSP标签的写法,进行对数据的遍历操作

在后端Control里面返回页面的时候,使用Model向其中添加属性,在前端页面上可以通过
${}
来获取这个属性值,并且用
th:each
来遍历

注意带th的语法表示交给thyme leaf模板解析的语法

例如前台index界面:

controller返回所有的博客列表

@RequestMapping("")
public String list(Model model){
List<Article> articles = articleService.list();
model.addAttribute("articles", articles);

return "front/index";
}


在前台使用ty的语法进行遍历显示

<div th:each="articles:${articles}">
<h3 th:text="${articles.title}"></h3>
<span class="summary" th:text="${articles.summary}"></span><br/><br/>
</div>


所以在前台展示只有三个页面,分别是列表显示所有博客,按类型显示博客,某个博客的全文

所以对应在controller里面只需要从数据库筛选全部的博客、某个类型的博客、取出某个博客(通过ID)的全文在页面上展示即可

管理员界面

在管理员界面要实现的功能比较多,最重要的是对博客的增删改

同时这里有一个登录的功能,只有在User表中有对应的账号密码才能登录,所以这里需要一层登陆拦截,这个稍后介绍。

引入Markdown组件

在编辑博客的时候我们支持使用markdown语法,我在网上找了一款叫做editormd的开源项目,放到static目录下

在我们write.html用如下的语法引入编辑器

<script th:src="@{/jquery-3.2.1.min.js}"></script>
<script th:src="@{/editormd/editormd.js}"></script>
<script th:src="@{/bootstrap/js/bootstrap.js}"></script>

<script type="text/javascript" th:inline="javascript">
//    调用编辑器
var testEditor;
$(function() {
testEditor = editormd("test-editormd", {
width   : "1000px",
height  : 640,
syncScrolling : "single",
path    : [[@{/editormd/lib/}]]
});
});
</script>

<script th:inline="javascript">
function selectCategory(obj) {
var name = $(obj).attr("name");
var displayName = $(obj).attr("abbr");
console.log(name + "   " + displayName);
$("#categoryBtn").html(displayName);
$("#cateoryInput").val(name);
}
</script>


在需要编辑器的地方输入一个textarea即可

<div id="test-editormd">
<textarea style="display:none;" name="content" th:field="*{content}" th:text="${target.content}"></textarea>
</div>


深坑:

如果在js中要使用thymeleaf的语法,比如@{} ${}这种语法,一定要加上这句话
th:inline="javascript


这样来使用该值
[[@{/editormd/lib/}]]


write 操作

在写文章按钮上绑定好要提交的action,在controller里面对这个action进行处理,这里我们重点是要返回一个new出来的Article对象,因为要对对象进行数据的绑定,所以如果不传这个参数的话会报错

@RequestMapping("/write")
public String write(Model model){
List<Category> categories = categoryService.list();
model.addAttribute("categories", categories);
model.addAttribute("article", new Article());

return "admin/write";
}


在write.html中引入相关的编辑器组件以后,通过th:object绑定到Article对象上,然后Spring Boot会自动的帮我们把表单中的数据组合成一个ArtIicle对象,是不是很方便

<form method="post" th:action="@{/admin/save}" th:object="${article}">
<input name="category" id="cateoryInput" type="hidden" th:field="*{category.name}"/>
<input type="text" class="form-contorl" palceholder="标题" name="title" th:field="*{title}"/>
<textarea style="display:none;" name="content" th:field="*{content}"></textarea>
</form>


save操作

前面html的表达提交之后,提交到save这个action中,在这里我们对提交的数据进行一个简单的处理,然后调用service里面封装的dao层的save方法即可

这里主要是对博客的日期,简介进行一个处理

@RequestMapping(value = "/save", method = RequestMethod.POST)
public String save(Article article){
//设置种类
String name = article.getCategory().getName();
Category category = categoryService.fingdByName(name);
article.setCategory(category);
//设置摘要,取前40个字
if(article.getContent().length() > 40){
article.setSummary(article.getContent().substring(0, 40));
}else {
article.setSummary(article.getContent().substring(0, article.getContent().length()));
}
article.setDate(sdf.format(new Date()));
articleService.save(article);

return "redirect:/admin";
}


update操作

更新博客其实和写博客是一个道理,不过在更新的时候需要把id传给controller,然后根据id找到这个文章

把这个博客的内容、标题渲染在update.html中

然后在表单提交的字段中加一个隐藏表单,把博客的id传进去

调用save方法即可完成更新(根据id进行save,所以这时候会执行更新操作)

@RequestMapping("/update/{id}")
public String update(@PathVariable("id") String id, Model model){
Article article = articleService.getById(id);
model.addAttribute("target", article);
List<Category> categories = categoryService.list();
model.addAttribute("categories", categories);
model.addAttribute("article", new Article());

return "admin/update";
}


登陆拦截

对于后台的增删改操作,我们只对管理员开放,虽然我们增加了一个登录界面,但是别人还是可以通过直接输入对应url进行访问,所以我们要在这里增加一层登陆拦截,让没有登录的人不允许访问到我们后天的界面

在登录处理登录的doLogin方法中,我们在登录成功之后在cookie中加一个标志

Cookie cookie = new Cookie(WebSecurityConfig.SESSION_KEY, user.toString());
response.addCookie(cookie);


在aspect包中建立一个拦截器

在WebSecurityConfig类中继承WebMvcConfigurerAdapter

重写
addInterceptors
方法,在里面配置要拦截的路径

在里面建一个内部类

SecurityInterceptor继承HandlerInterceptorAdapter
重写preHandle方法,表明在方法执行前执行拦截的动作
我们在这里对cookie的内容进行判断,如果有登录成功的标志,就进入后台管理界面,否则跳转到登录界面


注意:使用session的方法是不可以的,因为我们在登录的controller当中使用的是重定向(redirect),所以会导致session里面的值取不到
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring bootstrap markdown