赞
踩
硅谷课堂是尚硅谷与腾讯云官方合作的项目,是一款基于微信公众号 B2C 模式的在线学习平台,该平台包含三大模块:直播、教学与微信消息服务。平台会定期推出直播课程,方便学生与名师之间的交流互动,学生也可以购买教学视频在线学习,分享直播与教学视频获取平台收益,平台支持直播、腾讯云视频点播、微信支付、微信授权登录、微信菜单、微信消息与腾讯云文件存储等一系列功能,为学生构建了一个全方位的在线学习平台。
硅谷课堂项目具有很强的实用性,业务场景贴近实际,技术应用紧跟市场潮流,完全按照市场需求开发。既是对主流 Java 技术的系统性梳理和整合,同时也是各种主流技术实际应用的练兵场,能够帮助 Java 程序员积累项目经验。本套教程会在腾讯云开发者社区同步上线,也可以在“腾讯云开发者社区”学习和下载教程。
硅谷课堂-智慧星球项目是我作为一名 Java 初学者的第一款微服务实战项目,希望能通过学习和开发这款项目,提高自身编码能力,积累项目开发经验,帮助我拿到校招 Offer。
我是一名主修计算机科学与技术的大四学生,同时也是一个编程爱好者。目前,我正在学习 Java Spring,并且经常分享我的学习经验。我对 Unity 很感兴趣,希望将来成为一名独立的游戏开发者。
在夜空所有星星熄灭的时候,所有梦想、所有溪流,都能汇入同一片大海中,那时我们终会相见。
联系方式:
QQ: 1735350920
WeChat 微信: MYXH1735350920
Email 邮箱: denglei_myxh@qq.com
Twitter 推特: https://twitter.com/MYXH1735350920
Bilibili 哔哩哔哩: https://b23.tv/F3Nv0DP
GitHub: https://github.com/MYXHcode
CSDN: https://blog.csdn.net/qq_40734758
SpringBoot:简化 Spring 应用的初始搭建以及开发过程。
SpringCloud:基于 Spring Boot 实现的云原生应用开发工具,SpringCloud 使用的技术有 Spring Cloud Gateway、Spring Cloud Alibaba Nacos、Spring Cloud Alibaba Sentinel、Spring Cloud Alibaba Seata、Spring Cloud Task 和 Spring Cloud Feign 等。
Mysql:关系型数据库。
MyBatis Plus:持久层框架。
Redis:内存缓存。
RabbitMQ:消息中间件。
腾讯云:文件存储。
腾讯云:视频点播。
欢拓云直播:直播平台。
微信支付:支付工具。
Nginx:负载均衡。
Lombok:简化实体类。
Vue.js:Web 界面的渐进式框架。
Node.js:JavaScript 运行环境。
Axios:Axios 是一个基于 promise 的 HTTP 库。
NPM:包管理器。
Babel:转码器。
Webpack:打包工具。
Git:代码管理工具。
Docker:容器技术。
DockerFile:管理 Docker 镜像命令文本。
官网:https://baomidou.com
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作。
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求。
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错。
支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库。
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题。
支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动。
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作。
支持自定义全局通用操作:支持全局通用方法注入(Write once, use anywhere)。
支持关键词自动转义:支持数据库关键词(order、key …)自动转义,还可自定义关键词。
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置使用。
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询。
内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询。
内置全局拦截插件:提供全表 delete、update 操作智能分析阻断,也可自定义拦截规则,预防误操作。
内置 SQL 注入剥离器:支持 SQL 注入剥离,有效预防 SQL 注入攻击。
mybatis_plus
其表结构如下:
id | name | age | |
---|---|---|---|
1 | Jone | 18 | Jone@qq.com |
2 | Jack | 20 | Jack@qq.com |
3 | Tom | 28 | Tom@qq.com |
4 | Sandy | 21 | Sandy@qq.com |
5 | Billie | 24 | Billie@qq.com |
其对应的建表语句如下:
# 创建数据库 mybatis_plus
CREATE DATABASE IF NOT EXISTS `mybatis_plus`;
# 选择数据库 mybatis_plus
USE `mybatis_plus`;
# 创建用户表 user
CREATE TABLE IF NOT EXISTS `user`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
`age` INT(11) NULL DEFAULT NULL COMMENT '年龄',
`email` VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
);
其对应的数据库 Data 脚本如下:
# 添加用户表 user 的数据
INSERT INTO `user` (`id`, `name`, `age`, `email`)
VALUES (1, 'MYXH', 21, '1735350920@qq.com'),
(2, 'root', 21, 'root@qq.com'),
(3, 'admin', 21, 'admin@qq.com'),
(4, 'test', 18, 'test@qq.com'),
(5, 'Jon', 18, 'jon@qq.com'),
(6, 'Jack', 20, 'jack@qq.com'),
(7, 'Tom', 28, 'tom@qq.com'),
(8, 'Sandy', 21, 'sandy@qq.com'),
(9, 'Billie', 24, 'billie@qq.com');
(1)使用 Spring Initializr 初始化 Spring Boot 工程
GroupId:com.myxh.mybatisplus
ArtifactId:MyBatisPlusDemo
Spring Boot 版本:3.0.2
(2)项目引入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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.0.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.myxh.mybatisplus</groupId> <artifactId>MyBatisPlusDemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>MyBatisPlusDemo</name> <description>MyBatisPlusDemo</description> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Mybatis Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.2</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-extension</artifactId> <version>3.5.3.2</version> </dependency> <!-- MySQL --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <!-- lombok 用来简化实体类 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
(3)IDEA 中安装 Lombok 插件
MySQL 5
# 配置 MySQL 数据源
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus
spring.datasource.username=root
spring.datasource.password=123456
mysql8 以上(Spring Boot 2.1 以上),注意:driver 和 url 的变化。
# 配置 MySQL 数据源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8
spring.datasource.username=MYXH
spring.datasource.password=520.ILY!
注意:
1、这里的 url 使用了 ?serverTimezone=GMT%2B8 后缀,因为 Spring Boot 2.1 及以上集成了 8.0 版本的 jdbc 驱动,这个版本的 jdbc 驱动需要添加这个后缀,否则运行测试用例报告如下错误:
java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or represents more
2、这里的 driver-class-name 使用了 com.mysql.cj.jdbc.Driver ,在 jdbc 8 中 建议使用这个驱动,之前的 com.mysql.jdbc.Driver 已经被废弃,否则运行测试用例的时候会有 WARN 信息。
(1)创建启动类
在 Spring Boot 启动类中添加 @MapperScan
注解,扫描 Mapper 文件夹。
package com.myxh.mybatisplus;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.myxh.mybatisplus.mapper")
public class MyBatisPlusDemoApplication
{
public static void main(String[] args)
{
SpringApplication.run(MyBatisPlusDemoApplication.class, args);
}
}
(2)创建实体类
package com.myxh.mybatisplus.entity; import lombok.Data; import org.springframework.stereotype.Component; /** * @author MYXH * @date 2023/9/25 */ @Component @Data public class User { private Long id; private String name; private Integer age; private String email; }
(3)创建 Mapper
package com.myxh.mybatisplus.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.myxh.mybatisplus.entity.User;
import org.springframework.stereotype.Repository;
/**
* @author MYXH
* @date 2023/9/25
*/
@Repository
public interface UserMapper extends BaseMapper<User>
{
}
(4)功能测试-查询所有记录
package com.myxh.mybatisplus; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest class MyBatisPlusDemoApplicationTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * 查询 User 表中的所有用户数据 */ @Test public void testFindAllUsers() { List<User> userList = userMapper.selectList(null); for (User user : userList) { System.out.println("user = " + user); } } }
注意:
IDEA 在 userMapper 处报错,因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行。
为了避免报错,可以在 DAO 层 的接口上添加 @Repository 注解。
package com.myxh.mybatisplus; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class MyBatisPlusDemoApplicationTests { // 注入 userMapper @Autowired private UserMapper userMapper; // 注入 user @Autowired User user; /** * 添加用户数据 */ @Test public void testAddUser() { user.setName("末影小黑xh"); user.setAge(21); user.setEmail("1735350920@qq.com"); int rows = userMapper.insert(user); System.out.println("rows = " + rows); // 添加成功之后,把添加之后生成 id 值,回填到 user 对象里面 System.out.println("user = " + user); } }
注意: 数据库插入 id 值默认为全局唯一 id。
查看 sql 输出日志
# 配置 MyBatis Plus 日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
(1)ID_WORKER
MyBatis-Plus 默认的主键策略是:ID_WORKER 全局唯一 ID。
(2)自增策略
要想主键自增需要配置如下主键策略:
需要在创建数据表的时候设置主键自增。
实体字段中配置 @TableId(type = IdType.AUTO)。
package com.myxh.mybatisplus.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import org.springframework.stereotype.Component; /** * @author MYXH * @date 2023/9/25 */ @Component @Data public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; }
其它主键策略:分析 IdType 源码可知。
/* * Copyright (c) 2011-2023, baomidou (jobob@qq.com). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baomidou.mybatisplus.annotation; import lombok.Getter; /** * 生成ID类型枚举类 * * @author hubin * @since 2015-11-10 */ @Getter public enum IdType { /** * 数据库ID自增 * <p>该类型请确保数据库设置了 ID自增 否则无效</p> */ AUTO(0), /** * 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) */ NONE(1), /** * 用户输入ID * <p>该类型可以通过自己注册自动填充插件进行填充</p> */ INPUT(2), /* 以下2种类型、只有当插入对象ID 为空,才自动填充。 */ /** * 分配ID (主键类型为number或string), * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法) * * @since 3.3.0 */ ASSIGN_ID(3), /** * 分配UUID (主键类型为 string) * 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-","")) */ ASSIGN_UUID(4); private final int key; IdType(int key) { this.key = key; } public int getKey() { return this.key; } }
注意: update 时生成的 sql 自动是动态 sql:UPDATE user SET email=? WHERE id=?
package com.myxh.mybatisplus; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class MyBatisPlusDemoApplicationTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * 修改用户数据 */ @Test public void testUpdateUser() { // 1、根据 id 查询 User userById = userMapper.selectById(1706273960528822274L); // 2、设置修改值 userById.setEmail("myxh@qq.com"); // 3、调用方法实现修改 int rows = userMapper.updateById(userById); System.out.println("rows = " + rows); System.out.println("userById = " + userById); } }
MyBatis Plus 自带分页插件,只要简单的配置即可实现分页功能。
(1)创建配置类
package com.myxh.mybatisplus.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author MYXH * @date 2023/9/25 */ @Configuration public class MyBatisPlusConfig { /** * 分页插件 * * @return MyBatis Plus 拦截器 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
(2)测试 selectPage 分页
测试: 最终通过 page 对象获取相关数据。
package com.myxh.mybatisplus; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest class MyBatisPlusDemoApplicationTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * 分页查询 User 表中的所有用户数据 */ @Test public void testFindAllUsersByPage() { // 创建 Page 对象,传递两个参数:当前页、每页显示记录数 Page<User> userPage = new Page<>(1, 5); // 调用 userMapper 的方法实现分页 // IPage<User> userPageModel = userMapper.selectPage(userPage, null); userMapper.selectPage(userPage, null); List<User> records = userPage.getRecords(); System.out.println("records = " + records); long pages = userPage.getPages(); System.out.println("pages = " + pages); long size = userPage.getSize(); System.out.println("size = " + size); long total = userPage.getTotal(); System.out.println("total = " + total); boolean hasNext = userPage.hasNext(); System.out.println("hasNext = " + hasNext); boolean hasPrevious = userPage.hasPrevious(); System.out.println("hasPrevious = " + hasPrevious); } }
控制台 SQL 语句打印:SELECT id,name,age,email,create_time,update_time FROM user LIMIT 0,5
package com.myxh.mybatisplus; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class MyBatisPlusDemoApplicationTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * 根据 id 删除用户数据 */ @Test public void testDeleteUserById() { int rows = userMapper.deleteById(9L); System.out.println("rows = " + rows); } }
package com.myxh.mybatisplus; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Arrays; @SpringBootTest class MyBatisPlusDemoApplicationTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * 根据 id 批量删除用户数据 */ @Test public void testDeleteBatchUserById() { int rows = userMapper.deleteBatchIds(Arrays.asList(5, 6, 7, 8, 9)); System.out.println("rows = " + rows); } }
(1)数据库中添加 deleted 字段
# user 表中添加 deleted 字段
ALTER TABLE `user` ADD COLUMN `deleted` BOOLEAN DEFAULT 0;
(2)实体类添加 deleted 字段
并加上 @TableLogic 注解。
package com.myxh.mybatisplus.entity; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; import org.springframework.stereotype.Component; /** * @author MYXH * @date 2023/9/25 */ @Component @Data public class User { // @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; // 逻辑删除标志 @TableLogic private Integer deleted; }
(3)application.properties 加入配置
此为默认值,如果你的默认值和 MyBatis Plus 默认的一样,该配置可无。
# 配置 MyBatis Plus 逻辑删除标志,默认 0 代表没有删除,1 代表已经删除
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
(5)测试逻辑删除
测试后发现,数据并没有被删除,deleted 字段的值由 0 变成了 1。
测试后分析打印的 SQL 语句,是一条 update。
注意: 被删除数据的 deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作。
package com.myxh.mybatisplus; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest class MyBatisPlusDemoApplicationTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * 根据 id 删除用户数据(逻辑删除) */ @Test public void testDeleteUserById() { int rows = userMapper.deleteById(9L); System.out.println("rows = " + rows); } }
(7)测试逻辑删除后的查询
MyBatis Plus 中查询操作也会自动添加逻辑删除字段的判断。
package com.myxh.mybatisplus; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; @SpringBootTest class MyBatisPlusDemoApplicationTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * 查询 User 表中的所有用户数据 */ @Test public void testFindAllUsers() { List<User> userList = userMapper.selectList(null); for (User user : userList) { System.out.println("user = " + user); } } }
测试后分析打印的 SQL 语句,包含 WHERE deleted=0。
SELECT id,name,age,email,create_time,update_time,deleted FROM user WHERE deleted=0
Wrapper:条件构造抽象类,最顶端父类。
AbstractWrapper:用于查询条件封装,生成 SQL 的 where 条件。
QueryWrapper:Entity 对象封装操作类,不是用 Lambda 语法。
UpdateWrapper:Update 条件封装,用于 Entity 对象更新操作。
AbstractLambdaWrapper:Lambda 语法使用 Wrapper 统一处理解析 Lambda 获取 column。
LambdaQueryWrapper:看名称也能明白就是用于 Lambda 语法使用的查询 Wrapper。
LambdaUpdateWrapper:Lambda 更新封装 Wrapper。
注意:以下条件构造器的方法入参中的 column
均表示数据库字段
(1)ge、gt、le、lt
package com.myxh.mybatisplus; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; /** * @author MYXH * @date 2023/9/25 */ @SpringBootTest public class QueryDemoTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * queryWrapper * ge、gt、le、lt */ @Test public void testQuery1() { // 创建条件构造对象 QueryWrapper<User> wrapper = new QueryWrapper<>(); // ge、gt、le、lt // ge 为例有两个参数:第一个参数是表字段名称,第二个参数是值 wrapper.ge("age", 21); // 调用方法实现条件查询 List<User> userList = userMapper.selectList(wrapper); System.out.println("userList = " + userList); } }
(2)eq、ne
注意: seletOne 返回的是一条实体记录,当出现多条时会报错。selectList 返回的是多条实体记录。
package com.myxh.mybatisplus; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; /** * @author MYXH * @date 2023/9/25 */ @SpringBootTest public class QueryDemoTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * queryWrapper * eq、ne */ @Test public void testQuery2() { // 创建条件构造对象 QueryWrapper<User> wrapper = new QueryWrapper<>(); // eq、ne // eq 为例有两个参数:第一个参数是表字段名称,第二个参数是值 wrapper.eq("name", "MYXH"); // 调用方法实现条件查询 List<User> userList = userMapper.selectList(wrapper); System.out.println("userList = " + userList); } }
SELECT id,name,age,email,create_time,update_time,deleted,version FROM user WHERE deleted=0 AND name = ?
(3)like、likeLeft、likeRight
selectMaps 返回 Map 集合列表。
package com.myxh.mybatisplus; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; /** * @author MYXH * @date 2023/9/25 */ @SpringBootTest public class QueryDemoTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * queryWrapper * like、likeLeft、likeRight */ @Test public void testQuery3() { // 创建条件构造对象 QueryWrapper<User> wrapper = new QueryWrapper<>(); // like、likeLeft、likeRight // like、likeLeft 为例有两个参数:第一个参数是表字段名称,第二个参数是值 // wrapper.like("name","MYXH"); wrapper.likeLeft("name", "XH"); // 调用方法实现条件查询 List<User> userList = userMapper.selectList(wrapper); System.out.println("userList = " + userList); } }
SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 AND name LIKE ?
(4)orderByDesc、orderByAsc
package com.myxh.mybatisplus; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; /** * @author MYXH * @date 2023/9/25 */ @SpringBootTest public class QueryDemoTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * queryWrapper * orderByDesc、orderByAsc */ @Test public void testQuery4() { // 创建条件构造对象 QueryWrapper<User> wrapper = new QueryWrapper<>(); // orderByDesc、orderByAsc // orderByDesc 为例有一个参数:参数是表字段名称 wrapper.orderByDesc("id"); // 调用方法实现条件查询 List<User> userList = userMapper.selectList(wrapper); System.out.println("userList = " + userList); } }
SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 ORDER BY id DESC
package com.myxh.mybatisplus; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; /** * @author MYXH * @date 2023/9/25 */ @SpringBootTest public class QueryDemoTests { // 注入 userMapper @Autowired private UserMapper userMapper; /** * LambdaQueryWrapper */ @Test public void testLambdaQuery1() { // 创建条件构造对象 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getName, "MYXH"); // 调用方法查询 List<User> userList = userMapper.selectList(wrapper); System.out.println("userList = " + userList); } /** * LambdaQueryWrapper */ @Test public void testLambdaQuery2() { // 创建条件构造对象 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.like(User::getName, "MYXH"); // 调用方法查询 List<User> userList = userMapper.selectList(wrapper); System.out.println("userList = " + userList); } }
SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 AND name = ?
SELECT id,name,age,email,create_time,update_time,deleted,version
FROM user WHERE deleted=0 AND name LIKE ?
package com.myxh.mybatisplus.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.myxh.mybatisplus.entity.User;
/**
* @author MYXH
* @date 2023/9/25
*/
public interface UserService extends IService<User>
{
}
package com.myxh.mybatisplus.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.mapper.UserMapper; import com.myxh.mybatisplus.service.UserService; import org.springframework.stereotype.Service; /** * @author MYXH * @date 2023/9/25 */ @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { // 注入 userMapper // @Autowired // private UserMapper userMapper; }
底层封装了注入 Mapper 过程
package com.myxh.mybatisplus; import com.myxh.mybatisplus.entity.User; import com.myxh.mybatisplus.service.UserService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; /** * @author MYXH * @date 2023/9/25 */ @SpringBootTest public class ServiceDemoTests { // 注入 userService @Autowired private UserService userService; /** * 查询 User 表中的所有用户数据 */ @Test public void testFindAllUsers() { List<User> userList = userService.list(); for (User user : userList) { System.out.println("user = " + user); } } }
以下规则只针对本模块,更全面的文档参考《阿里巴巴 Java 开发手册》:
1、库名与应用名称尽量一致。
2、表名、字段名必须使用小写字母或数字,禁止出现数字开头。
3、表名不使用复数名词。
4、表的命名最好是加上“业务名称_表的作用”。如,edu_teacher。
5、表必备三字段:id, gmt_create, gmt_modified。
说明:
其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。
如果使用分库分表集群部署,则 id 类型为 verchar,非自增,业务中使用分布式 id 生成器。
gmt_create, gmt_modified 的类型均为 datetime 类型,前者现在时表示主动创建,后者过去分词表示被动更新。
6、单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。
7、表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint (1 表示是,0 表示否)。
说明:任何字段如果为非负数,必须是 unsigned。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的 命名方式是为了明确其取值含义与取值范围。
正例:表达逻辑删除的字段名 is_deleted,1 表示删除,0 表示未删除。
8、小数类型为 decimal,禁止使用 float 和 double。 说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。
9、如果存储的字符串长度几乎相等,使用 char 定长字符串类型。
10、varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
11、唯一索引名为 uk*字段名;普通索引名则为 idx*字段名。
说明:uk* 即 unique key;idx* 即 index 的简称
12、不得使用外键与级联,一切外键概念必须在应用层解决。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
模块说明:
SmartPlanet: 智慧星球根目录(父工程),管理多个子模块。
common:公共模块父节点。
common-util:工具类模块,所有模块都可以依赖于它。
service-utils:service 服务的 base 包,包含 service 服务的公共配置类,所有 service 模块依赖于它。
rabbit-utils:rabbitmq 封装工具类。
model:实体类相关模块。
server-gateway:服务网关。
service:API 接口服务父节点。
service-acl:权限管理接口服务。
service-activity:优惠券 API 接口服务。
service-live:直播课程 API 接口服务。
service-order:订单 API 接口服务。
service-user:用户 API 接口服务。
service-vod:点播课程 API 接口服务。
service-wechat:公众号 API 接口服务。
service-client:feign 服务调用父节点。
service-activity-client:优惠券 API 接口。
service-live-client:直播课程 API 接口。
service-order-client:订单 API 接口。
service-user-client:用户 API 接口。
service-vod-client:点播课程 API 接口。
在 IDEA 开发工具中,使用 Spring Initializr 快速初始化一个 Spring Boot 模块。
配置:
groupId:com.myxh.smart.planet
artifactId:SmartPlanet
使用 SpringBoot 版本为 :3.0.2
添加如下依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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.0.2</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.myxh.smart.planet</groupId> <artifactId>SmartPlanet</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <name>SmartPlanet</name> <description>SmartPlanet</description> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <skipTests>true</skipTests> <java.version>17</java.version> <spring-cloud.version>2022.0.0</spring-cloud.version> <spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version> <mybatis-plus.version>3.5.3.2</mybatis-plus.version> <mysql-connector-j.version>8.0.32</mysql-connector-j.version> <knife4j>3.0.3</knife4j> <jjwt-api.version>0.11.5</jjwt-api.version> <httpclient.version>4.5.13</httpclient.version> <fastjson.version>2.0.32</fastjson.version> <easyexcel.version>3.3.2</easyexcel.version> <aliyun-java-sdk-core.version>4.5.16</aliyun-java-sdk-core.version> <aliyun-sdk-oss.version>3.17.1</aliyun-sdk-oss.version> <joda-time.version>2.12.5</joda-time.version> <xxl-job-core.version>2.4.0</xxl-job-core.version> <springdoc-openapi-starter-webmvc-ui>2.1.0</springdoc-openapi-starter-webmvc-ui> </properties> <!--配置 dependencyManagement 锁定依赖的版本--> <dependencyManagement> <dependencies> <!-- Spring Cloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Spring Cloud Alibaba --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- Mybatis Plus 持久层 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> <!-- MySQL --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>${mysql-connector-j.version}</version> </dependency> <!-- knife4j --> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>${knife4j}</version> </dependency> <!-- jjwt --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>${jjwt-api.version}</version> </dependency> <!-- httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>${httpclient.version}</version> </dependency> <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <!-- easyexcel --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>${easyexcel.version}</version> </dependency> <!-- aliyun-java-sdk-core --> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>${aliyun-java-sdk-core.version}</version> </dependency> <!--aliyunOSS--> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>${aliyun-sdk-oss.version}</version> </dependency> <!-- 日期时间工具 --> <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>${joda-time.version}</version> </dependency> <!-- xxl-job-core --> <dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>${xxl-job-core.version}</version> </dependency> <!-- OpenAPI 3 整合 Swagger --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>${springdoc-openapi-starter-webmvc-ui}</version> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
groupId:com.myxh.smart.planet
artifactId:model
<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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> <parent> <groupId>com.myxh.smart.planet</groupId> <artifactId>SmartPlanet</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>model</artifactId> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- lombok 用来简化实体类 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- Mybatis Plus 持久层 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <scope>provided</scope> </dependency> <!-- easyexcel --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <scope>provided</scope> </dependency> <!-- knife4j --> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <scope>provided</scope> </dependency> <!-- data-mongodb --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> <scope>provided</scope> </dependency> <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <scope>provided</scope> </dependency> <!-- data-elasticsearch 创建索引库 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> <scope>provided</scope> </dependency> <!-- OpenAPI 3 整合 Swagger --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> </dependency> </dependencies> </project>
<?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> <parent> <groupId>com.myxh.smart.planet</groupId> <artifactId>SmartPlanet</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>service</artifactId> <packaging>pom</packaging> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- model 数据载体 --> <dependency> <groupId>com.myxh.smart.planet</groupId> <artifactId>model</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- Web 需要启动项目 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Mybatis Plus 持久层 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <!-- MySQL --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> </dependency> <!-- alibaba-nacos-discovery 服务注册 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- openfeign 服务调用 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- alibaba-sentinel 流量控制 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!-- devtools 开发者工具 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies> </project>
在 service 模块创建子模块 service-vod。
添加课程时候,需要选择所属教师,所以要对教师进行管理,就是基于教师的 CRUD 操作。
(1)引入代码生成器依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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> <parent> <groupId>com.myxh.smart.planet</groupId> <artifactId>service</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>service-vod</artifactId> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- model 数据载体 --> <dependency> <groupId>com.myxh.smart.planet</groupId> <artifactId>model</artifactId> <version>0.0.1-SNAPSHOT</version> <scope>compile</scope> </dependency> <!-- mybatis-plus-generator --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.3.1</version> </dependency> <!-- freemarker --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.32</version> </dependency> </dependencies> </project>
(2)编写生成代码工具类
修改代码中路径、数据库、包和表,创建在 test 目录下。
(3)实体类统一替换为 model 模块的实体类
配置文件:
# 服务端口 server.port=8301 # 服务名 spring.application.name=service-vod # 环境设置:dev、test、prod spring.profiles.active=dev # MySQL 数据库连接 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/smart_planet_vod?characterEncoding=utf-8&useSSL=false spring.datasource.username=MYXH spring.datasource.password=520.ILY! # 返回 Json 的全局时间格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 # MyBatis 日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
启动类:
package com.myxh.smart.planet.vod; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author MYXH * @date 2023/9/29 */ @SpringBootApplication public class ServiceVodApplication { public static void main(String[] args) { SpringApplication.run(ServiceVodApplication.class, args); } }
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.model.vod.Teacher; import com.myxh.smart.planet.vod.service.TeacherService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 查询所有教师 * * @return teacherList 所有教师 */ @GetMapping("find/all") public List<Teacher> findAllTeacher() { List<Teacher> teacherList = teacherService.list(); return teacherList; } }
package com.myxh.smart.planet.vod.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * @author MYXH * @date 2023/9/29 * @description 配置类 */ @Configuration @MapperScan("com.myxh.smart.planet.vod.mapper") public class VodConfig { }
访问 http://localhost:8301/admin/vod/teacher/find/all
得到 json 数据。
TeacherController 添加删除方法。
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.vod.service.TeacherService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 逻辑删除教师 * * @param id id * @return isSuccess 是否成功 */ @DeleteMapping("remove/{id}") public boolean removeTeacher(@PathVariable("id") Long id) { boolean isSuccess = teacherService.removeById(id); return isSuccess; } }
因为删除教师接口是 delete 提交方式,使用浏览器无法直接访问测试,可以通过工具测试,比如 Postman,这里通过整合 Swagger 进行接口测试。
前后端分离开发模式中,api 文档是最好的沟通方式。Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)、规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)、一致性(接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)、可测性(直接在接口文档上进行测试,以方便理解业务)。
(1)在 SmartPlanet 下创建子模块 common。
(2)在 common 模块引入依赖。
<?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> <parent> <groupId>com.myxh.smart.planet</groupId> <artifactId>SmartPlanet</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>common</artifactId> <packaging>pom</packaging> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Spring MVC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <scope>provided </scope> </dependency> <!-- Mybatis Plus 持久层 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <scope>provided </scope> </dependency> <!-- lombok 用来简化实体类 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> <!-- OpenAPI 3 整合 Swagger --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> </dependency> </dependencies> </project>
(3)在 common 下创建子模块 service-utils。
(4)在 service-utils 创建 swagger 配置类。
package com.myxh.smart.planet.swagger; import io.swagger.v3.oas.models.ExternalDocumentation; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import org.springdoc.core.models.GroupedOpenApi; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author MYXH * @date 2023/9/29 */ @Configuration public class SwaggerConfig { @Bean public GroupedOpenApi webApiConfig() { return GroupedOpenApi.builder() .group("smart.planet") .pathsToMatch("/admin/**") .addOpenApiMethodFilter(method -> true) .build(); } private OpenAPI webApiInfo() { return new OpenAPI() .info(new Info().title("网站-API文档") .description("本文档描述了网站微服务接口定义") .version("0.0.1-SNAPSHOT") .license(new License().name("GNU GENERAL PUBLIC LICENSE Version 3") .url("https://www.gnu.org/licenses/gpl-3.0.html"))) .externalDocs(new ExternalDocumentation() .description("智慧星球官网") .url("https://myxh.smart.planet.com")); } }
(5)在 service 模块引入 service-utils 依赖。
<?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> <parent> <groupId>com.myxh.smart.planet</groupId> <artifactId>SmartPlanet</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <artifactId>service</artifactId> <packaging>pom</packaging> <modules> <module>service-vod</module> </modules> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- model 数据载体 --> <dependency> <groupId>com.myxh.smart.planet</groupId> <artifactId>model</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- service-utils --> <dependency> <groupId>com.myxh.smart.planet</groupId> <artifactId>service-utils</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!-- Web 需要启动项目 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Mybatis Plus 持久层 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <!-- MySQL --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> </dependency> <!-- alibaba-nacos-discovery 服务注册 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- openfeign 服务调用 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- alibaba-sentinel 流量控制 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!-- devtools 开发者工具 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies> </project>
(6)在 service-vod 启动类上添加注解,进行测试。
package com.myxh.smart.planet.vod; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; /** * @author MYXH * @date 2023/9/29 */ @SpringBootApplication @ComponentScan("com.myxh.smart.planet") public class ServiceVodApplication { public static void main(String[] args) { SpringApplication.run(ServiceVodApplication.class, args); } }
定义在类上:@Tag
定义在方法上:@Operation
定义在参数上:@Parameter
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.model.vod.Teacher; import com.myxh.smart.planet.vod.service.TeacherService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @Tag(name = "教师接口", description = "教师管理接口") @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 查询所有教师 * * @return teacherList 所有教师 */ @Operation(summary = "查询", description = "查询所有教师") @GetMapping("find/all") public List<Teacher> findAllTeacher() { List<Teacher> teacherList = teacherService.list(); return teacherList; } /** * 逻辑删除教师 * * @param id id * @return isSuccess 是否成功 */ @Operation(summary = "删除", description = "逻辑删除教师") @DeleteMapping("remove/{id}") public boolean removeTeacher(@Parameter(name = "id", description = "ID", required = true) @PathVariable("id") Long id) { boolean isSuccess = teacherService.removeById(id); return isSuccess; } }
(1)浏览器输入固定地址 http://localhost:8301/swagger-ui.html
(2)测试接口
(3)执行接口
项目中会将响应封装成 json 返回,一般会将所有接口的数据格式统一, 使前端(iOS, Android, Web)对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容。
例如,系统要求返回的基本数据格式如下:
列表:
{
"code": 200,
"message": "成功",
"data": [
{
"id": 1,
"name": "张老师",
"intro": "高级教师"
}
],
"ok": true
}
分页:
{ "code": 200, "message": "成功", "data": { "records": [ { "id": 1, "name": "张老师", "intro": "高级教师" }, { "id": 2, "name": "李老师", "intro": "高级教师" }, { "id": 3, "name": "钟老师", "intro": "高级老师" } ], "total": 5, "size": 3, "current": 1, "orders": [], "hitCount": false, "searchCount": true, "pages": 2 }, "ok": true }
没有返回数据:
{
"code": 200,
"message": "成功",
"data": null,
"ok": true
}
失败:
{
"code": 201,
"message": "失败",
"data": null,
"ok": false
}
在 service-utils 模块创建 interfacle 定义返回状态码。
package com.myxh.smart.planet.result; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; /** * @author MYXH * @date 2023/9/30 * @description 统一返回结果状态信息类 */ @Schema(description = "统一返回结果状态信息") @Getter public enum ResultCodeEnum { SUCCESS(200, "成功"), FAIL(201, "失败"), SERVICE_ERROR(2012, "服务异常"), DATA_ERROR(204, "数据异常"), ILLEGAL_REQUEST(205, "非法请求"), REPEAT_SUBMIT(206, "重复提交"), LOGIN_AUTH(208, "未登陆"), PERMISSION(209, "没有权限"), PHONE_CODE_ERROR(211, "手机验证码错误"), MTCLOUD_ERROR(210, "直播接口异常"), COUPON_GET(220, "优惠券已经领取"), COUPON_LIMIT_GET(221, "优惠券已发放完毕"), FILE_UPLOAD_ERROR(21004, "文件上传错误"), FILE_DELETE_ERROR(21005, "文件刪除错误"), VOD_PALY_ERROR(209, "请购买后观看"), ; @Schema(description = "状态码") private final Integer code; @Schema(description = "返回状态信息(成功、失败)") private final String message; ResultCodeEnum(Integer code, String message) { this.code = code; this.message = message; } }
在 service-utils 模块创建结果类。
package com.myxh.smart.planet.result; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.NoArgsConstructor; /** * @author MYXH * @date 2023/9/30 * @description 全局统一返回结果类 */ @Schema(description = "全局统一返回结果") @NoArgsConstructor @Data public class Result<T> { @Schema(description = "状态码") private Integer code; @Schema(description = "返回状态信息(成功、失败)") private String message; @Schema(description = "返回数据") private T data; /** * 使用 ResultCodeEnum 枚举构建 Result 实例 * * @param body 返回数据 * @param resultCodeEnum ResultCodeEnum 结果码枚举 * @return 构建好的 Result 实例 * @param <T> 数据类型 */ public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) { Result<T> result = new Result<>(); if (body != null) { result.setData(body); } result.setCode(resultCodeEnum.getCode()); result.setMessage(resultCodeEnum.getMessage()); return result; } /** * 操作成功,有 data 数据 * * @param data 数据 * @param <T> 数据 data 类型 T * @return result 结果 */ public static <T> Result<T> ok(T data) { return build(data, ResultCodeEnum.SUCCESS); } /** * 操作失败,有 data 数据 * * @param data 数据 * @param <T> 数据 data 类型 T * @return result 结果 */ public static <T> Result<T> fail(T data) { return build(data, ResultCodeEnum.FAIL); } /** * 设置消息字段值 * * @param message 要设置的值 * @return 结果实例(用于链式调用) */ public Result<T> message(String message) { this.setMessage(message); return this; } /** * 设置代码字段值 * * @param code 要设置的值 * @return 结果实例(用于链式调用) */ public Result<T> code(Integer code) { this.setCode(code); return this; } /** * 操作成功,没有 data 数据 * * @return result 结果 * @param <T> 数据 data 类型 T */ /* public static <T> Result<T> ok() { Result<T> result = new Result<>(); result.setCode(200); result.setMessage("成功"); return result; } */ /** * 操作失败,没有 data 数据 * * @return result 结果 * @param <T> 数据 data 类型 T */ /* public static <T> Result<T> fail() { Result<T> result = new Result<>(); result.setCode(201); result.setMessage("失败"); return result; } */ }
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.model.vod.Teacher; import com.myxh.smart.planet.result.Result; import com.myxh.smart.planet.vod.service.TeacherService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @Tag(name = "教师接口", description = "教师管理接口") @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 查询所有教师 * * @return Result 全局统一返回结果 */ @Operation(summary = "查询所有", description = "查询所有教师") @GetMapping("find/all") public Result<List<Teacher>> findAllTeacher() { List<Teacher> teacherList = teacherService.list(); return Result.ok(teacherList).message("查询所有教师数据成功"); } /** * 逻辑删除教师 * * @param id id * @return Result 全局统一返回结果 */ @Operation(summary = "删除", description = "逻辑删除教师") @DeleteMapping("remove/{id}") public Result<Void> removeTeacher(@Parameter(name = "id", description = "ID", required = true) @PathVariable("id") Long id) { boolean isSuccess = teacherService.removeById(id); if (isSuccess) { return Result.<Void>ok(null).message("逻辑删除教师数据成功"); } else { return Result.<Void>fail(null).message("逻辑删除教师数据失败"); } } }
package com.myxh.smart.planet.vod.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; /** * @author MYXH * @date 2023/9/29 * @description 配置类 */ @Configuration @MapperScan("com.myxh.smart.planet.vod.mapper") public class VodConfig { /** * 分页插件 * * @return MyBatis Plus 拦截器 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
package com.myxh.smart.planet.vo.vod; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** * @author MYXH * @date 2023/9/27 */ @Data public class TeacherQueryVo { @Schema(description = "教师姓名") private String name; @Schema(description = "头衔:1 -> 高级教师,2 -> 首席教师") private Integer level; @Schema(description = "入驻时间") private String joinDateBegin; @Schema(description = "入驻时间") private String joinDateEnd; }
package com.myxh.smart.planet.vod.controller; 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.myxh.smart.planet.model.vod.Teacher; import com.myxh.smart.planet.result.Result; import com.myxh.smart.planet.vo.vod.TeacherQueryVo; import com.myxh.smart.planet.vod.service.TeacherService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @Tag(name = "教师接口", description = "教师管理接口") @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 条件查询教师分页 * * @param current 当前页码 * @param limit 每页记录数 * @param teacherQueryVo 查询对象 * @return Result 全局统一返回结果 */ @Operation(summary = "条件查询分页", description = "条件查询教师分页") @PostMapping("find/query/page/{current}/{limit}") public Result<IPage<Teacher>> findTeacherPage(@Parameter(name = "current", description = "当前页码", required = true) @PathVariable("current") Long current, @Parameter(name = "limit", description = "每页记录数", required = true) @PathVariable("limit") Long limit, @Parameter(name = "TeacherQueryVo", description = "查询对象") @RequestBody(required = false) TeacherQueryVo teacherQueryVo) { // 创建 page 对象,传递当前页和每页记录数 Page<Teacher> teacherPageParam = new Page<>(current, limit); // 判断 teacherQueryVo 对象是否为空 if (teacherQueryVo == null) { // 查询所有教师 IPage<Teacher> teacherPageModel = teacherService.page(teacherPageParam, null); return Result.ok(teacherPageModel); } else { // 获取条件值 // 教师名称 String name = teacherQueryVo.getName(); // 教师级别 Integer level = teacherQueryVo.getLevel(); // 开始时间 String joinDateBegin = teacherQueryVo.getJoinDateBegin(); // 结束时间 String joinDateEnd = teacherQueryVo.getJoinDateEnd(); // 进行非空判断,条件封装 QueryWrapper<Teacher> wrapper = new QueryWrapper<>(); if (StringUtils.hasLength(name)) { wrapper.like("name", name); } if (!ObjectUtils.isEmpty(level)) { wrapper.eq("level", level); } if (StringUtils.hasLength(joinDateBegin)) { wrapper.ge("join_date", joinDateBegin); } if (StringUtils.hasLength(joinDateEnd)) { wrapper.le("join_date", joinDateEnd); } //调用方法得到分页查询结果 IPage<Teacher> teacherPageModel = teacherService.page(teacherPageParam, wrapper); // 返回结果 return Result.ok(teacherPageModel); } } }
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.model.vod.Teacher; import com.myxh.smart.planet.result.Result; import com.myxh.smart.planet.vod.service.TeacherService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @Tag(name = "教师接口", description = "教师管理接口") @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 添加教师 * * @param teacher 教师数据 * @return Result 全局统一返回结果 */ @Operation(summary = "添加", description = "添加教师") @PostMapping("save") public Result<Void> saveTeacher(@RequestBody Teacher teacher) { boolean isSuccess = teacherService.save(teacher); if (isSuccess) { return Result.ok(null); } else { return Result.fail(null); } } }
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.model.vod.Teacher; import com.myxh.smart.planet.result.Result; import com.myxh.smart.planet.vod.service.TeacherService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @Tag(name = "教师接口", description = "教师管理接口") @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 根据 id 查询教师 * * @param id id * @return Result 全局统一返回结果 */ @Operation(summary = "查询", description = "根据 id 查询教师") @GetMapping("get/{id}") public Result<Teacher> getTeacher(@Parameter(name = "id", description = "ID", required = true) @PathVariable Long id) { Teacher teacher = teacherService.getById(id); return Result.ok(teacher); } }
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.model.vod.Teacher; import com.myxh.smart.planet.result.Result; import com.myxh.smart.planet.vod.service.TeacherService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @Tag(name = "教师接口", description = "教师管理接口") @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 修改教师 * * @param teacher 教师数据 * @return Result 全局统一返回结果 */ @Operation(summary = "修改", description = "修改教师") @PostMapping("update") public Result<Void> updateTeacher(@RequestBody Teacher teacher) { boolean isSuccess = teacherService.updateById(teacher); if (isSuccess) { return Result.ok(null); } else { return Result.fail(null); } } }
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.result.Result; import com.myxh.smart.planet.vod.service.TeacherService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @Tag(name = "教师接口", description = "教师管理接口") @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 批量删除教师 * * @param idList id 数组,Json 数组 [1,2,3,...] * @return Result 全局统一返回结果 */ @Operation(summary = "批量删除", description = "批量删除教师") @DeleteMapping("remove/batch") public Result<Void> removeBatchTeacher(@RequestBody List<Long> idList) { boolean isSuccess = teacherService.removeByIds(idList); if (isSuccess) { return Result.ok(null); } else { return Result.fail(null); } } }
除以 0。
// 模拟异常
int n = 10 / 0;
想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要统一异常处理。
在 service-utils 中创建统一异常处理类 GlobalExceptionHandler.java:
package com.myxh.smart.planet.exception; import com.myxh.smart.planet.result.Result; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; /** * @author MYXH * @date 2023/9/30 */ // 底层通过 AOP 实现 @ControllerAdvice public class GlobalExceptionHandler { /** * 全局异常处理 * * @return Result 全局统一返回结果 */ @ExceptionHandler(Exception.class) @ResponseBody public Result error(Exception e) { e.printStackTrace(); return Result.fail(null).message("执行全局异常处理"); } }
GlobalExceptionHandler.java 中添加:
package com.myxh.smart.planet.exception; import com.myxh.smart.planet.result.Result; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; /** * @author MYXH * @date 2023/9/30 */ // 底层通过 AOP 实现 @ControllerAdvice public class GlobalExceptionHandler { /** * 特定异常处理 * * @return Result 全局统一返回结果 */ @ExceptionHandler(ArithmeticException.class) @ResponseBody public Result error(ArithmeticException e) { e.printStackTrace(); return Result.fail(null).message("执行 ArithmeticException 特定异常处理"); } }
package com.myxh.smart.planet.exception; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @author MYXH * @date 2023/9/30 */ @NoArgsConstructor @AllArgsConstructor @Data public class SmartPlanetException extends RuntimeException { private Integer code; private String message; }
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.model.vod.Teacher; import com.myxh.smart.planet.result.Result; import com.myxh.smart.planet.vod.service.TeacherService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @author MYXH * @date 2023/9/29 * * <p> * 教师 前端控制器 * </p> */ @Tag(name = "教师接口", description = "教师管理接口") @RestController @RequestMapping("/admin/vod/teacher") public class TeacherController { @Autowired private TeacherService teacherService; /** * 查询所有教师 * * @return Result 全局统一返回结果 */ @Operation(summary = "查询所有", description = "查询所有教师") @GetMapping("find/all") public Result<List<Teacher>> findAllTeacher() { try { // 模拟异常 int n = 10 / 0; } catch (Exception e) { // 抛出异常 throw new SmartPlanetException(201, "执行 SmartPlanetException 自定义异常处理"); } List<Teacher> teacherList = teacherService.list(); return Result.ok(teacherList).message("查询所有教师数据成功"); } }
GlobalExceptionHandler.java 中添加
package com.myxh.smart.planet.exception; import com.myxh.smart.planet.result.Result; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; /** * @author MYXH * @date 2023/9/30 */ // 底层通过 AOP 实现 @ControllerAdvice public class GlobalExceptionHandler { /** * SmartPlanetException 自定义异常处理 * * @return Result 全局统一返回结果 */ @ExceptionHandler(SmartPlanetException.class) @ResponseBody public Result error(SmartPlanetException e) { e.printStackTrace(); return Result.fail(null).code(e.getCode()).message(e.getMessage()); } }
前端工程师“Front-End-Developer”源自于美国。大约从 2005 年开始正式的前端工程师角色被行业所认可,到了 2010 年,互联网开始全面进入移动时代,前端开发的工作越来越重要。
最初所有的开发工作都是由后端工程师完成的,随着业务越来越繁杂,工作量变大,于将项目中的可视化部分和一部分交互功能的开发工作剥离出来,形成了前端开发。
由于互联网行业的急速发展,导致了在不同的国家,有着截然不同的分工体制。
在日本和一些人口比较稀疏的国家,例如加拿大、澳洲等,流行“Full-Stack Engineer”,也就是通常所说的全栈工程师。通俗点说就是一个人除了完成前端开发和后端开发工作以外,有的公司从产品设计到项目开发再到后期运维可能都是同一个人,甚至可能还要负责 UI、配动画、写文档等等。
而在美国等互联网环境比较发达的国家项目开发的分工协作更为明确,整个项目开发分为前端、中间层和后端三个开发阶段,这三个阶段分别由三个或者更多的人来协同完成。
国内的大部分互联网公司只有前端工程师和后端工程师,中间层的工作有的由前端来完成,有的由后端来完成。
PRD(产品原型-产品经理)、PSD(视觉设计-UI 工程师)、HTML/CSS/JavaScript(PC/移动端网页,实现网页端的视觉展示和交互-前端工程师)。
https://code.visualstudio.com/
为方便后续开发,建议安装如下插件
VS Code 本身没有新建项目的选项,所以要先创建一个空的文件夹,如 project_xxx。
然后打开 VS Code,再在 VS Code 里面选择 File -> Open Folder 打开文件夹,这样才可以创建项目。
打开文件夹后,选择“文件 -> 将工作区另存为…”,为工作区文件起一个名字,存储在刚才的文件夹下即可。
以文件路径方式打开网页预览:
需要安装“open in browser”插件:
文件右键 -> Open In Default Browser
以服务器方式打开网页预览:
需要安装“Live Server”插件:
文件右键 -> Open with Live Server
左边栏 Manage -> settings -> 搜索 “font” -> Font size
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系?
要讲清楚这个问题,需要回顾历史。1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。
因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。
ECMAScript 2015(简称 ES2015)这个词,也是经常可以看到的。它与 ES6 是什么关系呢?
2011 年,ECMAScript 5.1 版发布后,就开始制定 6.0 版了。因此,ES6 这个词的原意,就是指 JavaScript 语言的下一个版本。
ES6 的第一个版本,在 2015 年 6 月发布,正式名称是《ECMAScript 2015 标准》(简称 ES2015)。
2016 年 6 月,小幅修订的《ECMAScript 2016 标准》(简称 ES2016)如期发布,这个版本可以看作是 ES6.1 版,因为两者的差异非常小,基本上是同一个标准。根据计划,2017 年 6 月发布 ES2017 标准。
因此,ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准。
ES 标准中不包含 DOM 和 BOM 的定义,只涵盖基本数据类型、关键字、语句、运算符、内建对象、内建函数等通用语法。
本部分只学习前端开发中 ES6 的最少必要知识,方便后面项目开发中对代码的理解。
创建 let.html。
/*
1、
var 声明的变量没有局部作用域
let 声明的变量有局部作用域
*/
{
var a = 0;
let b = 1;
}
// 0
console.log(a);
// ReferenceError: b is not defined
console.log(b);
/*
2、
var 可以声明多次
let 只能声明一次
*/
var m = 1;
var m = 2;
let n = 3;
let n = 4;
// 2
console.log(m);
// Identifier 'n' has already been declared
console.log(n);
创建 const.html。
// 1、声明之后不允许改变
const PI = "3.1415926";
// TypeError: Assignment to constant variable.
PI = 3.14;
// 2、一但声明必须初始化,否则会报错
// SyntaxError: Missing initializer in const declaration
const MY_NAME
创建解构赋值.html。
解构赋值是对赋值运算符的扩展。
是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。
// 对象解构
let user = { name: "MYXH", age: 21 };
// 1、传统
let myName = user.name;
let myAge = user.age;
console.log(name1, age1);
// 2、ES6
//注意:结构的变量必须是 user 中的属性
let { name, age } = user;
console.log(name, age);
创建模板字符串.html。
模板字符串相当于加强版的字符串,用反引号 `,除了作为普通字符串,还可以在字符串中加入变量和表达式。
// 1、字符串插入变量和表达式,变量名写在 ${} 中,${} 中可以放入 JavaScript 表达式
let name = "MYXH";
let age = 21;
let info = `My Name is ${name},I am ${age + 1} years.`;
// My Name is MYXH,I am 28 years.
console.log(info);
// 2、字符串中调用函数
function f() {
return "have fun!";
}
let string = `Game start,${f()}`;
// Game start,have fun!
console.log(string);
创建定义方法简写.html。
// 1、传统 const person1 = { sayHi: function () { console.log("Hi!"); }, }; //"Hi!" person1.sayHi(); // 2、ES6 const person2 = { sayHi() { console.log("Hi!"); }, }; //"Hi!" person2.sayHi();
创建对象拓展运算符.html。
拓展运算符(…)用于取出参数对象所有可遍历属性然后拷贝到当前对象。
// 1、拷贝对象
let person1 = { name: "MYXH", age: 21 };
let person2 = { ...person1 };
//{name: "MYXH", age: 21}
console.log(person2);
创建箭头函数.html。
箭头函数提供了一种更加简洁的函数书写方式,基本语法是:参数 => 函数体
。
// 1、传统 var f1 = function (a) { return a; }; console.log(f1(1)); // 2、ES6 var f2 = (a) => a; console.log(f2(1)); /* 当箭头函数没有参数或者有多个参数,要用 () 括起来。 当箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块, 当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。 */ var f3 = (a, b) => { let result = a + b; return result; }; // 3 console.log(f3(1, 2)); // 前面代码相当于: var f4 = (a, b) => a + b; // 3 console.log(f4(1, 2));
箭头函数多用于匿名函数的定义。
Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。
Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
官方网站:https://cn.vuejs.org
创建 demo.html。
<!-- id 标识 vue 作用的范围 --> <div id="app"> <!-- {{}} 插值表达式,绑定 vue 中的 data 数据 --> {{ message }} </div> <script src="vue.min.js"></script> <script> // 创建一个 vue 对象 new Vue({ // 绑定 vue 作用的范围 el: "#app", data: { // 定义页面中显示的模型数据 message: "Hello Vue!", }, }); </script>
这就是声明式渲染:Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。
这里的核心思想就是没有繁琐的 DOM 操作,例如 jQuery 中,需要先找到 div 节点,获取到 DOM 对象,然后进行一系列的节点操作。
在 vs code 中创建代码片段:
文件 -> 首选项 -> 用户代码片段 -> 新建全局代码片段或文件夹代码片段:vue-html.code-snippets
注意:制作代码片段的时候,字符串中如果包含文件中复制过来的“Tab”键的空格,要换成“空格键”的空格。
{ "vue html": { "scope": "html", "prefix": "vue-html", "body": [ "<!DOCTYPE html>", "<html lang=\"en\">", "<head>", " <meta charset=\"UTF-8\">", " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">", " <title>Document</title>", "</head>", "", "<body>", " <div id=\"app\">", "", " </div>", "", " <script src=\"vue.min.js\"></script>", " <script>", " new Vue({", " el: '#app',", " data: {", " $1", " }", " })", " </script>", "</body>", "</html>" ], "description": "my vue template in html" } }
创建 01-基本数据渲染和指令.html。
你看到的 v-bind 特性被称为指令。指令带有前缀 v-。
除了使用插值表达式{{}}进行数据渲染,也可以使用 v-bind 指令,它的简写的形式就是一个冒号:。
data: {
content: '我是标题',
message: '页面加载于 ' + new Date().toLocaleString()
}
<!--
如果要将模型数据绑定在 html 属性中,则使用 v-bind 指令
此时 title 中显示的是模型数据
-->
<h1 v-bind:title="message">{{content}}</h1>
<!-- v-bind 指令的简写形式:冒号: -->
<h1 :title="message">{{content}}</h1>
创建 02-双向数据绑定.html。
双向数据绑定和单向数据绑定:使用 v-model 进行双向数据绑定。
data: {
searchMap: {
keyWord: "MYXH";
}
}
<!-- v-bind:value 只能进行单向的数据渲染 -->
<input type="text" v-bind:value="searchMap.keyWord" />
<!-- v-model 可以进行双向的数据绑定 -->
<input type="text" v-model="searchMap.keyWord" />
<p>您要查询的是:{{searchMap.keyWord}}</p>
创建 03-事件.html。
需求: 点击查询按钮,调用方法。
增加 methods 节点 并定义 search 方法。
data: {
},
methods:{
search(){
console.log('search')
}
}
html 中增加 button 和 p。
使用 v-on 进行数件处理,v-on:click 表示处理鼠标点击事件,事件调用的方法定义在 vue 对象声明的 methods 节点中。
<!-- v-on 指令绑定事件,click 指定绑定的事件类型,事件发生时调用 vue 中 methods 节点中定义的方法 -->
<button v-on:click="search()">查询</button>
简写:
<!-- v-on 指令的简写形式 @ -->
<button @click="search()">查询</button>
创建 04-条件渲染.html。
v-if:条件指令。
data: {
ok: false;
}
注意:单个复选框绑定到布尔值。
<input type="checkbox" v-model="ok" />同意许可协议
<!-- v:if 条件指令:还有v-else、v-else-if ,要注意性能开销 -->
<h1 v-if="ok">这是一个通用的中文占位文本短语,用于表示占位文本的内容。</h1>
<h1 v-else>no</h1>
创建 05-列表渲染.html。
v-for:列表循环指令。
例 1:遍历数据列表。
data: {
userList: [
{ id: 1, username: "helen" },
{ id: 2, username: "peter" },
{ id: 3, username: "andy" },
];
}
<!-- 遍历数据列表 -->
<table border="1">
<!-- <tr v-for="item in userList"></tr> -->
<tr v-for="(item, index) in userList">
<td>{{index}}</td>
<td>{{item.id}}</td>
<td>{{item.username}}</td>
</tr>
</table>
创建 vue 实例的生命周期.html。
<h1 id="h1">{{ message }}</h1>
分析生命周期相关方法的执行时机
data: { message: 'Vue 生命周期' }, // 在渲染之前执行 created() { console.log("created...") }, // 在渲染之后执行 mounted() { console.log("mounted...") }, methods: { }
组件(Component)是 Vue.js 最强大的功能之一。
组件可以扩展 HTML 元素,封装可重用的代码。组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树。
var app = new Vue({
el: "#app",
// 定义局部组件,这里可以定义多个局部组件
components: {
// 组件的名字
Navbar: {
// 组件的内容
template: "<ul><li>首页</li><li>用户管理</li></ul>",
},
},
});
<div id="app">
<Navbar></Navbar>
</div>
Vue.js 路由允许通过不同的 URL 访问不同的内容。
通过 Vue.js 可以实现多视图的单页 Web 应用(single page web application,SPA)。
Vue.js 路由需要载入 vue-router 库。
创建 路由.html。
<script src="vue.min.js"></script>
<script src="vue-router.min.js"></script>
<div id="app"> <h1>Hello App!</h1> <p> <!-- 使用 router-link 组件来导航 --> <!-- 通过传入 to 属性指定链接 --> <!-- <router-link> 默认会被渲染成一个 <a> 标签 --> <router-link to="/">首页</router-link> <router-link to="/student">学生管理</router-link> <router-link to="/teacher">教师管理</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div>
<script> // 1、定义(路由)组件 // 可以从其他文件 import 进来 const Welcome = { template: "<div>欢迎</div>" }; const Student = { template: "<div>student list</div>" }; const Teacher = { template: "<div>teacher list</div>" }; // 2、定义路由 // 每个路由应该映射一个组件 const routes = [ // 设置默认指向的路径 { path: "/", redirect: "/welcome" }, { path: "/welcome", component: Welcome }, { path: "/student", component: Student }, { path: "/teacher", component: Teacher }, ]; // 3、创建 router 实例,然后传 routes 配置 const router = new VueRouter({ // 缩写,相当于 routes: routes routes, }); // 4、创建和挂载根实例 // 从而让整个应用都有路由功能 const app = new Vue({ el: "#app", router, }); // 现在,应用已经启动了 </script>
axios 是独立于 vue 的一个项目,基于 promise 用于浏览器和 node.js 的 http 客户端。
在浏览器中可以完成 ajax 请求的发送。
在 node.js 中可以向远程接口发送请求。
<script src="vue.min.js"></script>
<script src="axios.min.js"></script>
var app = new Vue({ el: "#app", data: { // 数组 memberList: [], }, created() { this.getList(); }, methods: { getList(id) { // vm = this axios .get("data.json") .then((response) => { console.log(response); this.memberList = response.data.data.items; }) .catch((error) => { console.log(error); }); }, }, });
控制台查看输出。
<div id="app">
<table border="1">
<tr>
<td>id</td>
<td>姓名</td>
</tr>
<tr v-for="item in memberList">
<td>{{item.memberId}}</td>
<td>{{item.nickname}}</td>
</td>
</tr>
</table>
</div>
element-ui 是饿了么前端出品的基于 Vue.js 的 后台组件库,方便程序员进行页面快速布局和构建。
官网: http://element-cn.eleme.io/#/zh-CN
ui 相关组件在项目中学习。
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个事件驱动 I/O 服务端 JavaScript 环境,基于 Google 的 V8 引擎,V8 引擎执行 Javascript 的速度非常快,性能非常好。
如果你是一个前端程序员,你不懂得像 PHP、Python 或 Ruby 等动态编程语言,然后你想创建自己的服务,那么 Node.js 是一个非常好的选择。
Node.js 是运行在服务端的 JavaScript,如果你熟悉 Javascript,那么你将会很容易的学会 Node.js。
当然,如果你是后端程序员,想部署一些高性能的服务,那么学习 Node.js 也是一个非常好的选择。
官网:https://nodejs.org/en/
中文网:http://nodejs.cn/
LTS:长期支持版本。
Current:最新版。
node -v
创建控制台程序.js。
console.log("Hello Node.js");
进入到程序所在的目录,输入:
node 控制台程序.js
浏览器的内核包括两部分核心:
DOM 渲染引擎;
js 解析器(js 引擎),
js 运行在浏览器中的内核中的 js 引擎内部。
Node.js 是脱离浏览器环境运行的 JavaScript 程序,基于 V8 引擎(Chrome 的 JavaScript 的引擎)。
NPM 全称 Node Package Manager,是 Node.js 包管理工具,是全球最大的模块生态系统,里面所有的模块都是开源免费的,也是 Node.js 的包管理工具,相当于前端的 Maven。
通过 npm 可以很方便地下载 js 库,管理前端工程。
Node.js 默认安装的 npm 包和工具的位置:Node.js 目录\node_modules
# 在命令提示符输入 npm -v 可查看当前 npm 版本
npm -v
# 建立一个空文件夹,在命令提示符进入该文件夹,执行命令初始化
npm init
# 按照提示输入相关信息,如果是用默认值则直接回车即可
# name: 项目名称
# version: 项目版本号
# description: 项目描述
# keywords: {Array} 关键词,便于用户搜索到的项目
# 最后会生成 package.json 文件,这个是包的配置文件,相当于 maven 的 pom.xml
# 之后也可以根据需要进行修改。
# 如果想直接生成 package.json 文件,那么可以使用命令
npm init -y
NPM 官方的管理的包都是从 http://npmjs.com 下载的,但是这个网站在国内速度很慢。
这里推荐使用淘宝 NPM 镜像 http://npm.taobao.org/ ,淘宝 NPM 镜像是一个完整 npmjs.com 镜像,同步频率目前为 10 分钟一次,以保证尽量与官方服务同步。
设置镜像地址:
# 经过下面的配置,以后所有的 npm install 都会经过淘宝的镜像地址下载
npm config set registry https://registry.npm.taobao.org
# 查看 npm 配置信息
npm config list
# 使用 npm install 安装依赖包的最新版 # 模块安装的位置:项目目录\node_modules # 安装会自动在项目目录下添加 package-lock.json 文件,这个文件帮助锁定安装包的版本 # 同时 package.json 文件中,依赖包会被添加到 dependencies 节点下,类似 maven 中的 <dependencies> npm install jquery # npm管理的项目在备份和传输的时候一般不携带 node_modules 文件夹 # 根据 package.json 中的配置下载依赖,初始化项目 npm install # 如果安装时想指定特定的版本 npm install jquery@2.1.x # 局部安装 # devDependencies节点:开发时的依赖包,项目打包到生产环境的时候不包含的依赖 # 使用 -D参数将依赖添加到 devDependencies 节点 npm install --save-dev eslint # 或 npm install -D eslint # 全局安装 # Node.js 全局安装的 npm 包和工具的位置:用户目录\AppData\Roaming\npm\node_modules # 一些命令行工具常使用全局安装的方式 npm install -g webpack # 或 npm install --global webpack
# 更新包(更新到最新版本)
npm update 包名
# 全局更新
npm update -g 包名
# 卸载包
npm uninstall 包名
# 全局卸载
npm uninstall -g 包名
随着网站逐渐变成"互联网应用程序",嵌入网页的 Javascript 代码越来越庞大,越来越复杂。
Javascript 模块化编程,已经成为一个迫切的需求。理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。
但是,Javascript 不是一种模块化编程语言,它不支持"类"(class),包(package)等概念,更遑论"模块"(module)了。
传统非模块化开发有如下的缺点:
命名冲突。
文件依赖。
模块化规范:
CommonJS 模块化规范。
ES6 模块化规范。
每个文件就是一个模块,有自己作用域。在一个文件里定义的变量、函数、类,都是私有的,对其他文件不可见。
创建 es5/四则运算.js。
// 定义成员
const sum = function (a, b) {
return parseInt(a) + parseInt(b);
};
const subtract = function (a, b) {
return parseInt(a) - parseInt(b);
};
导出模块中的成员。
// 导出成员
module.exports = {
sum: sum,
subtract: subtract,
};
简写:
// 简写
module.exports = {
sum,
subtract,
};
创建 es5/引入模块.js。
// 引入模块,注意:当前路径必须写 ./
const m = require("./四则运算.js");
console.log(m);
const result1 = m.sum(1, 2);
const result2 = m.subtract(2, 1);
console.log(result1, result2);
node es5/引入模块.js
CommonJS 使用 exports 和 require 来导出、导入模块。
ES6 使用 export 和 import 来导出、导入模块。
创建 es6/userApi1.js。
export function getList() {
console.log("获取数据列表1");
}
export function save() {
console.log("保存数据1");
}
创建 es6/userComponent1.js。
// 只取需要的方法即可,多个方法用逗号分隔
import { getList, save } from "./userApi1.js";
getList();
save();
注意:这时程序无法运行,因为 ES6 的模块化无法在 Node.js 中执行,需要用 Babel 编辑成 ES5 后再执行。
Babel 是一个广泛使用的转码器,可以将 ES6 代码转为 ES5 代码,从而在现有环境执行执行。
安装命令行转码工具。
Babel 提供 babel-cli 工具,用于命令行转码。它的安装命令如下:
npm install -global @babel/cli
# 查看是否安装成功
babel --version
Babel 的配置文件是 .babelrc,存放在项目的根目录下,该文件用来设置转码规则和插件,presets 字段设定转码规则,将 es2015 规则加入 .babelrc:
{
"presets": ["es2015"],
"plugins": []
}
在项目中安装。
npm install --save-dev babel-preset-es2015
# 整个目录转码
mkdir dist1
# --out-dir 或 -d 参数指定输出目录
babel es6 -d dist1
node dist1/userComponent1.js
创建 es6/userApi2.js。
export default {
getList() {
console.log("获取数据列表2");
},
save() {
console.log("保存数据2");
},
};
创建 es6/userComponent2.js。
import user from "./userApi2.js";
user.getList();
user.save();
# 整个目录转码
mkdir dist2
# --out-dir 或 -d 参数指定输出目录
babel es6 -d dist2
node dist2/userComponent2.js
vue-admin-template 是基于 vue-element-admin 的一套后台管理系统基础模板(最少精简版),可作为模板进行二次开发。
GitHub 地址:https://github.com/PanJiaChen/vue-admin-template
# 解压压缩包
# 进入目录
cd vue-admin-template
# 安装依赖
npm install
# 启动执行后,浏览器自动弹出并访问 http://localhost:9528/
npm run dev
创建 LoginController
package com.myxh.smart.planet.vod.controller; import com.myxh.smart.planet.result.Result; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; /** * @author MYXH * @date 2023/10/2 */ @RestController @RequestMapping("/admin/vod/user") // 允许跨域请求 @CrossOrigin public class UserLoginController { /** * login 用户登录 * * @return Result 全局统一返回结果 */ @PostMapping("login") public Result<Map<String, Object>> login() { // 前端接口:{"code":20000,"data":{"token":"admin-token"}} Map<String, Object> data = new HashMap<>(); data.put("token", "admin-token"); return Result.ok(data); } /** * info 用户信息 * * @return Result 全局统一返回结果 */ @GetMapping("info") public Result<Map<String, Object>> info() { /* 前端接口: { "code":20000, "data": { "roles":["admin"], "introduction":"I am a super administrator", "avatar":"https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif" "name":"Super Admin" } } */ Map<String, Object> data = new HashMap<>(); data.put("roles", "admin"); data.put("introduction", "我是一名超级管理员"); data.put("avatar", "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif"); data.put("name", "超级管理员"); return Result.ok(data); } /** * logout 退出登录 * * @return Result 全局统一返回结果 */ @PostMapping("logout") public Result<Void> logout() { // 前端接口:{"code":20000,"data":null} return Result.ok(null); } }
import request from "@/utils/request"; export function login(data) { return request({ // url: '/vue-admin-template/user/login', url: "/admin/vod/user/login", method: "post", data, }); } export function getInfo(token) { return request({ // url: '/vue-admin-template/user/info', url: "/admin/vod/user/info", method: "get", params: { token }, }); } export function logout() { return request({ // url: '/vue-admin-template/user/logout', url: "/admin/vod/user/logout", method: "post", }); }
(1)浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域 。前后端分离开发中,需要考虑 ajax 跨域的问题。
(2)跨域的本质:浏览器对 Ajax 请求的一种限制。
(3)这里可以从服务端解决这个问题。
在 Controller 类上添加注解
// 允许跨域请求
@CrossOrigin
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。