SpringBoot项目实战记录
1:项目初始化
(1)新建项目
(2)给pox.xml添加依赖
首先就是添加项目下载仓库为阿里云仓库
<repositories>
<repository>
<id>nexus-aliyun</id>
<name>nexus-aliyun</name>
<url>http://aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
并且在dependencies下添加baomidou
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
(3)编写配置文件application.yml
springBoot工程中是有一个application.yml配置文件的啊,其实application.yml的功能和application.properties是一样的,不过因为yml文件是树状结构,写起来有更好的层次感,更易于理解,所以很多人都选择了yml文件。
server:
// 修改端口为9090端口
port: 9090
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
// ?serverTimezone=GMT%2b8表示时区为东八区
url: jdbc:mysql://localhost:3306/party_building?serverTimezone=GMT%2b8
username: root
password: 528957
2:初步编写Mybatis
(1)添加实体entity
所有的实体类需要加上如下注释
@Data : 注在类上,提供类的get、set、equals、hashCode、canEqual、toString方法
@AllArgsConstructor : 注在类上,提供类的全参构造
@NoArgsConstructor : 注在类上,提供类的无参构造
比如现在对于一个党建信息的实体类(com/example/entity/PartyInf.java)
package com.example.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PartyInf {
// 序号0
// @TableId(type = IdType.AUTO)
private Integer id;
// 市(州)1
private String shi;
......
// 创建时间
private LocalDateTime create_time;
}
(2)添加Mapper接口
重点就是这个mapper或者有些人命名为dao的接口需要一个@Mapper的注释声明这是一个mapper接口,如果使用mybatis这个接口时直接对接sql语句的。
(com/example/Mapper/PartyInfMapper.java)
package com.example.mapper;
import com.example.entity.PartyInf;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface PartyInfMapper {
@Select("select * from party_inf")
List<PartyInf> findAll();
}
但是现在这个没有被SpringBoot所管理,呗SpringBoot管理的都必须要有@Conponent
(3)添加控制器类Controller
控制器类主要就是使用@RestController声明这是一个控制器,然后使用 @Autowired调用Mapper里的方法。
package com.example.controller;
import com.example.entity.PartyInf;
import com.example.mapper.PartyInfMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class PartyInfController {
@Autowired
private PartyInfMapper partyInfMapper;
@GetMapping("/party/findAll")
public List<PartyInf> findAll(){
return partyInfMapper.findAll();
}
}
(4)服务类Service
加一个Service
Service需要注解@Service,和@Mapper一样需要这个注解把这个类注入到SpringBoot的容器内
package com.example.service;
import com.example.entity.PartyInf;
import com.example.mapper.PartyInfMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PartyInfService {
@Autowired
private PartyInfMapper partyInfMapper;
}
3:Mybatis实现增删改查
对用户表进行增删改查
首先就是实体类
com/example/entity/UserInf.java
package com.example.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInf {
private Integer id;
private String username;
private String password;
private Integer power;
private String region;
}
之后Mapper数据库操作接口
com/example/mapper/UserInfMapper.java
package com.example.mapper;
import com.example.entity.UserInf;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface UserInfMapper {
@Select("select * from user_inf")
List<UserInf> findAll();
@Insert("INSERT INTO user_inf(username,password,power,region) VALUES (#{username},#{password},#{power},#{region})")
int insert(UserInf userInf);
@Update("UPDATE user_inf SET username=#{username},password=#{password},power=#{power},region=#{region} WHERE id=#{id}")
int update(UserInf userInf);
@Delete("DELETE FROM user_inf WHERE id=#{id}")
int delete(@Param("id") Integer id);
}
Service服务类
com/example/service/UserInfService.java
package com.example.service;
import com.example.entity.UserInf;
import com.example.mapper.UserInfMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserInfService {
@Autowired
private UserInfMapper userInfMapper;
public int save(UserInf userInf){
if (userInf.getId() == null){
return userInfMapper.insert(userInf);
}else {
return userInfMapper.update(userInf);
}
}
}
Controller控制器类
com/example/controller/UserInfController.java
package com.example.controller;
import com.example.entity.UserInf;
import com.example.mapper.UserInfMapper;
import com.example.service.UserInfService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserInfController {
@Autowired
private UserInfMapper userInfMapper;
@Autowired
private UserInfService userInfService;
@GetMapping("/findAll")
public List<UserInf> findAll(){
return userInfMapper.findAll();
}
@PostMapping("/save")
public Integer save(@RequestBody UserInf userInf){
System.out.println(userInf);
return userInfService.save(userInf);
}
@DeleteMapping("/delete/{id}")
public Integer delete(@PathVariable Integer id){
return userInfMapper.delete(id);
}
}
4:SpringBoot解决跨域问题
后端接口写好了前端要用axios进行数据请求还需要配置跨域,跨域分为前端跨域和后端跨域,后端跨域是一种一劳永逸的做法,所以本次开发对后端进行跨域配置。
新建一个config类com/example/config/CorsConfig.java
package com.example.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CorsConfig {
// 当前跨域请求最大有效时长。这里默认1天
private static final long MAX_AGE = 24 * 60 * 60;
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("http://localhost:8080"); // 1 设置访问源地址
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
corsConfiguration.setMaxAge(MAX_AGE);
source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
return new CorsFilter(source);
}
}
5:Mybatis-Plus进行实现分页查询
(1)配置Mybatis-Plus
先在pom.xml下的dependencies标签下引入依赖
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
之后创建一个Mybatis-Plus的Config插件
com/example/config/MybatisPlusConfig.java
package com.example.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
//这个是Mapper层的代码存放目录,之后就可以把Mapper里面的@Mapper注释去掉,这样的好处就是所有的配置统一管理
@MapperScan("com/example/mapper")
public class MybatisPlusConfig {
// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor (DbType.MYSQL));
return interceptor;
}
}
(2)Mybatis-Plus修改原本的增删改查
首先就是修改mapper,Mybatis-Plus下的mapper需要继承BaseMapper<实体>。
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.UserInf;
import org.apache.ibatis.annotations.*;
import java.util.List;
//@Mapper
public interface UserInfMapper extends BaseMapper<UserInf> {
@Select("select * from user_inf")
List<UserInf> findAll();
@Insert("INSERT INTO user_inf(username,password,power,region) VALUES (#{username},#{password},#{power},#{region})")
int insert(UserInf userInf);
@Update("UPDATE user_inf SET username=#{username},password=#{password},power=#{power},region=#{region} WHERE id=#{id}")
int update(UserInf userInf);
@Delete("DELETE FROM user_inf WHERE id=#{id}")
int delete(@Param("id") Integer id);
}
之后修改服务类,Mybatis-Plus自带了一个save方法,所以原本的sava方法就不用了
package com.example.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.UserInf;
import com.example.mapper.UserInfMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserInfService extends ServiceImpl<UserInfMapper,UserInf> {
public boolean saveUserInf(UserInf userInf){
return saveOrUpdate(userInf);
}
}
其他的都可以不变
分页查询代码
5:Vue使用axios进行api请求
(1)装配axios
先安装axios
yarn add [email protected]
在前端项目的src文件夹下新建一个文件夹utils,之后新建文件request.js,request代码如下
src\utils\request.js
import axios from 'axios'
const request = axios.create({
// 注意!! 这里是全局统一加上了 '/api' 前缀,也就是说所有接口都会加上'/api'前缀在
// 页面里面写接口的时候就不要加 '/api'了,否则会出现2个'/api',类似 '/api/api/user'这样的报错,切记!!!
baseURL: '/api',
timeout: 5000
})
// request 拦截器(对请求头进行处理)
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
// config.headers['token'] = user.token; // 设置请求头
return config
}, error => {
return Promise.reject(error)
});
// response 拦截器(对响应结果进行处理)
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
response => {
let res = response.data;
// 如果是返回的文件
if (response.config.responseType === 'blob') {
return res
}
// 兼容服务端返回的字符串数据
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
console.log('err' + error) // for debug
return Promise.reject(error)
}
)
export default request
最后在main.js中引入一下
import request from '@/utils/request'
Vue.prototype.request = request
(2)使用axios进行get和post请求
GET请求
this.request.get("http://127.0.0.1:9090/party/page", {
params: {
pageNum: this.pageNum,
pageSize: this.pageSize,
xxmc: this.xxmc,
xxlx: this.xxlx,
dzzszxs: this.dzzszxs,
}
}).then(res => {
this.tableData = res.data.records
this.total = res.data.total
})
POST请求
this.request.post("http://127.0.0.1:9090/party/save", this.form).then(res => {
if (res.data) {
this.$message.success("保存成功")
this.dialogFormVisible = false
this.load()
} else {
this.$message.error("保存失败")
this.dialogFormVisible = false
this.load()
}
})
DELETE请求
this.request.delete("http://127.0.0.1:9090/party/delete/" + id).then(res => {
if (res.data) {
this.$message.success("删除成功")
this.load()
} else {
this.$message.error("删除失败")
this.load()
}
})
6:SpringBoot+Vue登录
先写后端嘛
(1)SpringBoot创建接收登录参数的DTO
登录主要就只需要账号密码,在Controller项目包下创建一个项目包名为DTO
创建一个LogoinDto.java,需要#Date参数声明这是一个Date型类,
com/example/controller/dto/LoginDto.java代码如下:
package com.example.controller.dto;
import lombok.Data;
@Data
public class LoginDto {
public String username;
public String password;
}
之后编写登录验证函数
控制器类com/example/controller/UserInfController.java
@PostMapping("/login")
public Result login(@RequestBody LoginDto loginDto){
String userneme = loginDto.getUsername();
String password = loginDto.getPassword();
if (StringUtils.isBlank(userneme) || StringUtils.isBlank(password)){
return Result.error("404","账号或密码不可为空");
}
UserInf userInf = userInfService.login(loginDto);
if (userInf == null){
return Result.error("404","账号或密码错误");
}
return Result.success(userInfService.login(loginDto));
}
服务类com/example/service/UserInfService.java
public UserInf login(LoginDto loginDto) {
QueryWrapper<UserInf> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",loginDto.getUsername());
queryWrapper.eq("password",loginDto.getPassword());
UserInf userInf = getOne(queryWrapper);
return userInf;
}
Postman测试一下可行,开始编写前端界面
(2)Vue axios调用登录接口
<template>
<div id="Login" class="wrapper">
<div style="margin:200px auto; background-color: #fff; width: 350px; height: 300px; padding: 20px; border-radius: 10px;">
<div style="margin: 20px 0; text-align: center; font-size: 24px;"><b>Welcome</b></div>
<el-form :rules="rules" :model="user" ref="userForm">
<el-form-item prop="username" label="">
<el-input placeholder="请输入账号" size="medium" style="margin:10px 0" prefix-icon="el-icon-user" v-model="user.username"></el-input>
</el-form-item>
<el-form-item prop="password" label="">
<el-input placeholder="请输入密码" size="medium" style="margin:10px 0" prefix-icon="el-icon-lock" show-password v-model="user.password"></el-input>
</el-form-item>
<div style="margin: 10px 0;text-align:right">
<el-button type="primary" size="small" autocomplete="off" @click="login">登录</el-button>
</div>
</el-form>
</div>
</div>
</template>
<script>
name:"Login"
export default {
data(){
return{
adminLogin:'0',
user: {
username: "username",
password: "123456"
},
rules: {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 3, max: 20, message: '用户名长度在 6 到 20 个字符', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 3, max: 20, message: '密码长度在 6 到 20 个字符', trigger: 'blur' }
],
}
}
},
methods:{
login(){
this.$refs['userForm'].validate((valid) => {
if (valid) {
// 用户登录
if (this.adminLogin === '0'){
this.request.post("http://127.0.0.1:9090/user/login", this.user).then(res =>{
if(res.code === '200'){
this.$message.success('登录成功,欢迎使用!!!');
// localStorage.setItem("user", JSON.stringify(res.data)) // 存储用户信息到前端浏览器
// this.$router.push("/Manage/home")
}else{
this.$message.error(res.msg);
}
console.log(res)
})
}
} else {
this.$message.error('请以正确的方式输入登录信息!!');
return false;
}
});
}
}
}
</script>
<style>
.wrapper{
height: 100vh;
background-image: linear-gradient(to bottom right,#FC466B,#3F5EFB);
overflow: hidden;
}
</style>
7:SpringBoot集成JWT(Json Web Token)
(1)JWT简介
Jwt全称是:json web token。它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证token的正确性,只要正确即通过验证。
优点
- 简洁: 可以通过URL、POST参数或者在HTTP header发送,因为数据量小,传输速度也很快;
- 自包含:负载中可以包含用户所需要的信息,避免了多次查询数据库;
- 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持;
- 不需要在服务端保存会话信息,特别适用于分布式微服务。
缺点
- 无法作废已颁布的令牌;
- 不易应对数据过期。
(2)JWT组成
一个token分3部分,按顺序为
- 头部(header)
- 载荷(payload)
- 签证(signature)
三部分之间用“.”号做分隔。
/*不废话了,开始使用Token/
(3)JWT的使用
1)SpringBoot引入jwt
项目依赖pom.xml
<!--JWT-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
2)编写token工具类
先写一个生成token的方法,将用户id作为载荷,用户密码作为密钥生成token
建立一个utils的插件包,创建一个TokenUtils代码如下:
com/example/utils/TokenUtils.java
package com.example.utils;
import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import java.util.Date;
public class TokenUtils {
public static String genToken(String userId, String sign) {
return JWT.create().withAudience(userId) // 将 userId 保存到 token 里面,作为载荷
.withExpiresAt(DateUtil.offsetDay (new Date(), 1)) // 设置1天(offsetDay)后token过期
.sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥
}
}
3)修改登录的返回值以及方法,把token一起作为返回值的返回到前端
com/example/controller/dto/UserDto.java
package com.example.controller.dto;
import lombok.Data;
@Data
public class UserDto {
private Integer id;
private String username;
private String password;
private Integer power;
private String shi;
private String xian;
private String token;
}
com/example/controller/UserInfController.java
@PostMapping("/login")
public Result login(@RequestBody UserDto userDto){
String userneme = userDto.getUsername();
String password = userDto.getPassword();
if (StringUtils.isBlank(userneme) || StringUtils.isBlank(password)){
return Result.error("404","账号或密码不可为空");
}
if (userInfService.login(userDto) == null){
return Result.error("404","账号或密码错误");
}
return Result.success(userInfService.login(userDto));
}
com/example/service/UserInfService.java
public UserDto login(UserDto userDto) {
QueryWrapper<UserInf> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",userDto.getUsername());
queryWrapper.eq("password",userDto.getPassword());
UserInf userInf = getOne(queryWrapper);
if (userInf == null){
return null;
}else {
BeanUtil.copyProperties(userInf,userDto,true);
userDto.setToken(TokenUtils.genToken(userDto.getId().toString(),userDto.getPassword()));
return userDto;
}
}
4)前端在接收到登录时把返回参数保存下来,并且设置登陆之后的请求时带上token
先修改axios的配置文件,utils/request.js
import axios from 'axios'
const request = axios.create({
// 注意!! 这里是全局统一加上了 '/api' 前缀,也就是说所有接口都会加上'/api'前缀在
// 页面里面写接口的时候就不要加 '/api'了,否则会出现2个'/api',类似 '/api/api/user'这样的报错,切记!!!
baseURL: 'http://127.0.0.1:9090',
timeout: 5000
})
// request 拦截器(对请求头进行处理)
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8';
let user = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : null
if(user){
config.headers['token'] = user.token; // 设置请求头
}
return config
}, error => {
return Promise.reject(error)
});
// response 拦截器(对响应结果进行处理)
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
response => {
let res = response.data;
// 如果是返回的文件
if (response.config.responseType === 'blob') {
return res
}
// 兼容服务端返回的字符串数据
if (typeof res === 'string') {
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
console.log('err' + error) // for debug
return Promise.reject(error)
}
)
export default request
之后修改一下登录的login方法。
this.request.post("/user/login", this.user).then(res =>{
if(res.code === '200'){
this.$message.success('登录成功,欢迎使用!!!');
localStorage.setItem("user", JSON.stringify(res.data))// 存储用户信息到前端浏览器
this.$router.push("/")
}else{
this.$message.error(res.msg);
}
console.log(res)
})
if (response.config.responseType === ‘blob’) {
return res
}
// 兼容服务端返回的字符串数据
if (typeof res === ‘string’) {
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
console.log(‘err’ + error) // for debug
return Promise.reject(error)
}
)
export default request
[外链图片转存中...(img-hs5Hiwcu-1667960992419)]
之后修改一下登录的login方法。
```js
this.request.post("/user/login", this.user).then(res =>{
if(res.code === '200'){
this.$message.success('登录成功,欢迎使用!!!');
localStorage.setItem("user", JSON.stringify(res.data))// 存储用户信息到前端浏览器
this.$router.push("/")
}else{
this.$message.error(res.msg);
}
console.log(res)
})