基于Vue+SpringBoot+MySQL实现个人博客系统
实现前后端完全分离:
前端
- 使用Vue等技术构建单页应用
- 通过Axios或Fetch调用后端提供的API
- 关注交互逻辑和UI展现
后端
- 提供基于RESTful的JSON API
- 只关注数据与业务逻辑
- 不关心前端技术
交互
- 前端通过Axios等调用后端API
- 后端返回JSON数据
- 前端根据JSON数据更新UI
优点
- 前后端解耦
- 独立开发、部署
- 更易于维护
- 前端可替换
- 后端可扩展
例如:
// 前端 axios.get('/api/articles').then(response => { this.articles = response.data })
// 后端 @GetMapping("/api/articles") public List<Article> getArticles() { return articleService.findAll(); }
上述代码实现了:
- 前端调用
/api/articles
接口获取文章列表数据 - 后端提供基于RESTful的JSON API接口
- 通过JSON数据交互
实现前后端分离。
前端只关注使用JSON数据更新UI,后端只关注提供JSON数据。
部署方面,前端可以部署到任何webapp上,后端可以部署到任何Java应用服务器。
通过这种方式,我们可以:
- 独立开发前后端
- 独立部署前后端
- 通过JSON数据进行通信
前端(Vue):
创建Vue项目,包含路由、状态管理等
根据你提供的需求,可按以下步骤创建Vue项目:
- 创建项目
vue create my-blog
选择默认配置(请选择Manually select features)
- 安装依赖
npm install vue-router vuex axios
- vue-router:路由
- vuex:状态管理
- axios:请求库
- 定义路由(router/index.js)
import VueRouter from 'vue-router' const router = new VueRouter({ routes: [ { path: '/', component: ArticleList } ] }) export default router
- 创建状态管理器(store/index.js)
import Vuex from 'vuex' const store = new Vuex.Store({ state: { }, mutations: { }, actions: { } }) export default store
- 定义接口服务(api/article.js)
import axios from 'axios' export default { getArticles() { return axios.get('/api/articles') } }
通过这种方式,我们基本搭建好路由、状态管理和接口调用功能,后续再根据需求完善组件、actions等内容。
定义ArticleList组件,获取文章列表
ArticleList组件获取文章列表的代码如下:
<template> <ul> <li v-for="article in articles"> {{ article.title }} </li> </ul> </template> <script> export default { data() { return { articles: [] } }, created() { this.getArticles() }, methods: { async getArticles() { let { data } = await axios.get('/api/articles') this.articles = data } } } </script>
- 在
data
函数中定义articles
数组保存文章数据 - 在
created
生命周期钩子中,调用getArticles
方法获取文章列表 getArticles
方法异步调用接口请求文章列表- 接口请求成功后,将响应数据
data
赋值给articles
,从而渲染文章列表
主要功能包括:
- 定义
articles
数据 - 在
created
钩子中调用接口 - 通过
await
执行异步接口请求 - 接口成功后设置
articles
渲染列表
上述代码实现了:
- 定义ArticleList组件渲染文章列表
- 在创建时调用接口获取文章数据
- 设置数据后渲染列表
重复定义ArticleEdit、TagList等组件
根据你提供的需求,可按以下步骤定义其他组件:
- ArticleEdit组件
<template> <form @submit.prevent="saveArticle"> <input v-model="article.title" /> <textarea v-model="article.content"></textarea> <button type="submit">保存文章</button> </form> </template> <script> export default { data() { return { article: { title: '', content: '' } } }, methods: { async saveArticle() { await axios.post('/api/article', this.article) this.$router.push('/') } } } </script>
- TagList组件
<template> <ul> <li v-for="tag in tags"> {{ tag.name }} </li> </ul> </template> <script> export default { data() { return { tags: [] } }, async created() { let { data } = await axios.get('/api/tags') this.tags = data } } </script>
- CommentList组件
<template> <ul> <li v-for="comment in comments"> <p>{{ comment.content }}</p> </li> </ul> </template> <script> export default { data() { return { comments: [] } }, async created() { let { data } = await axios.get('/api/comments') this.comments = data } } </script>
通过定义不同的组件,对应处理不同的数据和业务逻辑,我们就可以完整展现页面所需功能。
后端(Spring Boot):
创建Spring Boot项目,配置JPA和MySQL依赖
根据需求,可以按照以下步骤搭建Spring Boot项目:
- 使用IDE(IDEA、STS)创建 Spring Initialzr 项目
选择:
- Group: com.example
- Artifact: demo
- Java: 1.8
- Packaging: Jar
- Dependencies:
- Web
- MySQL
- JPA
- 添加MySQL连接信息
在application.properties
添加:
spring.datasource.url= jdbc:mysql://localhost:3306/demo spring.datasource.username= root spring.datasource.password= 123456
- 定义Article实体类
@Entity public class Article { @Id @GeneratedValue private Long id; private String title; private String content; // getters and setters }
- 定义ArticleRepository
public interface ArticleRepository extends JpaRepository<Article, Long> { }
- 定义ArticleService
@Service public class ArticleService { @Autowired ArticleRepository articleRepository; public List<Article> findAll() { return articleRepository.findAll(); } }
- 定义ArticleController提供API
@GetMapping("/articles") public List<Article> findAll() { return articleService.findAll(); }
通过这种方式,我们基于Spring Boot + JPA 就能快速搭建一个连接MySQL的示例项目。
定义Article实体类映射到数据库表
,Article实体类可以如下定义:
@Entity @Table(name = "articles") public class Article { @Id @GeneratedValue private Long id; @Column(name = "title") private String title; @Column(name = "content") private String content; // getters and setters }
主要特征有:
- 使用
@Entity
注解声明这是一个实体类 - 使用
@Table
指定数据库表名为articles
id
字段使用@Id
和@GeneratedValue
注解,指定为主键和自动增长- 其他字段使用
@Column
注解指定数据库列名 - 为字段提供对应的
getter
和setter
方法
当我们运行项目时,Spring Data JPA 会根据此对象对应的表的结构:
CREATE TABLE articles ( id bigint AUTO_INCREMENT, title VARCHAR(255), content TEXT, PRIMARY KEY (id) )
所以通过在实体类上使用JPA注解,我们就实现了:
- 定义一个映射到数据库表的实体类
- 指定主键和列名映射
- 将Java属性映射到数据库列
JPA在后续进行持久化时,就会根据此对象操作对应的数据库表。
定义ArticleRepository接口,继承JpaRepository
根据需求,ArticleRepository可以如下定义:
public interface ArticleRepository extends JpaRepository<Article, Long> { }
- 继承
JpaRepository
类 - 参数一:管理的实体类
Article
- 参数二:实体类主键类型
Long
通过继承JpaRepository
,我们即声明了一个操作Article
实体类与数据库表对应的仓库接口。
Spring Data JPA会自动为我们实现该接口,提供如下方法:
save() // 保存实体 findAll() //查询全部 findById() // 根据id查询 ...
比如findAll()
方法实现为:
public List<Article> findAll() { return em.createQuery("from Article").getResultList(); }
所以通过继承JpaRepository
,我们只需要定义接口即可:
- 不需要编写任何实现
- Spring Data JPA会自动实现
- 提供CRUD和复杂查询的方法
在后续ArticleService
中,我们可以通过自动装配的方式使用此仓库:
@Autowired ArticleRepository articleRepository;
通过调用其方法实现与数据库的交互。
定义ArticleService服务,调用Repository
根据需求,ArticleService可以如下定义:
@Service public class ArticleService { @Autowired ArticleRepository articleRepository; public List<Article> findAll() { return articleRepository.findAll(); } // 定义其他方法 }
- 使用
@Service
注解声明这是一个服务类 - 通过
@Autowired
自动装配ArticleRepository
- 定义
findAll()
方法调用仓库的findAll()
方法获取全部文章 - 可以定义其他业务方法,调用Repository
所以在服务类中:
- 通过
@Autowired
注入对应的仓库 - 定义业务方法,调用仓库方法
- 提供业务逻辑
同时我们:
- 解耦了Service和Repository
- 服务可以支持多种数据持久层
- Repository专注于数据访问
在Controller中,我们可以注入此服务类使用:
@Autowired ArticleService articleService; // ... articleService.findAll();
通过这种设计:
- Service调用Repository访问数据
- Controller调用Service提供业务
实现了清晰的分层结构。
定义ArticleController控制器,提供RESTful API
根据你的需求,ArticleController可以如下定义:
@RestController @RequestMapping("/api/articles") public class ArticleController { @Autowired ArticleService articleService; @GetMapping public List<Article> findAll() { return articleService.findAll(); } @GetMapping("/{id}") public Article findById(@PathVariable Long id) { ... } // 其它API }
主要特征:
- 使用
@RestController
注解,代表一个RESTful的控制器 - 使用
@RequestMapping
指定基础URL为/api/articles
- 通过
@Autowired
注入ArticleService
- 使用
@GetMapping
标注GET
请求路由,映射到方法 - 在方法中调用服务进行业务逻辑
- 使用
@PathVariable
绑定URL模板变量,如id
- 返回对应实体类的JSON数据
通过这种方式,我们定义了与Article相关的所有RESTful API:
GET /api/articles
获取全部文章GET /api/articles/{id}
根据id获取文章- 其他获取、新增、修改、删除文章的API
客户端(前端)通过这些接口与后端交互,获取JSON格式的数据。
重复定义Tag、Comment等实体类
根据需求,可以按照以下方式定义Tag、Comment等实体类:
Tag
@Entity public class Tag { @Id @GeneratedValue private Long id; @Column(name = "name") private String name; @ManyToMany @JoinTable( name = "article_tag", joinColumns = @JoinColumn(name = "tag_id"), inverseJoinColumns = @JoinColumn(name = "article_id")) private Set<Article> articles; // getters and setters }
Comment
@Entity public class Comment { @Id @GeneratedValue private Long id; @Column(name = "content") private String content; @ManyToOne @JoinColumn(name = "article_id") private Article article; // getters and setters }
主要特征:
- 使用JPA注解定义实体类
- 指定ID生成策略
- 映射数据库表字段
- 定义关系:
@ManyToMany
一个标签对应多个文章@ManyToOne
一个评论对应一个文章
- 使用
@JoinTable
指定关系表article_tag
- 为属性提供
getter
和setter
方法
通过这种方式,我们可以:
- 定义多个实体类映射到不同数据库表
- 定义实体类间的关系映射
- 搭建完整的ORM框架
部署:
分别构建并部署Vue项目和Spring Boot项目
根据你的需求,可以按以下步骤分别构建和部署Vue前端项目和Spring Boot后端项目:
Vue项目
- 使用
vue create
命令创建Vue项目 - 安装依赖:
npm install
- 开发前端组件,定义接口调用等
- 构建产物:
npm run build
此命令会构建出一个dist
文件夹,包含静态文件
- 部署:
- 直接上传
dist
文件夹到任意web服务器即可提供服务 - 也可以使用:
npm install -g serve
安装serve
,使用serve -s dist
本地部署
Spring Boot项目
- 使用IDE创建Spring Initializr项目
- 连接MySQL数据库,编写实体、服务、接口等代码
- 构建产物:
./mvnw package
生成一个可执行的jar
文件
- 部署:
- 直接运行jar文件:
java -jar demo.jar
- 也可部署到任意Java服务器上
交互
- Vue项目中,通过Axios调用Spring Boot提供的RESTful API接口
- 获取JSON数据后,渲染到相应组件
- 实现前后端完全分离
通过这种方式,我们可以:
- 分别开发和部署前后端
- 前端使用Vue等技术
- 后端提供RESTful API服务
- 前后端通过JSON交互
实现前后端完全分离的个人博客系统。
Vue通过Axios调用Spring Boot提供的JSON API
前端Vue项目通过Axios调用Spring Boot提供的JSON API的过程如下:
在Vue项目中,需要:
- 安装Axios库:
npm install axios
- 定义接口服务:
import axios from 'axios' export default { getArticles() { return axios.get('http://localhost:8080/api/articles') } }
- 在组件中使用接口:
import ArticleApi from './api/article' export default { methods: { async loadArticles() { let { data } = await ArticleApi.getArticles() this.articles = data } } }
- 调用方法加载数据:
created() { this.loadArticles() }
在Spring Boot后端,需要定义:
- ArticleController:
@GetMapping("/api/articles") public List<Article> getArticles() { return articleService.findAll(); }
- ArticleService,ArticleRepository在Spring Boot后端,需要定义:ArticleService
@Service public class ArticleService { @Autowired ArticleRepository articleRepository; public List<Article> findAll() { return articleRepository.findAll(); } }
- ArticleService中的方法:
- 调用ArticleRepository进行数据访问
- 提供业务逻辑,如新增、删除文章
- ArticleRepository
public interface ArticleRepository extends JpaRepository<Article, Long> { }
- ArticleRepository:
- 继承JpaRepository,定义对Article操作的方法
- Spring Data JPA会自动实现接口,提供基本CRUD方法
- ArticleController
@RestController public class ArticleController { @Autowired ArticleService articleService; @GetMapping("/api/articles") public List<Article> getArticles() { return articleService.findAll(); } }
- ArticleController:
- 定义JSON API端点
- 通过@Autowired注入ArticleService
- 实现HTTP请求处理方法,调用ArticleService
- 三者组合结构:
- ArticleRepository提供数据访问
- ArticleService封装业务逻辑,调用Repository
- ArticleController提供API,调用Service
- 分层设计:
- 解耦了数据访问和业务逻辑
- Repository只负责数据,Service只负责业务
- Controller只关注API定义
- 所以在Spring后端,需要定义三个部分:Repository(数据)、Service(业务)和Controller(API)。
上述接口会返回:
[ { "id": 1, "title": "Introduction to Spring Boot", "content": "..." }, ... ]
Vue项目通过Axios调用该接口,获取JSON数据,再设置给articles
用来渲染。
通过这种方式,我们实现了:
- 前端通过Axios调用后端提供的JSON API接口
- 后端使用Spring MVC定义Restful JSON API
- 通过JSON交互实现前后端分离
- 实现前后端完全分离
通过以上步骤,我们就可以实现一个基于Vue、Spring Boot和MySQL的简单个人博客系统,实现前后端分离