目录
- 1.springboot简介
- 2.REST风格
- 1.简介
- 2.RESTful
- 3.复制工程
- 4.属性配置
- 1.application.properties
- 2.application.yml、.yaml
- 3.关于写配置文件的时候没有提示的解决方案
- 4.yaml
- 1.语法格式
- 2.读取数据
- 3.封装对象
- 5.整合第三方技术
- 1.整合Junit
- 1.实现
- 2.注意事项
- 2.整合mybatis
- 3.整合mybatis-plus
- 4.整合Druid
- 6.完结案例
- 1.项目初始化
- 2.数据库搭建&数据源配置
- 3.测试Mybatis-plus
- 1.增删改查
- 2.分页查询
- 3.条件查询
- 4.业务层开发
- 1.业务接口与实体类
- 2.测试类
- 3.MP业务层快速开发
- 5.表现层开发
- 1.表现层代码
- 2.接口测试
- 3.表现层消息一致性处理
- 4.异常消息处理
- 6.前后端协议联调
1.springboot简介
SpringBoot程序优点:
-
起步依赖(简化依赖配置)
-
- parent
所有SpringBoot项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到减少依赖冲突的目的
-
自动配置(简化常用工程相关配置)
-
- stater
SpringBoot中常见项目名称,定义了当前项目使用的所有依赖坐标,以达到减少依赖配置的目的
-
引导类
springboot的启动方式:
-
- SpringBoot的引导类是Boot工程的执行入口,运行main方法就可以启动项目
-
- SpringBoot工程运行后初始化Spring容器,扫描引导类所在包加载bean
-
辅助功能(内置服务器,……)
-
- 内嵌Tomcat
2.REST风格
1.简介
REST(Representational State Transfer): 表现形式状态转换。
- 传统风格资源描述形式:
http://localhost/user/getById?id=1
http://localhost/user/saveUser
- REST风格描述形式:
http://localhost/user/id=1
http://localhost/user
- 优点:
-
- 隐藏资源的访问行为,无法通过地址得知对资源是何种操作
-
- 书写简化
REST风格访问资源时使用 行为动作 区分对资源进行了何种操作
- 查询 GET
- 新增/修改 POST
- 修改/更新 PUT
- 删除 DELETE
2.RESTful
RESTful:按照REST风格对资源进行访问。
- 通过 @RequestMapping 注解的路径设置,通过 @PathVariable 注解绑定参数
-
- @PostMapping
-
- @GetMapping
-
- @DeleteMapping
-
- @PutMapping
- @RestController 类注解
基于SpringMVC的RESTful开发控制器类定义上方
设置当前控制器类为RESTful风格,等同于@Controller与@RresponseBody组合
@RequestBody、 @RequestParam 、 @PathVariable 和 @Vaild 注解的使用及区别
3.复制工程
原则:
- 保留工程基础结构
- 抹掉原始工程痕迹
步骤:
- 在工作空间中复制对应工程,并修改工程名称
- 删除与Idea相关配置文件,仅保留src目录与pom.xml文件
- 修改pom.xml文件中的artifactId与新工程/模块名相同
- 删除name标签(可选)
- 保留备份工程供后期使用
4.属性配置
1.application.properties
SpringBoot内置属性查询: https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties
2.application.yml、.yaml
SpringBoot配置文件加载顺序: application.properties > application.yml > application.yaml
3.关于写配置文件的时候没有提示的解决方案
如果点击不了OK的解决方案:
- 在输入框里面填入配置文件的名
4.yaml
1.语法格式
YAML(YAML Ain’t Markup Language): 一种数据序列化格式。
优点:
- 容易阅读
- 容易与脚本语言交互
- 以数据为核心,重数据轻格式
YAML文件扩展名:
- .yml(主流)
- .yaml
yaml语法规则:
- 大小写敏感
- 属性层级关系使用多行描述,每行结尾使用:结束
- 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
- 属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
- # 表示注释
- 核心规则:数据前面要加空格与冒号隔开
字面值表示方式:
boolean: TRUE #TRUE,true,True,FALSE,false,False均可 float: 3.14 #6.8523015e+5 #支持科学计数法 int: 123 #0b1010_0111_0100_1010_1110 #支持二进制、八进制、十六进制 null: ~ #使用~表示null string: HelloWorld #字符串可以直接书写 string2: "Hello World" #可以使用双引号包裹特殊字符 date: 2022-06-10 #日期必须使用yyyy-MM-dd格式 datetime: 2022-06-10T15:02:31+08:00 #时间和日期之间使用T连接,最后使用+代表时区
数组表示方式:
subject: - Java - 前端 - 大数据 enterprise: name: itcast age: 16 subject: - Java - 前端 - 大数据 likes: [王者荣耀,刺激战场] #数组书写缩略格式 users: #对象数组格式 - name: Tom age: 4 - name: Jerry age: 5 users: #对象数组格式二 - name: Tom age: 4 - name: Jerry age: 5 #对象数组缩略格式 users2: [ { name:Tom , age:4 } , { name:Jerry , age:5 } ]
2.读取数据
使用@Value读取单个数据,属性名引用方式:${一级属性名.二级属性名……}
lesson: SpringBoot server: port: 82 enterprise: name: itcast age: 16 tel: 4006184000 subject: - Java - 前端 - 大数据
@RestController @RequestMapping("/books") public class BookController { @Value("${lesson}") private String lessonName; @Value("${server.port}") private int port; @Value("${enterprise.subject[1]}") private String[] subject_01; }
在配置文件中可以使用属性名引用方式引用属性
baseDir: /usr/local/fire center: dataDir: ${baseDir}/data tmpDir: ${baseDir}/tmp logDir: ${baseDir}/log msgDir: ${baseDir}/msgDir
属性值中如果出现转移字符,需要使用双引号包裹
lesson: "Spring\tboot\nlesson"
3.封装对象
封装全部数据到Environment对象
lesson: SpringBoot server: port: 82 enterprise: name: itcast age: 16 tel: 4006184000 subject: - Java - 前端 - 大数据
@RestController @RequestMapping("/books") public class BookController { @Value("${lesson}") private String lessonName; @Value("${server.port}") private int port; @Value("${enterprise.subject[1]}") private String[] subject_01; }
自定义对象封装指定数据 @ConfigurationProperties(prefix = "")
lesson: SpringBoot server: port: 82 enterprise: name: itcast age: 16 tel: 4006184000 subject: - Java - 前端 - 大数据
@Component @ConfigurationProperties(prefix = "enterprise") public class Enterprise { private String name; private Integer age; private String[] subject; }
@RestController @RequestMapping("/books") public class BookController { @Autowired private Enterprise enterprise; }
5.整合第三方技术
1.整合Junit
1.实现
导入测试对应的starter:
org.springframework.boot spring-boot-starter-test test 测试类使用@SpringBootTest修饰:
@SpringBootTest class SpringbootDemo1ApplicationTests { @Test void contextLoads() { } }
使用自动装配的形式添加要测试的对象(impl实现类)
2.注意事项
正确的测试类路径应该与引导类路径一致,如下:
当路径不一致的时候,如下:
此时测试类就无法依赖注入,需要做以下配置(任选其一):
- @SpringBootTest(classes = SpringbootDemo1Application.class)
- @ContextConfiguration(classes = SpringbootDemo1Application.class)
2.整合mybatis
导入依赖:
org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 mysql mysql-connector-java 设置数据源参数:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/book username: root password: root
注意: SpringBoot版本低于2.4.3(不含),Mysql驱动版本大于8.0时,需要在url连接串中配置时区
jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
或在MySQL数据库端配置时区解决此问题
定义实体类:
@Data @AllArgsConstructor @NoArgsConstructor public class Book { private String name; private Integer id; private String type; private BigDecimal price; }
定义数据层接口与映射配置:
@Mapper public interface BookDao { @Select("select * from book") public List
getAll(); } 测试类中注入dao接口,测试功能:
@SpringBootTest class Springboot08MybatisApplicationTests { @Autowired private BookDao bookDao; @Test public void testGetById() { Book book = bookDao.getById(1); System.out.println(book); } }
3.整合mybatis-plus
https://blog.csdn.net/qq_41402200/article/details/88891511
4.整合Druid
com.alibaba druid-spring-boot-starter 1.1.22 spring: main: banner-mode: off datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC username: root password: root
6.完结案例
项目完整目录结构:
1.项目初始化
项目初始化:
2.数据库搭建&数据源配置
SQL语句:
create database `spbt`; use `spbt`; create table `sp_book` ( `id` int primary key auto_increment comment '主键id', `type` varchar(20) not null comment '书籍类型', `name` varchar(30) not null comment '书籍名称', `description` varchar(100) not null comment '书籍描述' )default charset=utf8 comment '书籍表'; insert into `sp_book`(`type`,`name`,`description`) values ('科幻','地球往事','《三体》三部曲第一部,作品讲述了地球人类文明和三体文明的信息交流、生死搏杀及两个文明在宇宙中的兴衰历程。'), ('科幻','黑暗森林','《三体》三部曲第二部,作品讲述了地球人类文明和三体文明的信息交流、生死搏杀及两个文明在宇宙中的兴衰历程。'), ('科幻','死神永生','《三体》三部曲第三部,作品讲述了地球人类文明和三体文明的信息交流、生死搏杀及两个文明在宇宙中的兴衰历程。'), ('仙侠','将夜','这本小说讲述的是一段可歌可泣可笑可爱的草根崛起史,一个物质要求宁滥勿缺的开朗少年行。小说基于修真世界,却又胜于修真,讲述了人定胜天,花开彼岸天的历史,引人深思。'), ('仙侠','雪中悍刀行','该小说讲述一个关于庙堂权争与刀剑交错的时代,一个暗潮涌动粉墨登场的江湖。'), ('都市','大国重工','国家重大装备办处长冯啸辰穿越到了1980年,看他如何与同代人一道,用汗水和智慧,铸就大国重工。'), ('架空','赘婿','本书主要讲述了主角从现代金融界巨头的身份回到了古代,进入一个商贾之家最没地位的赘婿身体后,涉及到一系列家国天下事的故事。'), ('科幻','北京折叠','该小说讲述北京在未来按照社会阶层被分成三个空间,生活在第三空间的垃圾工老刀穿越三层空间送信的故事。'), ('鬼怪','地煞七十二变','地煞七十二般变化,每一般变化,就是一项神通。一本神奇的黄壳书将李长安送回古代世界,每斩杀一个妖魔,就获得一项神通。'), ('魔幻','诡秘之主','作品融汇了克苏鲁风格、西方魔幻元素、第一次工业革命时代风情和蒸汽朋克情怀。这是一个蒸汽与机械的世界,这是一段“愚者”的传说。'), ('幻想','第一序列','作品讲述了主角任小粟通过吸收正能量一步步崛起,同时逐渐改变了世界的故事。'), ('人文','明朝那些事儿','作品主要讲述的是从1344年到1644年这三百年间关于明朝的一些故事 。'), ('人文','三国那些事儿','本书分为上下两篇,上篇为读者介绍了三国里的著名人物,下篇讲述了发生在这些人物身上的经典故事。'), ('都市','余罪','警校学生余罪从一场特殊的选拔开始,进入了生活和社会矛盾之中,成为一个卧底,是一种挑战,也是一种难以想象的痛苦生活的开始。'), ('科幻','安德的游戏','第三次虫族入侵已迫在眉睫,而地球舰队尚未找到任何抵抗的可能。世界即将毁灭。人类最后的救世主 ,是一个六岁的儿童——安德。'), ('科幻','死者代言人','安德遭遇了一起残忍而离奇的虐杀事件。安德隐匿身份,再次踏入一个异生物星球。随着调查的深入,他一步步走向异种生物的圈套,而真相,居然与他童年的那段回忆有千丝万缕的联系……'), ('科幻','安德的影子','当安德和队员们全力以赴挑战最后的难关时,只有一人早已知晓这背后残酷的真相。他是影子,在英雄如阳光照射大地之时,只有影子能承接必然尾随而至的黑暗。'), ('惊悚','我有一座冒险屋','作该作品讲述主人公陈歌在翻旧物时寻得黑色手机,在手机的指引下探寻世界的另一面。'), ('科幻','星门','传说,在那古老的星空深处,伫立着一道血与火侵染的红色之门。'), ('仙侠','凡人修仙传','小说讲述了一个普通的山村穷小子,偶然之下,跨入到一个江湖小门派,虽然资质平庸,但依靠自身努力和合理算计最后修炼成仙的故事。'), ('惊悚','地狱公寓','在这公寓内,如果住户房间的墙壁上出现了血字,住户就必须完成血字任务,一旦违背就会死亡。'), ('仙侠','求魔','该书主要讲述了主角苏铭不甘命运被掌控,与命运斗争的故事。修仙觅长生,热血任逍遥,踏莲曳波涤剑骨,凭虚御风塑圣魂。'), ('仙侠','仙逆','作该小说讲述的是一个平庸的少年,踏入仙途,如何一步一步走向巅峰,凭一己之力扬名修真界的故事。'), ('诗集','炸裂志','陈年喜以其平实婉转的文字,在诗句中建构了一个中年男人的世界,以白雪抗衡黑洞,以爱感悟生死,以诗超越世间。'), ('仙侠','大奉打更人','作者巧妙地将俗世朝堂背景,百家文化与仙侠修炼结合起来,并设计了一个个精妙的案件作为引线,一步步展现出了一个波澜壮阔的全新世界');
创建实体类:
@Data @AllArgsConstructor @NoArgsConstructor public class Book { private Integer id; private String type; private String name; private String description; }
配置数据源:
spring: datasource: druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/spbt?serverTimezone=UTC username: root password: admin mybatis-plus: global-config: db-config: # 表前缀 table-prefix: sp_ # 设置数据库的自增策略 id-type: auto # 开启mybatis-plus的运行日志 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.测试Mybatis-plus
注意: mybatis-plus不用提交事务。
1.增删改查
mapper接口:
@Mapper public interface QueryBookMapper extends BaseMapper
{ } 测试类:
@SpringBootTest public class BookMapperTests { @Autowired private BookMapper mapper; @Test void testGetById() { System.out.println(mapper.selectById(1)); } @Test void testGetAll() { System.out.println(mapper.selectList(null)); } @Test void testSave() { Book book = new Book(); book.setName("一念永恒"); book.setType("仙侠"); book.setDescription("一念成沧海,一念化桑田。一念斩千魔,一念诛万仙。"); mapper.insert(book); } @Test void testUpdate() { Book book = new Book(); book.setId(20); book.setDescription("韩老魔的杀人夺宝一路升级的传奇故事"); mapper.updateById(book); } @Test void testDelet() { mapper.deleteById(26); } }
2.分页查询
- 设定分页对象 IPage
- IPage 对象中封装了分页操作中的所有数据
当前页码值、每页数据总量、最大页码值、数据总量、数据
- 分页操作是在MyBatisPlus的常规操作基础上增强得到,内部是动态的拼写SQL语句,因此需要增强对应的功能,
使用 MyBatisPlus拦截器 实现
-
/*分页查询*/ @Test void testGetPage() { IPage page = new Page(2,5); mapper.selectPage(page,null); System.out.println(page.getCurrent()); System.out.println(page.getSize()); System.out.println(page.getTotal()); System.out.println(page.getPages()); System.out.println(page.getRecords()); }
分页查询工具类
@Configuration public class MPConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }
3.条件查询
- 使用QueryWrapper对象封装查询条件,推荐使用LambdaQueryWrapper对象,所有查询操作封装成方法调用
/*条件*/ @Test void testGetBy() { /* 一般写法 QueryWrapper
qw = new QueryWrapper<>(); qw.like("type","仙"); mapper.selectList(qw);*/ /*Lambda写法*/ String type = "仙"; LambdaQueryWrapper lqw = new LambdaQueryWrapper<>(); lqw.like(type!=null, Book::getType, type); mapper.selectList(lqw); } 4.业务层开发
1.业务接口与实体类
业务接口:
public interface BookService { Boolean save(Book book); Boolean update(Book book); Boolean delete(Integer id); Book getById(Integer id); List
getAll(); IPage getPage(int currentPage, int pageSize); } 实现类:
@Service public class BookServiceImpl implements BookService { @Resource BookMapper mapper; @Override public Boolean save(Book book) { return mapper.insert(book) > 0; } @Override public Boolean update(Book book) { return mapper.updateById(book) > 0; } @Override public Boolean delete(Integer id) { return mapper.deleteById(id) > 0; } @Override public Book getById(Integer id) { return mapper.selectById(id); } @Override public List
getAll() { return mapper.selectList(null); } @Override public IPage getPage(int currentPage, int pageSize) { IPage page = new Page(currentPage, pageSize); mapper.selectPage(page, null); return page; } } 2.测试类
选中实体类名,Ctrl shift t 快速创建测试类
@SpringBootTest public class BookServiceImplTest { @Resource BookServiceImpl bookService; @Test void save() { Book book = new Book(); book.setName("诛仙"); book.setType("仙侠"); book.setDescription("天地不仁,以万物为刍狗"); bookService.save(book); } @Test void update() { Book book = new Book(); book.setId(5); book.setDescription("高人雅士、王者之痴、匹夫之怒、美人之勇、凡人琐事"); bookService.update(book); } @Test void delete() { bookService.delete(27); } @Test void getById() { bookService.getById(5); } @Test void getAll() { bookService.getAll(); } @Test void getPage() { IPage
page = bookService.getPage(2,5); System.out.println(page.getCurrent()); System.out.println(page.getSize()); System.out.println(page.getTotal()); System.out.println(page.getPages()); System.out.println(page.getRecords()); } } 3.MP业务层快速开发
接口:
public interface IBookService extends IService
{ } 实现类:
@Service public class IBookServiceImpl extends ServiceImpl
implements IBookService { } 测试类:
@SpringBootTest class IBookServiceImplTest { @Resource private IBookServiceImpl iBookService; @Test void testGetById() { System.out.println(iBookService.getById(1)); } @Test void testGetAll() { System.out.println(iBookService.list(null)); } @Test void testSave() { Book book = new Book(); book.setName("重生之超级战舰"); book.setType("科幻"); book.setDescription("探索宇宙之中的奥秘,和系外行星,各种恒星,白矮星,中子星,黑洞,类星体等进行亲密接触,和各种宇宙文明进行血腥厮杀,探寻被隐藏在重重迷雾之后的宇宙真理……"); iBookService.save(book); } @Test void testUpdate() { Book book = new Book(); book.setId(20); book.setDescription("韩老魔的杀人夺宝一路升级的传奇故事"); iBookService.updateById(book); } @Test void testDelet() { iBookService.removeById(26); } /*分页*/ @Test void testGetPage() { IPage
page = new Page<>(2,5); iBookService.page(page,null); System.out.println(page.getCurrent()); System.out.println(page.getSize()); System.out.println(page.getTotal()); System.out.println(page.getPages()); System.out.println(page.getRecords()); } } 注意: 如果有特殊的业务需求,可以在 IBookService 中重写方法。
5.表现层开发
1.表现层代码
使用REST风格写接口:
@RestController @RequestMapping("/books") public class BookController { @Resource private IBookService iBookService; @GetMapping public List
getAll() { return iBookService.list(null); } @PostMapping public Boolean save(@RequestBody Book book){ return iBookService.save(book); } @PutMapping public Boolean update(@RequestBody Book book){ return iBookService.updateById(book); } @DeleteMapping("{id}") public Boolean delete(@PathVariable Integer id){ return iBookService.removeById(id); } @GetMapping("{id}") public Book getById(@PathVariable Integer id){ return iBookService.getById(id); } @GetMapping("{currentPage}/{pageSize}") public IPage getPage(@PathVariable int currentPage, @PathVariable int pageSize){ IPage page = new Page<>(currentPage,pageSize); iBookService.page(page,null); return page; } } 2.接口测试
本文采用的测试工具是: ApiPost
接口测试目录文档: https://console-docs.apipost.cn/preview/7f61bffaaafd5a1c/44eae2dee5dca510
3.表现层消息一致性处理
设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议。
工具类:
@Data @AllArgsConstructor @NoArgsConstructor public class R { // 标志接口是否成功执行 private Boolean flag; // 调用接口返回数据格式 private Object data; public R(Boolean flag) { this.flag = flag; } }
升级版的表现层:
@RestController @RequestMapping("/books") public class BookControllerPro { @Resource private IBookService iBookService; @GetMapping public R getAll() { return new R(true, iBookService.list()); } @PostMapping public R save(@RequestBody Book book){ return new R(iBookService.save(book)); } @PutMapping public R update(@RequestBody Book book){ return new R(iBookService.updateById(book)); } @DeleteMapping("{id}") public R delete(@PathVariable Integer id){ return new R(iBookService.removeById(id)); } @GetMapping("{id}") public R getById(@PathVariable Integer id){ return new R(true, iBookService.getById(id)); } @GetMapping("{currentPage}/{pageSize}") public R getPage(@PathVariable int currentPage, @PathVariable int pageSize){ IPage
page = new Page<>(currentPage,pageSize); return new R(true, iBookService.page(page,null)); } } 4.异常消息处理
工具类:
@Data @AllArgsConstructor @NoArgsConstructor public class R { // 标志接口是否成功执行 private Boolean flag; // 调用接口返回数据格式 private Object data; // 返回状态信息 private String msg; public R(Boolean flag) { this.flag = flag; } public R(Boolean flag, Object data) { this.flag = flag; this.data = data; } public R(Boolean flag, String msg) { this.flag = flag; this.msg = msg; } }
@RestControllerAdvice public class ProjectExceptionAdvice { @ExceptionHandler public R doException(Exception e) { return new R(false, "服务器故障,稍后再试"); } }
新增接口修改:
@PostMapping public R save(@RequestBody Book book){ boolean flag = iBookService.save(book); return new R(flag, flag ? "新增成功" : "新增失败"); }
前端save接口调用:
//添加 handleAdd () { axios.post("/books", this.formData).then((res) => { // 判断是否添加成功 if (res.data.flag) { // 关闭弹窗 this.dialogFormVisible = false; this.$message({ message: '新增成功', type: 'success' }); } else { // this.$message.error('新增失败'); this.$message.error(res.data.msg); } }).finally(() => { // 重新加载一下数据 this.getAll(); }) },
6.前后端协议联调
项目代码地址:git@github.com:Crashinging/ssmp.git
本部分到此为止了…
- 使用QueryWrapper对象封装查询条件,推荐使用LambdaQueryWrapper对象,所有查询操作封装成方法调用
- 在输入框里面填入配置文件的名
- 书写简化
- 传统风格资源描述形式:
- 内嵌Tomcat
- stater
- parent
-
还没有评论,来说两句吧...