本文已参与「新人创作礼」活动,一起开启掘金创作之路。
新手在Spring boot + VUE2 项目的常见问题及接口测试工具
Spring boot
搭建配置
application.yml文件模板
注:在mysql配置中,mysql8.0以下开启useSSL可能会导致报错,8.0以上不开启可能会报错
# 配置端口
server:
port: 8080
spring:
# 配置数据源
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/sport1?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=true
username: root
# mysql8以下的密码不需要单引号
# password: sport
password: 'zgx949'
type: com.alibaba.druid.pool.DruidDataSource
# mybatis-plus相关配置
mybatis-plus:
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:mapper/*.xml
# 以下配置均有默认值,可以不设置
global-config:
db-config:
#主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: auto
#字段策略 IGNORED:"忽略判断" NOT_NULL:"非 NULL 判断") NOT_EMPTY:"非空判断"
field-strategy: NOT_EMPTY
#数据库类型
db-type: MYSQL
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
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 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>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>sport</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sport</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<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.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>
<!-- mybatisPlus 核心库 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<!-- 引入阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<!-- JWT验证依赖-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<!-- 自动生成代码的配置文件地址 -->
<configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
</project>
常见问题
包的目录结构
各层介绍
| 包 | 作用 |
|---|---|
| Bean | 实体对象(和数据库表、字段对应) |
| Controller | 控制对应url的动作 |
| Service | 对应功能接口模块(需要有一个接口和一个具体实现类) |
| Mapper | 查询数据库并映射到Bean中 |
| Utils | 工具类(杂七杂八都放里头) |
| Config | 插件或者拦截器的配置(如:Mybatis、JWT等配置) |
| Annotation | 配置@注解方法(可以用于定义Token验证之类的接口) |
各层模板代码
-
Bean
package com.example.sport.Bean;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("admin")
public class AdminBean {
@TableId
private int id;
@TableField("username")
private String username;
@TableField("password")
private String password;
@TableField("name")
private String name;
@TableField("level")
private int level;
@TableField("college_id")
private int collegeId;
@TableField("is_delete")
private int isDelete;
}
-
Controller
package com.example.sport.Controller;
import com.example.sport.Bean.AdminBean;
import com.example.sport.Bean.menuOptionsBean;
import com.example.sport.Service.AdminService;
import com.example.sport.Utils.CommonApi;
import com.example.sport.Utils.ParamsFormater;
import com.example.sport.Annotation.TokenRequired;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@RestController
@CrossOrigin
@RequestMapping("/admin")
public class Admin {
@Autowired
private AdminService adminService;
/**
* @Description: 获取所有管理员信息
* @Param:
* @return:
* @Author: 左手
* @Date: 2022-01-18
*/
@GetMapping("list")
@TokenRequired
public Map<String, Object> getAdmins(@RequestParam Map<String, Object> params) {
// 解析页面分页参数
Map<String, Object> data = ParamsFormater.pageParams(params);
// 分页查询
List<AdminBean> admins = adminService.getAdmin((int)data.get("page"), (int)data.get("pageSize"));
return CommonApi.success(admins, adminService.countAdmin());
}
}
-
Service
接口:
package com.example.sport.Service;
import com.example.sport.Bean.AdminBean;
import com.example.sport.Bean.MenusBean;
import com.example.sport.Bean.menuOptionsBean;
import java.util.List;
import java.util.Map;
public interface AdminService {
/**
* @Description: 获取管理员用户列表(分页)接口
* @Param: int page 页数
* @Param: int pageSize 页面大小
* @return: AdminBean
* @Author: 左手
* @Date: 2022-01-18
*/
List<AdminBean> getAdmin(int page, int pageSize);
}
实现Impl:
package com.example.sport.Service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.sport.Bean.AdminBean;
import com.example.sport.Bean.MenusBean;
import com.example.sport.Bean.menuOptionsBean;
import com.example.sport.Controller.Admin;
import com.example.sport.Mapper.AdminMapper;
import com.example.sport.Mapper.MenusMapper;
import com.example.sport.Service.AdminService;
import com.example.sport.Utils.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @program: sport
* @description: 管理员服务层实现
* @author: 左手
* @create: 2022-01-18 10:40
**/
@Service
public class AdminServiceImpl implements AdminService {
@Autowired
private AdminMapper adminMapper;
@Override
public List<AdminBean> getAdmin(int page, int pageSize) {
List<AdminBean> result = adminMapper.selectPage(new Page<>(page, pageSize), null).getRecords();
return result;
}
}
-
Mapper
package com.example.sport.Mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.sport.Bean.AdminBean;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.ResultType;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
@Repository
@Mapper
public interface AdminMapper extends BaseMapper<AdminBean> {
/**
* @Description: 自定义sql查询(非必要)
* @Param:
* @return:
* @Author: 左手
* @Date: 2022-02-14
*/
@Select({"${sql}"})
@ResultType(ArrayList.class)
List<AdminBean> executeQuery(@Param("sql") String sql);
}
-
Utils
(无需指定模板)
package com.example.sport.Utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import java.util.Date;
import java.util.HashMap;
/**
* @program: sport
* @description: JWT签名计算计算工具
* @author: 左手
* @create: 2022-02-13 13:50
**/
public class JwtUtil {
//过期时间86400分钟
private static final long EXPIRE_TIME = 24*60*60*1000;
//生成签名,86400分钟后过期
public static String sign(String username,String id,String password){
//过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//使用用户密码作为私钥进行加密
Algorithm algorithm = Algorithm.HMAC256(password);
//设置头信息
HashMap<String, Object> header = new HashMap<>(2);
header.put("typ", "JWT");
header.put("alg", "HS256");
//附带username和adminID生成签名
return JWT.create().withHeader(header).withClaim("id", id)
.withClaim("username",username).withExpiresAt(date).sign(algorithm);
}
//校验token
public static boolean verity(String token,String password){
try {
Algorithm algorithm = Algorithm.HMAC256(password);
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(token);
return true;
} catch (IllegalArgumentException | JWTVerificationException e) {
return false;
}
}
}
-
Config
package com.example.sport.Config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @program: sport
* @description: 分页拦截器
* @author: 左手
* @create: 2022-01-19 15:09
**/
@Configuration
public class MybatisConfiguration {
@Bean
public PaginationInterceptor paginationInterceptor() {
// 分页配置
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
}
-
Annotation
package com.example.sport.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: 管理员Token验证注解定义
* @Param:
* @return:
* @Author: 左手
* @Date: 2022-02-13
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TokenRequired {
boolean required() default true;
}
请求、响应拦截器
实现org.springframework.web.servlet 的HandlerInterceptor类
| 方法 | 调用前提 | 调用时间 | 执行顺序 |
|---|---|---|---|
| preHandle | - | Controller方法处理之前 | 链式Intercepter情况下,Intercepter按照声明的顺序一个接一个执行若返回false,则中断执行,注意:不会进入afterCompletion |
| postHandle | preHandle返回true | Controller方法处理完之后,DispatcherServlet进行视图的渲染之前,也就是说在这个方法中你可以对ModelAndView进行操作(备注:postHandle虽然post打头,但post、get方法都能处理) | 链式Intercepter情况下,Intercepter按照声明的顺序倒着执行。(备注:postHandle虽然post打头,但post、get方法都能处理) |
| afterCompletion | preHandle返回true | DispatcherServlet进行视图的渲染之后 |
集成JWT的token验证
Mybatis-Plus配置
整合Redis
- 完整教程
- 封装操作方法
@Component
public class RedisUtils {
@Autowired
private RedisTemplate redisTemplate;
/**
* 读取缓存
*
* @param key
* @return
*/
public Object get(final String key) {
return redisTemplate.opsForValue().get(key);
}
/**
* 写入缓存
*/
public boolean set( String key, Object value) {
boolean result = false;
try {
redisTemplate.opsForValue().set(key, value,1, TimeUnit.DAYS);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 更新缓存
*/
public boolean getAndSet(final String key, String value) {
boolean result = false;
try {
redisTemplate.opsForValue().getAndSet(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 删除缓存
*/
public boolean delete(final String key) {
boolean result = false;
try {
redisTemplate.delete(key);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
打包成Jar并部署到Linux
- IDEA打包方法(Maven):
-
pom.xml中找到Java版本 (很重要)
java的版本不对的话就容易导致项目在服务器跑不起来
- Linux安装Java(以11为例)
- 运行
-
-
命令行:
java -jar xxx.jar
-
-
- 宝塔:
VUE
常用问题
组件注册
组件通信问题
-
父 -> 子
组件模板引入初始化:
利用引入子组件,以HTML标签中加入Prop类型来进行传递初始化参数
调用子方法:
<templateName ref="asd"></templateName> ... this.$ref.asd.func("123");单向数据流:
父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
-
子 -> 父
子组件回调:
this.$emit(func, args);父组件接收:
<com @func="fun"></com> <script> fun(args) { alert(args); } </script>
-
Vuex的Store通用通信方法
引入配置 main.js
import { createApp } from 'vue' import { createStore } from 'vuex' // 创建一个新的 store 实例 const store = createStore({ state () { return { count: 0 } }, mutations: { increment (state) { state.count++ } } }) const app = createApp({ /* 根组件 */ }) // 将 store 实例作为插件安装 app.use(store)使用方法:
// 通过 `store.state` 来获取状态对象,并通过 `store.commit` 方法触发状态变更: // 修改操作 store.commit('increment') // 获取操作 console.log(store.state.count) // -> 1
- 路由
- axios拦截和封装
接口测试工具
\