开山篇
- 了解 基于springboot官网创建项目,并导入运行 https://spring.io/projects/spring-boot

通过idea联网版创建springboot项目,web项目,并启动注意 parent�使用2.7.1
基于阿里云创建项目 https://start.aliyun.com
- 手动创建SpringBoot项目,创建maven工程并导入springboot依赖,创建引导类和处理器启动即可
- 文件隐藏
解析spring-boot-starter-parent,主要使用来做统一的jar包版本管理,版本仲裁
开发SpringBoot程序要继承spring-boot-starter-parent
2. spring-boot-starter-parent中定义了若干个依赖管理
3. 继承parent模块可以避免多个依赖使用相同技术时出现依赖版本冲突
4. 继承parent的形式也可以采用引入依赖的形式实现效果- 解析starter,快速启动器
spring-boot-starter-xxx
按照功能不同会有不同功能的starter,我们可以把starter理解成一个集成了多个依赖的pom文件是一个封装好的依赖集合。
1. 开发SpringBoot程序需要导入坐标时通常导入对应的starter
2. 每个不同的starter根据功能不同,通常包含多个依赖坐标
3. 使用starter可以实现快速配置的效果,达到简化配置的目的
- 解析starter,快速启动器
parent的目的是避免版本冲突,starter的目的是简化依赖配置
8.解析引导类,其实就是通过SpringApplication.run(Application.class, args);启动容器,扫描引导类所在包加载bean
9.解析内嵌服务器,tomcat,jetty
<dependencies><dependency><groupid>org.springframework.boot</groupid><artifactid>spring-boot-starter-web</artifactid><exclusions><!-- 去除Tomcat容器 --><exclusion><groupid>org.springframework.boot</groupid><artifactid>spring-boot-starter-tomcat</artifactid></exclusion></exclusions></dependency><!-- 增加Jetty容器 --><dependency><groupid>org.springframework.boot</groupid><artifactid>spring-boot-starter-jetty</artifactid></dependency></dependencies>
配置
1.复制模块,因为练习需要大量的模块,所以复制工程
原则
保留工程基础结构
抹掉原始工程痕迹
1. 在工作空间中复制对应工程,并修改工程名称
2. 删除与Idea相关配置文件,仅保留src目录与pom.xml文件
3. 修改pom.xml文件中的artifactId与新工程/模块名相同
4. 删除name标签(可选)
5. 保留备份工程供后期使用
2.基础配置
properties配置和yaml的使用方式.wmv
使用properties的方式修改端口,修改banner,日志,缺点是代码可读性低,配置冗长
然后引入yml,yaml的方式配置,以及三种配置并存的优先级:properties > yml > yaml
以及yaml提示不生效的原因:是因为idea不认为他是一个配置文件,在项目结构中进行配置即可
b31477f1bac957aaa9bce1f099dc2b86.rar
3.yml的使用
yaml数据格式
yml中的数据格式.wmv
YAML(YAML Ain’t Markup Language),一种数据序列化格式
优点:
容易阅读
容易与脚本语言交互
以数据为核心,重数据轻格式
YAML文件扩展名
.yml(主流)
.yaml
yaml语法规则
大小写敏感属性
层级关系使用多行描述,每行结尾使用冒号结束
使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
# 表示注释
核心规则:数据前面要加空格与冒号隔开
#单一属性name: gouwaage: 18#对象user:name: goudanage: 18#数组like:- 抽烟- 喝酒- 烫头#数组缩略写法like2: [抽烟,喝酒,烫头]#对象数组users:- name: goushengage: 18- name: goudanage: 20#对象数组 格式2users2:-name: goushengage: 18-name: goudanage: 20#对象数组缩略格式user3: [{name: gouwa,age: 18},{name: goudan,age: 18}]
读取数据
yaml属性的读取方式.wmv
使用@Value读取单个数据,属性名引用方式:${一级属性名.二级属性名……}
1. 使用@Value配合SpEL读取单个数据
2. 如果数据存在多层级,依次书写层级名称即可
@Value("${name}")private String name;@Value("${user.age}")private String age;@Value("${like[1]}")private String like;@Value("${users[1].name}")private String name2;@RequestMapping("/value")public String getValue(){return "name:"+name+",age:"+age+",like:"+like+",name2:"+name2;}
引用变量
center:dataDir: /usr/local/fire/datatmpDir: /usr/local/fire/tmplogDir: /usr/local/fire/logmsgDir: /usr/local/fire/msgDir
如上👆🏻:如果配置中值出现大量重复的内容,则可以把他们提取成一个变量进行引用,如下:↓
basedir: /usr/local/firecenter:dataDir: ${basedir}/datatmpDir: ${basedir}/tmplogDir: ${basedir}/logmsgDir: ${basedir}/msgDir
�如果出现转义字符可以使用双引号包裹进行解析
转义字符的解析方式.wmv
basedir: /usr/local/firecenter:dataDir: ${basedir}/datatmpDir: "${basedir}/tmp \t1 \t2 \t3"logDir: ${basedir}/logmsgDir: ${basedir}/msgDir
@Value("${center.tmpDir}")private String tmpDir;@RequestMapping("/value")public String getValue(){System.out.println("tmpDir:"+tmpDir);return "name:"+name+",age:"+age+",like:"+like+",name2:"+name2;}
�
读取yaml全部属性数据
目前来说上一章引用变量的方式存在明显的痛点,因为如果想要引用一个属性就需要创建一个变量,随着属性的增多代码量也会增多
可以 1. 使用Environment对象封装全部配置信息 2. 使用@Autowired自动装配数据到Environment对象中
Environment.wmv
@Autowiredprivate Environment en;@RequestMapping("/en")public String getEn(){System.out.println(en.getProperty("user.name"));System.out.println(en.getProperty("like[0]"));System.out.println(en.getProperty("user3[0].name"));return null;}
�
:::info
确实解决了变量过多的问题,但是我们会发现用起来其实并不简单,在实际的开发中也是SpringBoot内部引入配置属性的主流方式其实是把一组数据封装到一个对象中,诸君请往下看↓
:::
重点自定义对象封装指定数据 主流方式
1.定义一组数据
datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/xuezhi?serverTimezone=UTCusername: rootpassword: root
�2.创建一个类,用于封装上面的这组数据
@ConfigurationProperties.wmv
:::info
1.使用@ConfigurationProperties注解指定前缀绑定配置信息到封装类中,注意数据的名字要和类的属性名一致
2.封装类需要定义为Spring管理的bean(@Component),否则无法进行属性注入
:::
package com.xuezhi.pojo;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;// 定义数据模型封装yaml中的对应数据// 定义为让Spring管控的Bean@Component// 指定加载的数据@ConfigurationProperties(prefix = "datasource")public class DataSource {private String driverClassName;private String url;private String userName;private String password;public String getDriverClassName() {return driverClassName;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return "DataSource{" +"driverClassName='" + driverClassName + '\'' +", url='" + url + '\'' +", userName='" + userName + '\'' +", password='" + password + '\'' +'}';}}
�测试:
@Autowiredprivate DataSource dataSource;@RequestMapping("/db")public String getDb(){System.out.println(dataSource);return null;}
:::info
总结:
1.我们要想封装局部数据,首先需要提供一组对象格式的数据
2.然后提供一个用来封装的模型类,这个模型类通过@ConfigurationProperties注解指定封装的是那一组数据,把前缀告诉他就行了
3.我们的数据是由springboot读取的,那么模型类也需要交给Spring管控,使用@Component注解SpringBoot会自动把模型类扫描进Spring容器
这种数据封装的方式应用场景非常多,后面我们会定义格式各样的数据,交给SpringBoot框架中的技术使用,这些技术本身有一些配置信息,我们想改就可以把这些配置信息数据定义出来,他就会读取成为一个对象去使用,这也是为什么我们在yaml中配置一个东西在一些技术上就生效了的原因。其实就是通过这种形式把我们定义的一组一组的配置,加载成了一个又一个的对象,提供给一个又一个的技术去使用的。
:::
整合第三方技术
我们要学习的是整合思想,其中整合JUnit是最简单的整合方式,通过学习这四种技术的整合方式,我们一通百通
整合JUnit
整合Junit.wmv
直接创建一个maven工程进行测试即可,测试所需的所有东西都自动配置好了
什么技术都不需要勾选,直接点击下一步

创建用于测试的内容
在测试类中,通过自动装配注入测试对象,进行测试即可
:::info
springboot整合jUnit主要是通过@SpringBootTest注解,比起原始Spring的方式方便很多,具体步骤如下:
1. 导入测试对应的starter
2. 测试类使用@SpringBootTest修饰
3. 使用自动装配的形式添加要测试的对象
:::
:::info
注意:
1. 测试类如果存在于引导类所在包或子包中无需指定引导类
2. 测试类如果不存在于引导类所在的包或子包中需要通过classes 属性指定引导类
:::
比如我们把测试类提到com包下,而不放在和引导类所在的com.xuezhi包下
那么这时他就会抛出异常:
java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test
这个异常的意思是在当前包下没有找到使用@SpringBootConfiguration修饰的类(其实就是引导类,引导类使用了@SpringBootConfiguration修饰),我们的Spring容器要通过启动引导类生成,找不到Spring容器,就导致了自动装配失败,所以报错,异常信息往后看可以看到它提示我们使用@ContextConfiguration或者@SpringBootTest(classes=…)指定引导类解决该问题。那么我们只需要如下:
:::info
通过@SpringBootTest的classes属性手动指定引导类,从而加载Spring容器
:::
:::info
或者如原始Spring一样使用@ContextConfiguration手动指定引导类,从而加载Spring容器
:::

这两种方式都可以,产生这个问题的原因在于,测试类如果不存在于引导类所在的包或子包中则无法找到Spring容器从而导致了自动装配失败
整合MyBatis
整合MyBatis.wmv
:::info
知识加油站:@Mapper
如果Mapper.xml与Mapper.class在同一个包下且同名,spring的Mapper组件扫描器扫描Mapper.class的同时会自动扫描同名的Mapper.xml并装配到Mapper.class。
如果Mapper.xml与Mapper.class不在同一个包下或者不同名,就必须使用配置mapperLocations指定mapper.xml的位置。而使用了@Mapper注解的类采用注解开发不需要写xml映射文件,并会生成动态代理类交Mapper组件扫描器扫描即可
@Mapper是由Mybatis框架中定义的一个描述数据层接口的注解,注解往往起到的都是一个描述性作用,目的就是为了不再写mapper映射文件并被Spring容器识别到并产生自动代理的对象,并将其实现类对象存储到spring容器中。
@Mapper使用MyBatis或者搭配原始Spring去写的话是可以省略的,但是使用SpringBoot是必须要写的
搭配SpringBoot使用,@Mapper会告诉spring框架的此接口的实现类由Mybatis负责创建,并将其实现类对象存储到spring容器中。
:::
整合MyBatis只要创建一个SpringBoot项目并勾选相对应的技术栈即可
选择对应的技术栈
idea会自动帮我们导入MyBatis-spring-boot-starter的启动器,自动帮我们配置好整合的配置,我们只需要指定数据源即可
在yaml文件中配置数据源
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatisusername: rootpassword: xzjyroot
pojo:
package com.xuezhi.pojo;import lombok.Data;import lombok.ToString;import java.time.LocalDateTime;@Data@ToStringpublic class Items {private Integer id;private String name;private double price;private String detail;private String pic;private LocalDateTime createtime;}
�mapper接口:
package com.xuezhi.mapper;import com.xuezhi.pojo.Items;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Select;import java.util.List;@Mapperpublic interface ItemsMapper {@Select("select * from items")List<Items> getItems();}
测试:
package com.xuezhi;import com.xuezhi.mapper.ItemsMapper;import com.xuezhi.pojo.Items;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.List;@SpringBootTestclass SpringBoot05MyBatisApplicationTests {@Autowiredprivate ItemsMapper itemsMapper;@Testvoid contextLoads() {List<Items> items = itemsMapper.getItems();System.out.println(items);}}
2.4.1,2.3.9版本SpringBoot会出现时区和驱动过时问题
1. MySQL 8.X驱动强制要求设置时区
修改url,添加serverTimezone设定
修改MySQL数据库配置(略)
2. 驱动类过时,提醒更换为com.mysql.cj.jdbc.Driver
:::info
总结:1. 勾选MyBatis技术,也就是导入MyBatis对应的starter
2. 数据库连接相关信息转换成配置
3. 数据库SQL映射需要添加@Mapper被容器识别到
:::
整合MyBatis-Plus
整合MyBatisPllus.wmv
:::info
mp是国人开发的技术,在SpringBoot中并没有收录启动器,所以我们可以去maven的仓库自己去查找启动器的坐标,整合方式和MyBatis大同小异
:::

<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency>
:::info
总结:
1.导入对应的starter
2.数据层使用BaseMapper简化开发
3.配置数据源
4.测试
由于SpringBoot中未收录MyBatis-Plus的坐标版本,需要指定对应的Version
需要使用的第三方技术无法通过勾选确定时,需要手工添加坐标
:::
整合Druid
整合Druid.wmv
:::info
SpringBoot有默认的数据源(HikariDataSource),如果想用第三方的数据源比如Druid可以导入相应的starter,然后配置数据源即可。
:::
数据源主要是搭配数据库技术使用的所以导入MyBatis或MyBatisPlus的启动器
导入start
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.17</version></dependency>
�配置数据源有两种方式,推荐第一种
#第一种#spring:# datasource:# druid:# driver-class-name: com.mysql.cj.jdbc.Driver# url: jdbc:mysql://localhost:3306/mybatis# username: root# password: xzjyroot#第二种spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatisusername: rootpassword: xzjyroottype: com.alibaba.druid.pool.DruidDataSource
:::info
总结:
1. 整合Druid需要导入Druid对应的starter
2. 根据Druid提供的配置方式进行配置
整合第三方技术通用方式
导入对应技术的starter
根据提供的配置格式,配置非默认值对应的配置项
:::
使用SpringBoot整合SSMP案例
实体类开发————使用Lombok快速制作实体类Dao开发————整合MyBatisPlus,制作数据层测试类Service开发————基于MyBatisPlus进行增量开发,制作业务层测试类Controller开发————基于Restful开发,使用PostMan测试接口功能Controller开发————前后端开发协议制作页面开发————基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理列表、新增、修改、删除、分页、查询项目异常处理按条件查询————页面功能调整、Controller修正功能、Service修正功能
环境准备
:::info 数据库脚本 :::
DROP TABLE IF EXISTS `tbl_book`;CREATE TABLE `tbl_book` (`id` int(11) NOT NULL AUTO_INCREMENT,`type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 51 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;INSERT INTO `tbl_book` VALUES (1, '计算机理论', 'Spring实战 第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕');INSERT INTO `tbl_book` VALUES (2, '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,手写Spring精华思想');INSERT INTO `tbl_book` VALUES (3, '计算机理论', 'Spring 5 设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式');INSERT INTO `tbl_book` VALUES (4, '计算机理论', 'Spring MVC+MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手');INSERT INTO `tbl_book` VALUES (5, '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者');INSERT INTO `tbl_book` VALUES (6, '计算机理论', 'Java核心技术 卷I 基础知识(原书第11版)', 'Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新');INSERT INTO `tbl_book` VALUES (7, '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,大厂面试知识点全覆盖');INSERT INTO `tbl_book` VALUES (8, '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉');INSERT INTO `tbl_book` VALUES (9, '计算机理论', '零基础学Java(全彩版)', '零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术');INSERT INTO `tbl_book` VALUES (10, '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中');INSERT INTO `tbl_book` VALUES (11, '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍');INSERT INTO `tbl_book` VALUES (12, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');
:::info pojo :::
package com.xuezhi.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;@Data@ToString@NoArgsConstructor@AllArgsConstructorpublic class Book {private Integer id;private String type;private String name;private String description;}
:::info 创建模块,导入启动器 :::
<?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><groupId>com.xuezhi</groupId><artifactId>SpringBoot_08_SSMP</artifactId><version>0.0.1-SNAPSHOT</version><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.7.1</spring-boot.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.22</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement></project>
:::info yml :::
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatisusername: rootpassword: xzjyroottype: com.alibaba.druid.pool.DruidDataSourcemybatis-plus:global-config:db-config:# 表前缀table-prefix: tbl_# 日志configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
:::info
配置分页拦截器
:::
package com.xuezhi.config;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;@Configurationpublic class MpConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());return mybatisPlusInterceptor;}}
:::info 测试分页查询 :::
@Testpublic void test02(){Page<Book> bookPage = new Page<Book>(1, 5);Page<Book> bookPage1 = bookMapper.selectPage(bookPage, null);System.out.println(bookPage1.getRecords());}
页面
放到static目录下
页面.zip
访问pages/books.html
统一返回结果集
package com.xuezhi.pojo;import lombok.Data;@Datapublic class R {private Boolean flag;private Object data;private String msg;public R(){}public R(Boolean flag){this.flag = flag;}public R(Boolean flag,Object data){this.flag = flag;this.data = data;}public R(Boolean flag,Object data,String msg){this.flag = flag;this.data = data;this.msg = msg;}public R(Boolean flag,String msg){this.flag = flag;this.msg = msg;}public R(String msg){this.flag = false;this.msg = msg;}}
:::info
- 设计统一的返回值结果类型便于前端开发读取数据
2. 返回值结果类型可以根据需求自行设定,没有固定格式
3. 返回值结果模型类用于后端与前端进行数据格式统一,也称为前后端数据协议 :::查询列表
列表展现.wmv :::info dataList:里面放入当前页要展示的列表数据 :::
:::info
让钩子函数,在Vue对象初始化之后自动调用getAll方法,getAll使用axios发送get请求,调用then函数处理结果集,把查询出的列表数据放入到 Vue对象的dataList中,将查询数据返回到页面,利用前端数据双向绑定进行数据展示
:::
增删改
数据新增.wmv
删除操作.wmv
修改功能.wmv :::info 增删改Controller ::: ```java package com.xuezhi.controller;
import com.xuezhi.pojo.Book; import com.xuezhi.pojo.R; import com.xuezhi.service.BooksService; import org.apache.ibatis.annotations.Delete; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*;
import java.sql.SQLException;
@RestController @RequestMapping(“/books”) public class BooksController { @Autowired private BooksService booksService; @GetMapping public R getAll(){ return new R(true,booksService.list()); }
@GetMapping("/{id}")public R getById(@PathVariable Integer id){return new R(true,booksService.getById(id));}@PostMappingpublic R save(@RequestBody Book book) throws SQLException{return new R(booksService.save(book));}@DeleteMapping("/{id}")public R delete(@PathVariable Integer id){System.out.println(id);return new R(booksService.removeById(id));}@PutMappingpublic R update(@RequestBody Book books){return new R(booksService.updateById(books));}
}
:::info增删改页面逻辑:::```html<!DOCTYPE html><html><head><!-- 页面meta --><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>基于SpringBoot整合SSM案例</title><meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport"><!-- 引入样式 --><link rel="stylesheet" href="../plugins/elementui/index.css"><link rel="stylesheet" href="../plugins/font-awesome/css/font-awesome.min.css"><link rel="stylesheet" href="../css/style.css"></head><body class="hold-transition"><div id="app"><div class="content-header"><h1>图书管理</h1></div><div class="app-container"><div class="box"><div class="filter-container"><el-input placeholder="图书类别" style="width: 200px;" class="filter-item"></el-input><el-input placeholder="图书名称" style="width: 200px;" class="filter-item"></el-input><el-input placeholder="图书描述" style="width: 200px;" class="filter-item"></el-input><el-button @click="getAll()" class="dalfBut">查询</el-button><el-button type="primary" class="butT" @click="handleCreate()">新建</el-button></div><el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row><el-table-column type="index" align="center" label="序号"></el-table-column><el-table-column prop="type" label="图书类别" align="center"></el-table-column><el-table-column prop="name" label="图书名称" align="center"></el-table-column><el-table-column prop="description" label="描述" align="center"></el-table-column><el-table-column label="操作" align="center"><template slot-scope="scope"><el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button><el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button></template></el-table-column></el-table><!--分页组件--><div class="pagination-container"><el-paginationclass="pagiantion"@current-change="handleCurrentChange":current-page="pagination.currentPage":page-size="pagination.pageSize"layout="total, prev, pager, next, jumper":total="pagination.total"></el-pagination></div><!-- 新增标签弹层 --><div class="add-form"><el-dialog title="新增图书" :visible.sync="dialogFormVisible"><el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px"><el-row><el-col :span="12"><el-form-item label="图书类别" prop="type"><el-input v-model="formData.type"/></el-form-item></el-col><el-col :span="12"><el-form-item label="图书名称" prop="name"><el-input v-model="formData.name"/></el-form-item></el-col></el-row><el-row><el-col :span="24"><el-form-item label="描述"><el-input v-model="formData.description" type="textarea"></el-input></el-form-item></el-col></el-row></el-form><div slot="footer" class="dialog-footer"><el-button @click="cancel()">取消</el-button><el-button type="primary" @click="handleAdd()">确定</el-button></div></el-dialog></div><!-- 编辑标签弹层 --><div class="eidt-form"><el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit"><el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right" label-width="100px"><el-row><el-col :span="12"><el-form-item label="图书类别" prop="type"><el-input v-model="formData.type"/></el-form-item></el-col><el-col :span="12"><el-form-item label="图书名称" prop="name"><el-input v-model="formData.name"/></el-form-item></el-col></el-row><el-row><el-col :span="24"><el-form-item label="描述"><el-input v-model="formData.description" type="textarea"></el-input></el-form-item></el-col></el-row></el-form><div slot="footer" class="dialog-footer"><el-button @click="cancel()">取消</el-button><el-button type="primary" @click="handleEdit()">确定</el-button></div></el-dialog></div></div></div></div></body><!-- 引入组件库 --><script src="../js/vue.js"></script><script src="../plugins/elementui/index.js"></script><script type="text/javascript" src="../js/jquery.min.js"></script><script src="../js/axios-0.18.0.js"></script><script>var vue = new Vue({el: '#app',data:{dataList: [],//当前页要展示的列表数据dialogFormVisible: false,//添加表单是否可见dialogFormVisible4Edit:false,//编辑表单是否可见formData: {},//表单数据rules: {//校验规则type: [{ required: true, message: '图书类别为必填项', trigger: 'blur' }],name: [{ required: true, message: '图书名称为必填项', trigger: 'blur' }]},pagination: {//分页相关模型数据currentPage: 1,//当前页码pageSize:10,//每页显示的记录数total:0//总记录数}},//钩子函数,VUE对象初始化完成后自动执行created() {this.getAll();},methods: {//列表getAll() {axios.get("/books").then((rs)=>{this.dataList = rs.data.data;});},//弹出添加窗口handleCreate() {//弹出新增弹层this.dialogFormVisible = true;//重置表单this.resetForm();},//重置表单resetForm() {//重置表单数据this.formData={};},//添加handleAdd () {axios.post("/books",this.formData).then((res)=>{if(res.data.flag){//提示this.$message.success("新增成功");//关闭新增弹层this.dialogFormVisible = false;}else{this.$message.error("新增失败");}}).finally(()=>{//不管成功与否,都要重新查询列表this.getAll();})},//取消cancel(){this.dialogFormVisible = false;this.dialogFormVisible4Edit = false;this.$message.info("新增操作取消");},// 删除handleDelete(row) {this.$confirm("是否永久删除本条数据?","提示",{type:"info"}).then(()=>{axios.delete("/books/"+row.id).then((res)=>{if(res.data.flag){this.$message.success("删除成功");}else{this.$message.error("数据同步失败,已刷新页面");}}).finally(()=>{this.getAll();});}).catch(()=>{this.$message.info("删除操作取消");})},//弹出编辑窗口handleUpdate(row) {this.resetForm();//查询当前用户并数据回显axios.get("/books/"+row.id).then((res)=>{if(res.data.flag && res.data.data != null){this.formData = res.data.data;//弹出编辑弹出层this.dialogFormVisible4Edit = true;}else{this.$message.error("数据同步失败,已刷新页面");}}).finally(()=>{this.getAll();});// 直接把row中的数据赋值给formData会有数据同步问题// this.formData = {"id":row.id,"type":row.type,"name":row.name,"description":row.description}},//修改handleEdit() {console.log(this.formData);axios.put("/books",this.formData).then((res)=>{if(res.data.flag){this.$message.success("修改成功");this.dialogFormVisible4Edit = false;}else{this.$message.error("数据同步失败,刷新页面");}}).finally(()=>{this.getAll();})},//分页查询//切换页码handleCurrentChange(currentPage) {},//条件查询}})</script></html>
全局异常处理
全局异常处理器和统一返回格式.wmv
后台系统bug导致数据格式不统一
:::info
可以使用SpringMVC提供的@RestControllerAdvice用来处理全局数据
顾名思义,@ControllerAdvice就是@Controller 的增强版。@ControllerAdvice主要用来处理全局数据,一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用。
:::
package com.xuezhi.config;import com.xuezhi.pojo.R;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvicepublic class ProjectExceptionAdvice {@ExceptionHandlerpublic R doException(Exception e){e.printStackTrace();return new R("系统错误,请稍后再试!");}}
:::info 模拟异常 :::
@PostMappingpublic R save(@RequestBody Book book) throws SQLException{if("123".equals(book.getName())){// 异常外抛throw new SQLException();}return new R(booksService.save(book));}
//添加handleAdd () {axios.post("/books",this.formData).then((res)=>{if(res.data.flag){//提示this.$message.success("新增成功");//关闭新增弹层this.dialogFormVisible = false;}else{// 使用后台提供的消息this.$message.error(res.data.msg);}}).finally(()=>{//不管成功与否,都要重新查询列表this.getAll();})},
业务消息一致性处理
目前我们的业务消息有来自于前端的也有来自于后端的,我们可以在后端统一处理
:::info
例如:
:::
package com.xuezhi.controller;import com.xuezhi.pojo.Book;import com.xuezhi.pojo.R;import com.xuezhi.service.BooksService;import org.apache.ibatis.annotations.Delete;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.*;import java.sql.SQLException;@RestController@RequestMapping("/books")public class BooksController {@Autowiredprivate BooksService booksService;@GetMappingpublic R getAll(){return new R(true,booksService.list());}@GetMapping("/{id}")public R getById(@PathVariable Integer id){Book book = booksService.getById(id);return new R(true,book,book == null?"数据同步失败,已刷新页面!":"");}@PostMappingpublic R save(@RequestBody Book book) throws SQLException{if("123".equals(book.getName())){throw new SQLException();}boolean flag = booksService.save(book);return new R(flag,flag?"新增成功^_^":"新增失败-_-!");}@DeleteMapping("/{id}")public R delete(@PathVariable Integer id){boolean flag = booksService.removeById(id);return new R(flag,flag?"删除成功^_^":"数据同步失败,已刷新页面!");}@PutMappingpublic R update(@RequestBody Book books){boolean flag = booksService.updateById(books);return new R(flag,flag?"修改成功^_^":"修改失败-_-!");}}
分页
分页查询.wmv
:::info
分页数据模版
:::

:::info
把getAll方法,改成分页查询
:::
//分页查询getAll() {axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize).then((rs)=>{this.pagination.currentPage = rs.data.data.current;this.pagination.total = rs.data.data.total;this.dataList = rs.data.data.records;});},//切换页码handleCurrentChange(currentPage) {this.pagination.currentPage = currentPage;this.getAll();},
@GetMapping("/{currentPage}/{pageSize}")public R getByPage(@PathVariable Integer currentPage , @PathVariable Integer pageSize){Page<Book> bookPage = new Page<Book>(currentPage, pageSize);Page<Book> page = booksService.page(bookPage);return new R(true,page);}
分页删除bug
:::info
如果最后一页只有一条数据,并且删除他,会导致一下bug
:::
:::info
解决:
:::
@GetMapping("/{currentPage}/{pageSize}")public R getByPage(@PathVariable Integer currentPage , @PathVariable Integer pageSize){Page<Book> bookPage = new Page<Book>(currentPage, pageSize);Page<Book> page = booksService.page(bookPage);// 如果当前页大于总页码数,则查询最后一页数据if(currentPage>page.getPages()){bookPage = new Page<Book>(page.getPages(), pageSize);page = booksService.page(bookPage);}return new R(true,page);}
条件查询
条件查询.wmv

我们发现查询查询的模版并没有绑定模型数据,所以我们需要自己绑定一下,因为条件查询是跟着分页走的所以我们把数据写到 分页的模型里
:::info
组装查询参数,并拼接到url,携带到后台
:::
//分页查询getAll() {//组装条件查询请求参数let param = "?q";param += "&type="+this.pagination.type;param += "&name="+this.pagination.name;param += "&description="+this.pagination.description;axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize+param).then((rs)=>{//分页this.pagination.currentPage = rs.data.data.current;this.pagination.total = rs.data.data.total;this.dataList = rs.data.data.records;});},
:::info 修改分页查询Controller,把业务逻辑放到Service层 :::
@GetMapping("/{currentPage}/{pageSize}")public R getByPage(@PathVariable Integer currentPage , @PathVariable Integer pageSize,Book book){IPage<Book> page = booksService.getPage(currentPage,pageSize,book);return new R(true,page);}
:::info 根据条件查询接口 :::
public interface BooksService extends IService<Book> {IPage<Book> getPage(Integer currentPage, Integer pageSize, Book book);}
:::info 根据条件查询实现 :::
@Servicepublic class BooksServiceImpl extends ServiceImpl<BookMapper, Book> implements BooksService {@Autowiredprivate BookMapper bookMapper;public IPage<Book> getPage(Integer currentPage, Integer pageSize, Book book) {Page<Book> page = new Page(currentPage, pageSize);// 组装查询条件LambdaQueryWrapper<Book> wrapper = new LambdaQueryWrapper<Book>();wrapper.like(Strings.isNotEmpty(book.getType()),Book::getType,book.getType()).like(Strings.isNotEmpty(book.getName()),Book::getName,book.getName()).like(Strings.isNotEmpty(book.getDescription()),Book::getDescription,book.getDescription());// 分页查询Page<Book> bookPage = bookMapper.selectPage(page, wrapper);// 避免删除之后分页bug,如果当前页>总页数,则查询最后一页if(currentPage>bookPage.getPages()){page = new Page(bookPage.getPages(), pageSize);bookPage = bookMapper.selectPage(page, wrapper);}return bookPage;}}
总结
1. pom.xml配置起步依赖2. application.yml设置数据源、端口、框架技术相关配置等3. dao继承BaseMapper、设置@Mapper4. dao测试类5. service调用数据层接口或MyBatis-Plus提供的接口快速开发6. service测试类7. controller基于Restful开发,使用Postman测试跑通功能8. 页面放置在resources目录下的static目录中






