一、快速上手SpringBoot
1.1 SpringBoot入门程序开发
- SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的初始搭建以及开发过程
- Spring程序缺点
- 依赖设置繁琐
- 配置繁琐
- SpringBoot程序优点
- 起步依赖(简化依赖配置)
- 自动配置(简化常用工程相关配置)
- 辅助功能(内置服务器等)
1.1.1 基于Idea创建SpringBoot工程
-
创建新模块,选择Spring Initializr,并配置模块相关基础信息
-
选择当前模块需要使用的技术
-
开发控制器类
// Rest模式 @RestController @RequestMapping("/books") public class BookController { @GetMapping public String getById(){ System.out.println("springboot is running..."); return "springboot is running..."; } }
-
运行自动生成的Application类
类/配置文件 | Spring | SpringBoot |
---|---|---|
pom文件中的坐标 | 手工添加 | 勾选添加 |
web3.0配置类 | 手工制作 | 无 |
Spring/SpringMVC配置类 | 手工制作 | 无 |
控制器 | 手工制作 | 手工制作 |
- 基于idea开发SpringBoot程序需要确保联网且能够加载到程序框架结构
1.1.2 基于官网创建SpringBoot工程
- 打开springboot官网,选择Quickstart Your Project
- 创建工程,并保存项目
- 解压项目,通过IDE导入项目
1.1.3 基于阿里云创建SpringBoot工程
- 打开springboot官网,选择Quickstart Your Project
1.1.4 手工创建Maven工程修改为SpringBoot工程
1.3 入门案例解析
1.3.1 parent
- 定义一系列的常用坐标版本
- 定义一系列的常用坐标组合
- 直接使用组合
- 开发SpringBoot程序要继承spring-boot-starter-parent
- spring-boot-starter-parent中定义了若干个依赖管理
- 继承parent模块可以避免多个依赖使用相同技术时出现依赖版本冲突
- 继承parent的形式也可以采用引入依赖的形式实现效果
1.3.2 starter
-
SpringBoot中常见项目名称,定义了当前项目使用的所有依赖坐标,以达到减少依赖配置的目的
-
spring-boot-starter-web.pom
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
1.3.3 引导类
-
启动方式
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
-
SpringBoot的引导类是Boot工程的执行入口,运行main方法就可以启动项目
-
SpringBoot工程运行后初始化Spring容器,扫描引导类所在包加载bean
1.3.4 内嵌tomcat
-
内嵌Tomcat服务器是SpringBoot辅助功能之一
-
内嵌Tomcat工作原理是将Tomcat服务器作为对象运行,并将该对象交给Spring容器管理
-
变更内嵌服务器思想是去除现有服务器,添加全新的服务器
-
内置服务器
- tomcat(默认):apache出品,应用面广,负载了若干较重的组件
- jetty:更轻量级,负载型能远不及tomcat
- unsertow:undertow,负载性能勉强跑赢tomcat
二、SpringBoot基础配置
2.1 属性配置
-
SpringBoot默认配置文件application.properties,通过键值对配置对应属性
server.port=80 spring.main.banner-mode=off spring.banner.image.location=logo.png logging.level.root=info(error,debug)
-
SpringBoot内置属性查询
- 官方文档中参考文档第一项:Application Properties
2.2 配置文件分类
-
SpringBoot提供了多种属性配置方式
-
application.properties
server.port=80
-
application.yml
server: port:81
-
application.yaml
server: port:82
-
-
SpringBoot配置文件加载顺序:application.properties > application.yml > application.yaml
-
指定SpringBoot配置文件
- Setting -> Project Structure -> Facets
- 选中对应项目/工程
- Customize Spring Boot
- 选择配置文件
2.3 yaml文件
- YAML(YAML Ain’t Markup Language),一种数据序列化格式
- 优点
- 容易阅读
- 容易与脚本语言交互
- 以数据为核心,重数据轻格式
- YAML
- .yml(主流)
- .yaml
- YAML语法规则
- 大小写敏感
- 属性层级关系使用多行描述,每行结尾使用冒号结束
- 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
- 属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
- (#)表示注释
2.4 yaml数据读取
-
使用@Value读取单个数据,属性名引用方式:${一级属性名.二级属性名…}
-
在配置文件中可以使用属性名引用方式引用属性
-
属性值中如果出现转义字符,需要使用双引号包裹
-
封装全部数据到Environment对象
-
自定义对象封装指定数据
@Component @ConfigurationProperties(prefix = "enterprise") public class Enterprise { private String name; private Integer age; private String[] subject; }
三、基于SpringBoot实现SSM整合
3.1 整合JUnit
-
SpringBoot整合JUnit
@SpringBootTest class Springboot04JunitApplicationTests { // 1、注入要测试的对象 @Autowired private BookDao bookDao; @Test void contextLoads() { // 2、执行要测试的对象对应的方法 bookDao.save(); } }
3.2 整合Mybatis
- 核心配置:数据库连接相关信息
- 映射配置:SQL映射(XML/注解)
- 步骤
- 创建新模块,选择Spring初始化,并配置模块相关基础信息
- 选择当前模块需要使用的技术集(MyBatis、MySQL)
- 设置数据源参数(application.yaml)
- 定义数据层接口与映射配置
- 测试类中注入dao接口,测试功能
3.3 整合Druid
- 导入Druid对应的starter
- 根据Druid提供的配置方式进行配置
3.4 整合第三方技术通用方式
- 导入对应的starter
- 根据提供的配置格式,配置非默认值对应的配置项
3.5 SSM整合案例
3.5.1 模块创建
- 勾选SpringMVC与MySQL坐标
- 修改配置文件为yml格式
- 设置端口为80方便访问
3.5.2 实体类开发
-
Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
-
lombok版本由SpringBoot提供,无需指定版本
-
常用注解@Data
@Data public class Book { private Integer id; private String type; private String name; private String description; }
-
为当前实体类在编译期设置对应的get/set方法,toString方法,hashCode方法,equals方法等
3.5.2 数据层开发
-
技术实现方案
- MyBatisPlus
- Druid
-
导入MyBatisPlus与Druid对应的starter
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency> <dependency>
-
配置数据源与MyBatisPlus对应的基础配置(id生成策略使用数据库自增策略)
spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db username: root password: 1234 mybatis-plus: global-config: db-config: table-prefix: tbl_ id-type: auto
-
继承BaseMapper并指定泛型
@Mapper public interface BookDao extends BaseMapper<Book> { }
-
制作测试类测试结果
@Test void testUpdate() { Book book = new Book(); book.setId(15); book.setName("测试数据abc"); book.setDescription("测试数据a"); bookDao.updateById(book); } @Test void testDelete() { bookDao.deleteById(15); }
-
为方便调试可以开启MyBatisPlus的日志
mybatis-plus: global-config: db-config: table-prefix: tbl_ id-type: auto configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
分页功能
-
分页操作需要设定分页对象IPage
@Test void testGetPage(){ IPage page = new Page(1,5); bookDao.selectPage(page,null); }
-
IPage对象中封装了分页操作中的所有数据
- 数据
- 当前页码值
- 每页数据总量
- 最大页码值
- 数据总量
-
分页操作实在MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能,使用MyBatisPlus拦截器实现
@Configuration public class MPConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ // 1、定义MP拦截器 MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 2、添加具体的拦截器 interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }
-
3.5.3 业务层开发
-
Service层接口定义与数据层接口定义具有较大差别,不要混用
- selectByUsernameAndPassword(Steing username,String password)
- login(Steing username,String password)
-
测试类定义
@SpringBootTest public class BookServiceTestCase { @Autowired private BookService bookService; @Test void testGetById(){ System.out.println(bookService.getById(4)); } @Test void testDelete() { bookService.delete(15); } @Test void testGetAll() { bookService.getAll(); } }
-
业务层快速开发方案
- 使用MyBatisPlus提供有业务层通用接口(IService)与业务层通用实现类(ServiceImpl<M,T>)
- 在通用类基础上做功能重载或功能追加
- 注意重载时不要覆盖原始操作,避免原始提供的功能丢失
3.5.4 表现层开发
- 基于Restful进行表现层接口开发
- 新增:POST
- 删除:DELETE
- 修改:PUT
- 查询:GET
- 接收参数:@Request
- 路径变量:@PathVariable
- 使用Postman测试表现层接口功能
3.5.5 表现层消息一致性处理
-
修改前数据格式统一
-
增删改
true
-
查单条
{ "id": 3, "type": "计算机理论", "name": "3", "description": "3" }
-
查全部
[ { "id": 1, "type": "计算机理论", "name": "1", "description": "1" }, { "id": 2, "type": "计算机理论", "name": "2", "description": "2" }, { "id": 3, "type": "计算机理论", "name": "3", "description": "3" } ]
-
-
设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也成为前后端数据协议
@Data public class R{ private Boolean flag; private Object data; }
3.5.6 前后端协议联调
-
前后端分离结构设计中页面归属前端服务器
-
单体工程中页面放置在resources目录下的static目录中(出问题先执行clean)
-
created钩子函数用于初始化页面时发起调用
-
前端发送异步请求,调用后端接口
getAll() { axios.get("/books").then((res)=>{ console.log(res.data); }); }
-
弹出添加窗口
handleCreate() { this.dialogFormVisible = true; }
-
添加
//添加 handleAdd () { axios.post("/books",this.formData //判断当前操作是否成功 if(res.data.flag){ //1、关闭弹层 this.dialogFormVisible = this.$message.success("添加 }else{ this.$message.error("添加失败 } }).finally(()=>{ //2、重新加载数据 this.getAll(); }); },
-
删除
// 删除 handleDelete(row) { console.log(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(()=>{ //2、重新加载数据 this.getAll(); }); }).catch(()=>{ this.$message.info("取消操作"); }); },
-
修改
handleUpdate(row) { // 发送异步请求 axios.get("/books/" + row.id).then((res)=>{ if(res.data.flag && res.data.data != null){ this.dialogFormVisible4Edit = true; this.formData = res.data.data; }else{ this.$message.error("数据同步失败,自动刷新"); } }).finally(()=>{ //2、重新加载数据 this.getAll(); }); },
3.5.7 业务消息一致性处理
-
对异常进行统一处理,出现异常后,返回指定信息
//作为springmvc的异常处理器 @RestControllerAdvice public class ProjectExceptionAdvice { //拦截所有 @ExceptionHandler(Exception.class) public R doException(Exception ex){ //记录日志 //通知运维 //通知开发 ex.printStackTrace(); return new R("服务器故障,请稍后再试"); } }
-
页面消息处理,没有传递消息加载默认消息,传递消息后加载指定消息
handleAdd() { axios.post("/books", this.formData).then((res) => { //判断当前操作是否成功 if (res.data.flag) { //1、关闭弹层 this.dialogFormVisible = false; this.$message.success(res.data.msg); } else { this.$message.error(res.data.msg); } }).finally(() => { //2、重新加载数据 this.getAll(); }); },
-
可以在表现层Controller中进行消息统一处理
@PostMapping public R save(@RequestBody Book book) throws IOException { if(book.getName().equals("123")) throw new IOException(); boolean flag = bookService.save(book); return new R(flag,flag ? "添加成功!" : "添加失败!"); }
3.5.8 分页功能
-
页面使用el分页组件添加分页功能
<!--分页组件--> <div class="pagination-container"> <el-pagination class="pagiantion" @current-change="handleCurrentChange" :current-page="pagination.currentPage" :page-size="pagination.pageSize" layout="total, prev, pager, next, jumper" :total="pagination.total"> </el-pagination> </div>
-
定义分页组件需要使用的数据并将数据绑定到分页组件
data:{ pagination:{ //分页相关模型数据 currentPage:1, //当前页码 pageSize:10, //每页显示的记录数 total:1, //总记录数 } }
-
替换查询全部功能为分页功能
getAll(){ axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize).then((res) => { }
3.5.9 删除功能维护
-
对查询结果进行校验,如果当前页码值大于最大页码值,使用最大页码值作为当前页码值重新查询
@GetMapping("{currentPage}/{pageSize}") public R getPage(@PathVariable int currentPage, @PathVariable int pageSize){ IPage<Book> page = bookService.getPage(currentPage, pageSize); //如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值 if (currentPage > page.getPages()){ page = bookService.getPage((int)page.getPages(), pageSize); } return new R(true,page); }
3.5.10 条件查询功能
-
页面数据模型绑定
<div class="filter-container"> <el-input placeholder="图书类别" v-model="pagination.type" style="width: 200px;" class="filter-item"></el-input> <el-input placeholder="图书名称" v-model="pagination.name" style="width: 200px;" class="filter-item"></el-input> <el-input placeholder="图书描述" v-model="pagination.description" 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>
-
组织数据成为get请求发送到数据
getAll() { //组织参数,拼接url请求地址 console.log(this.pagination.type); param = "?type="+this.pagination.type; param += "&name="+this.pagination.name; param += "&description="+this.pagination.description; console.log(param); // 发送异步请求 axios.get("/books/"+this.pagination.currentPage+"/"+this.pagination.pageSize+param).then((res) => { this.pagination.pageSize = res.data.data.size; this.pagination.currentPage = res.data.data.current; this.pagination.total = res.data.data.total; this.dataList = res.data.data.records; }); },
-
Controller接收参数
@GetMapping("{currentPage}/{pageSize}") public R getAll(@PathVariable int currentPage, @PathVariable int pageSize,String name,Book book){ IPage<Book> page = bookService.getPage(currentPage, pageSize,book); //如果当前页码值大于了总页码值,那么重新执行查询操作,使用最大页码值作为当前页码值 if (currentPage > page.getPages()){ page = bookService.getPage((int)page.getPages(), pageSize,book); } return new R(true,page); }
-
业务层接口功能开发
@Override public IPage<Book> getPage(int currentPage, int pageSize, Book book) { LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>(); lqw.like(Strings.isNotEmpty(book.getType()),Book::getType,book.getType()); lqw.like(Strings.isNotEmpty(book.getName()),Book::getName,book.getName()); lqw.like(Strings.isNotEmpty(book.getDescription()),Book::getDescription,book.getDescription()); IPage page = new Page(currentPage,pageSize); bookDao.selectPage(page,lqw); return page; }
3.5.11 总结
- pom.xml:配置起步依赖
- application.yam:设置数据源、端口、框架技术相关配置等
- dao:继承BaseMapper、设置@Mapper
- dao测试类
- service:调试数据层接口或MyBatis-Plus提供的接口快速开发
- service测试类
- controller:基于Restful开发,使用Postman测试跑通功能
- 页面:放置在resources目录下的static目录中