前后端分离但人不分离:IDEA+VUE创建springboot项目和对应的前端项目
约 3121 字大约 10 分钟
2025-07-11
环境要求
- 安装 IDEA
- 安装 mysql、Navicat
- 配置好 maven
- 安装 vue-cli
- 安装 postman
实现思路
- 用 IDEA 创建 springboot 项目,并同时勾选依赖 springboot web, mysql, mybatics, lombok
- 设置连接数据库,写一个测试接口 hello,用浏览器测试成功后开始写对数据库的 crud 接口
- 在后端测试成功后,写前端项目,在前端项目中用 axios 访问 get、post 请求,并返回结果
- 在前端测试成功后,打包前端项目,并把打包后的 dist 文件夹中的内容放到 idea 项目的 src/main/resources/static 目录下
- idea 服务器重启,即可用后端的地址正常访问。
具体操作
1. 查看要操作的数据库
user 表
选中表 user,右键“设计表”可以看到属性类型
2. 用 idea 创建 springboot 项目
File | New | Project
Next
Create,等待项目下载完成
项目下载完成后,目录如下。关掉无关页面,双击打开 pom.xml
。如果有忘记安装的依赖可以在这里写入,然后刷新 maven 安装。
打开 application.properties
默认内容如下
添加连接数据库的配置
spring.application.name=mydemo
server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/test_springboot?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
开始写后端代码:在 com.example.mydemo
下新建 3 个包 controller、mapper、entity
然后 entity 中新建一个 User 类,按照数据库 user 表的列属性声明变量,并引入 @Data
注解
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private int age;
private int grade;
}
在 mapper
中新建 UserMapper
接口(暂时先不写内容)
在 controller
中新建 UserController.java
, 并写一个测试接口
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@GetMapping("/hello")
public String hello() {
return "HELLO";
}
}
双击 MydemoApplication.java
打开,右键 Run
,启动服务器
在浏览器地址栏输入 http://localhost:8081/hello ,页面显示 HELLO
表示正确。
3. 写对数据库操作的接口
先在 MydemoApplication.java
上加注解扫描 mapper
类
@MapperScan("com.example.mydemo.mapper")
然后在 UserMapper.java
中编写第一个查询接口
import com.example.mydemo.entity.User;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List<User> queryAll();
}
在 UserController.java
中调用 queryAll
并添加对应注解
import com.example.mydemo.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserMapper userMapper;
@GetMapping("/hello")
public String hello() {
return "HELLO";
}
@GetMapping("/findAll")
public List<User> findAllUser() {
return userMapper.queryAll();
}
}
重启服务器,在浏览器输入 http://localhost:8081/findAll (注意大小写)就查到了数据,和数据库表中的数据一样。
4. 在 vue 项目中调用 http://localhost:8081/findAll 接口
新建一个 vue2 项目,npm run serve
运行
安装 axios 详细过程可以参考 vue 中的网络请求
然后在 HelloWorld.vue
中删除无用的代码,写布局和 axios 访问接口
<template>
<div class="hello">
<h2>测试和 springboot 项目配合使用接口</h2>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "HelloWorld",
data() {
return {
baseUrl: "http://localhost:8081",
};
},
mounted() {
this.testHello();
},
methods: {
testHello() {
const url = this.baseUrl + "/hello";
axios
.get(url)
.then((res) => {
console.log("res", res.data);
})
.catch((err) => {
console.log("err", err);
});
},
},
};
</script>
<style scoped></style>
但是运行后发现报错 has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
这是因为跨域了。跨域有很多种解决办法,这里我使用最简单的一种:后端代码 controller
类上加注解 @CrossOrigin
然后重启服务器。
刷新前端页面,没有报错并返回 HELLO
然后在前端页面中调用 /findAll
接口,成功返回数据。
/**
* 访问"查询所有信息"接口
* 成功返回 所有信息json
*/
testGetAllRecords() {
const url = this.baseUrl + "/findAll";
axios.get(url).then(res => {
console.log('res.data', res.data);
}).catch(err => {
console.log('err', err);
})
}
5. 编写、测试 post 接口
在 mapper 中编写插入、修改接口
package com.example.mydemo.mapper;
import com.example.mydemo.entity.User;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List<User> queryAll();
@Update("insert into user(name, age,grade) values(#{name}, #{age}, #{grade})")
@Transactional
void insertRecord(User user);
@Update("update user set name=#{name}, age=#{age}, grade=#{grade} where id=#{id}")
@Transactional
void updateRecordById(User user);
}
在 controller
中调用 mapper
接口
package com.example.mydemo.controller;
import com.example.mydemo.entity.User;
import com.example.mydemo.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@CrossOrigin
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserMapper userMapper;
@GetMapping("/hello")
public String hello() {
return "HELLO";
}
@GetMapping("/findAll")
public List<User> findAllUser() {
return userMapper.queryAll();
}
@PostMapping("/insertRecord")
public String insertUser(@RequestBody User user) {
userMapper.insertRecord(user);
return "插入数据成功!";
}
@PostMapping("/updateRecord")
public String updateUser(@RequestBody User user) {
userMapper.updateRecordById(user);
return "更新数据成功!";
}
}
重启服务器。因为浏览器不能模拟 post 请求,所以需要用 postman 进行 post 请求。
注意 body 类型,数据插入成功。
然后在前端测试插入数据。
/**
* 访问"插入数据"接口
* 成功返回 “插入数据成功!”
*/
testPostInsertRecord() {
const url = this.baseUrl + "/insertRecord";
const newRecord = {
"name": "dannis",
"age": 33,
"grade": 54
};
axios({
url: url,
method: 'post',
"content-type": "application/x-www-form-urlencoded",
data: newRecord
}).then((res) => {
console.log("res.data:", res.data);
}).catch((err) => {
console.log("err:", err);
});
},
运行后插入成功。
刷新数据库又多了一条新数据
但此时插入的数据是写死的,为了能插入动态的数据,我们需要再前端页面增加输入框。
<template>
<div class="hello">
<h2>测试和 springboot 项目配合使用接口</h2>
<div>
<div class="input-box">
<p>name:</p>
<input v-model="userInfo.name" />
<br />
<p>age:</p>
<input v-model="userInfo.age" />
<br />
<p>grade:</p>
<input v-model="userInfo.grade" />
</div>
<button @click="testPostInsertRecord">插入数据</button>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "HelloWorld",
data() {
return {
baseUrl: "http://localhost:8081",
userInfo: {
name: "",
age: null,
grade: null,
},
};
},
mounted() {
// this.testPostInsertRecord();
},
methods: {
/**
* 访问测试接口
* 成功返回 HELLO
*/
testHello() {
const url = this.baseUrl + "/hello";
axios
.get(url)
.then((res) => {
console.log("res.data", res.data);
})
.catch((err) => {
console.log("err", err);
});
},
/**
* 访问"查询所有信息"接口
* 成功返回 所有信息json
*/
testGetAllRecords() {
const url = this.baseUrl + "/findAll";
axios
.get(url)
.then((res) => {
console.log("res.data", res.data);
})
.catch((err) => {
console.log("err", err);
});
},
/**
* 访问"插入数据"接口
* 成功返回 “插入数据成功!”
*/
testPostInsertRecord() {
const url = this.baseUrl + "/insertRecord";
// const newRecord = {
// "name": "dannis",
// "age": 33,
// "grade": 54
// };
axios({
url: url,
method: "post",
"content-type": "application/x-www-form-urlencoded",
data: this.userInfo,
})
.then((res) => {
console.log("res.data:", res.data);
})
.catch((err) => {
console.log("err:", err);
});
},
},
};
</script>
<style scoped>
.input-box p {
display: inline-block;
width: 60px;
}
</style>
插入数据成功
为了方便查看,在前端把查到的所有用户信息显示出来。
<template>
<div class="hello">
<h2>测试和 springboot 项目配合使用接口</h2>
<div class="input-box">
<p>name:</p>
<input v-model="userInfo.name" />
<br />
<p>age:</p>
<input v-model="userInfo.age" />
<br />
<p>grade:</p>
<input v-model="userInfo.grade" />
</div>
<button @click="testPostInsertRecord">插入数据</button>
<table cellspacing="10">
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>grade</th>
</tr>
<tr v-for="record in allRecords" :key="record.id">
<td>{{ record.id }}</td>
<td>{{ record.name }}</td>
<td>{{ record.age }}</td>
<td>{{ record.grade }}</td>
</tr>
</table>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "HelloWorld",
data() {
return {
baseUrl: "http://localhost:8081",
allRecords: [],
userInfo: {
name: "",
age: null,
grade: null,
},
};
},
mounted() {
this.testGetAllRecords();
},
methods: {
/**
* 访问测试接口
* 成功返回 HELLO
*/
testHello() {
const url = this.baseUrl + "/hello";
axios
.get(url)
.then((res) => {
console.log("res.data", res.data);
})
.catch((err) => {
console.log("err", err);
});
},
/**
* 访问"查询所有信息"接口
* 成功返回 所有信息json
*/
testGetAllRecords() {
const url = this.baseUrl + "/findAll";
axios
.get(url)
.then((res) => {
console.log("res.data", res.data);
this.allRecords = res.data;
})
.catch((err) => {
console.log("err", err);
});
},
/**
* 访问"插入数据"接口
* 成功返回 “插入数据成功!”
*/
testPostInsertRecord() {
const url = this.baseUrl + "/insertRecord";
axios({
url: url,
method: "post",
"content-type": "application/x-www-form-urlencoded",
data: this.userInfo,
})
.then((res) => {
console.log("res.data:", res.data);
// 插入成功后刷新数据
this.testGetAllRecords();
})
.catch((err) => {
console.log("err:", err);
});
},
},
};
</script>
<style scoped>
.input-box p {
display: inline-block;
width: 60px;
}
table {
background-color: aqua;
margin: 20px auto;
}
</style>
插入成功后自动刷新结果
更新、删除以此类推
6. 全部代码
后端代码:
pom.xml
UserController.java
UserMapper.java
User.java
MydemoApplication.java
application.properties
<?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>3.3.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>CRUD</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>CRUD</name>
<description>CRUD</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<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>3.0.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</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>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.3</version>
<scope>test</scope>
</dependency>
<!-- 运行单元测试需要的依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
package com.example.mydemo.controller;
import com.example.mydemo.entity.User;
import com.example.mydemo.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@CrossOrigin
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserMapper userMapper;
@GetMapping("/hello")
public String hello() {
return "HELLO";
}
@GetMapping("/findAll")
public List<User> findAllUser() {
return userMapper.queryAll();
}
@PostMapping("/insertRecord")
public String insertUser(@RequestBody User user) {
userMapper.insertRecord(user);
return "插入数据成功!";
}
@PostMapping("/updateRecord")
public String updateUser(@RequestBody User user) {
userMapper.updateRecordById(user);
return "更新数据成功!";
}
@DeleteMapping("/deleteRecord/{id}")
public String deleteUser(@PathVariable int id) {
userMapper.delRecordById(id);
return "删除数据成功!";
}
}
package com.example.mydemo.mapper;
import com.example.mydemo.entity.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
public interface UserMapper {
@Select("select * from user")
List<User> queryAll();
@Update("insert into user(name, age,grade) values(#{name}, #{age}, #{grade})")
@Transactional
void insertRecord(User user);
@Update("update user set name=#{name}, age=#{age}, grade=#{grade} where id=#{id}")
@Transactional
void updateRecordById(User user);
@Delete("delete from user where id=#{id}")
@Transactional
void delRecordById(int id);
}
package com.example.mydemo.entity;
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private int age;
private int grade;
}
package com.example.mydemo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.mydemo.mapper")
public class MydemoApplication {
public static void main(String[] args) {
SpringApplication.run(MydemoApplication.class, args);
}
}
spring.application.name=mydemo
server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/test_springboot?useSSL=false&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
application.properties
前端代码
HelloWorld.vue
<template>
<div class="hello">
<h2>测试和 springboot 项目配合使用接口</h2>
<div class="input-box">
<p>name:</p>
<input v-model="userInfo.name" />
<br />
<p>age:</p>
<input v-model="userInfo.age" />
<br />
<p>grade:</p>
<input v-model="userInfo.grade" />
</div>
<button @click="testPostInsertRecord">插入数据</button>
<hr />
<table cellspacing="10">
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>grade</th>
<th>操作</th>
</tr>
<tr v-for="record in allRecords" :key="record.id">
<td>{{ record.id }}</td>
<td>{{ record.name }}</td>
<td>{{ record.age }}</td>
<td>{{ record.grade }}</td>
<td>
<button @click="startUpdate(record)">更新</button>
<button @click="testDelRecord(record)">删除</button>
</td>
</tr>
</table>
<div class="input-box" style="background-color: aquamarine;">
<h4>在这里输入更新后的数据!</h4>
<p>id:</p>
<input v-model="updateRecord.id" disabled />
<br />
<p>name:</p>
<input v-model="updateRecord.name" />
<br />
<p>age:</p>
<input v-model="updateRecord.age" />
<br />
<p>grade:</p>
<input v-model="updateRecord.grade" />
<br />
<button @click="testPostUpdateRecord()">确认更新</button>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "HelloWorld",
data() {
return {
baseUrl: "http://localhost:8081",
allRecords: [],
// 插入信息绑定的变量
userInfo: {
name: "",
age: null,
grade: null,
},
// 更新信息绑定的变量
updateRecord: {},
};
},
mounted() {
this.testGetAllRecords();
},
watch: {
allRecords(newvalue, oldvalue) {
console.log({ newvalue });
console.log({ oldvalue });
},
},
methods: {
/**
* 访问测试接口
* 成功返回 HELLO
*/
testHello() {
const url = this.baseUrl + "/hello";
axios
.get(url)
.then((res) => {
console.log("res.data", res.data);
})
.catch((err) => {
console.log("err", err);
});
},
/**
* 访问"查询所有信息"接口
* 成功返回 所有信息json
*/
testGetAllRecords() {
const url = this.baseUrl + "/findAll";
axios
.get(url)
.then((res) => {
console.log("res.data", res.data);
this.allRecords = res.data;
})
.catch((err) => {
console.log("err", err);
});
},
/**
* 访问"插入数据"接口
* 成功返回 “插入数据成功!”
*/
testPostInsertRecord() {
const url = this.baseUrl + "/insertRecord";
axios({
url: url,
method: "post",
"content-type": "application/x-www-form-urlencoded",
data: this.userInfo,
})
.then((res) => {
console.log("res.data:", res.data);
// 插入成功后刷新数据
this.testGetAllRecords();
})
.catch((err) => {
console.log("err:", err);
});
},
/**
* 选中一行数据,准备进行更新
*/
startUpdate(record) {
this.updateRecord = record;
},
/**
* 更新选中的记录
*/
testPostUpdateRecord() {
const url = this.baseUrl + "/updateRecord";
axios({
url: url,
method: "post",
"content-type": "application/x-www-form-urlencoded",
data: this.updateRecord,
})
.then((res) => {
console.log("res.data:", res.data);
// this.testGetAllRecords();
})
.catch((err) => {
console.log("err:", err);
});
},
/**
* 删除选中的记录
*/
testDelRecord(record) {
const url = this.baseUrl + "/deleteRecord/" + record.id;
axios({
url: url,
method: "delete",
})
.then((res) => {
console.log("res.data:", res.data);
this.testGetAllRecords();
})
.catch((err) => {
console.log("err:", err);
});
},
},
};
</script>
<style scoped>
.input-box p {
display: inline-block;
width: 60px;
}
table {
background-color: aqua;
margin: 20px auto;
}
</style>
7. 打包放到服务器上
前端项目运行 npm run build
生成 dist 文件夹
复制文件夹内容,粘贴到 src/main/resources/static 下
重启服务器,访问 http://localhost:8081/ 可以看到和前端项目一样的页面,并可以执行功能。