视频地址:https://www.bilibili.com/video/BV15b4y1a7yG?p=24&share_source=copy_web
部分学习笔记:https://blog.csdn.net/qq_42324086/article/details/121184789
starter
SpringBoot中常见项目名称,定义了当前项目使用的所有依赖坐标,以达到减少依赖配置的目的
parent
所有SpringBoot项目要继承的项目,定义了若干个坐标版本号(依赖管理,而非依赖),以达到减少依赖冲突的目的
spring-boot-starter-parent各版本间存在着诸多坐标版本不同
实际开发
使用任意坐标时,仅书写GAV(groupId, artifactId, version)中的G和A,V由SpringBoot提供,除非SpringBoot未提供对应版本V
如发生坐标错误,再指定Version(要小心版本冲突)
启动方式
@SpringBootApplication
public class Springboot0101QuickstartApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Springboot0101QuickstartApplication.class, args);
//获取bean对象
BookController bean = ctx.getBean(BookController.class);
System.out.println("bean======>" + bean);
}
}
SpringBoot的引导类是Boot工程的执行入口,运行main方法就可以启动项目
SpringBoot工程运行后初始化Spring容器,扫描引导类所在包加载bean
Jetty比Tomcat更轻量级,可扩展性更强(相较于Tomcat),谷歌应用引擎(GAE)已经全面切换为Jetty
内置服务器
tomcat(默认) apache出品,粉丝多,应用面广,负载了若干较重的组件
jetty 更轻量级,负载性能远不及tomcat
undertow undertow,负载性能勉强跑赢tomcat
什么是 rest :
REST(Representational State Transfer)表现形式状态转换
传统风格资源描述形式
http://localhost/user/getById?id=1 (得到id为1的用户)
http://localhost/user/saveUser (保存用户)
REST风格描述形式
http://localhost/user/1 (得到id为1的用户)
http://localhost/user (保存用户)
优点:
隐藏资源的访问行为, 无法通过地址得知对资源是何种操作
书写简化
按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源
http://localhost/users 查询全部用户信息 GET (查询)
http://localhost/users/1 查询指定用户信息 GET (查询)
http://localhost/users 添加用户信息 POST (新增/保存)
http://localhost/users 修改用户信息 PUT (修改/更新)
http://localhost/users/1 删除用户信息 DELETE (删除)
注意:
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如: users、
步骤:
①设定http请求动作(动词)
使用 @RequestMapping 注解的 method 属性声明请求的方式
使用 @RequestBody 注解 获取请求体内容。直接使用得到是 key=value&key=value…结构的数据。get 请求方式不适用。
使用@ResponseBody 注解实现将 controller 方法返回对象转换为 json 响应给客户端。
@RequestMapping(value=“/users”,method=RequestMethod.POST)
②:设定请求参数(路径变量)
使用@PathVariable
用于绑定 url 中的占位符。例如:请求 url 中 /delete/{id}
,这个{id}
就是 url 占位符。
@RequestMapping
@PathVariable
@RequestBody @RequestParam @PathVariable
使用 @RestController
注解开发 RESTful 风格
使用 @GetMapping @PostMapping @PutMapping @DeleteMapping 简化 @RequestMapping
注解开发
# 服务器端口配置
server.port=80
# 修改banner
# spring.main.banner-mode=off
# spring.banner.image.location=logo.png
# 日志
logging.level.root=info
SpringBoot提供了多种属性配置方式
application.properties
server.port=80
application.yml
server:
port: 81
application.yaml
server:
port: 82
yaml
YAML(YAML Ain’t Markup Language),一种数据序列化格式
优点:
容易阅读
容易与脚本语言交互
以数据为核心,重数据轻格式
YAML文件扩展名
.yml(主流)
.yaml
yaml语法规则
基本语法
key: value -> value 前面一定要有空格
大小写敏感
属性层级关系使用多行描述,每行结尾使用冒号结束
使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
# 表示注释
核心规则:数据前面要加空格与冒号隔开
server:
servlet:
context-path: /hello
port: 82
# 字面值表示方式
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: 2018-02-17 # 日期必须使用 yyyy-MM-dd 格式
datetime: 2018-02-17T15:02:31+08:00 # 时间和日期之间使用 T 连接,最后使用 + 代表时区
subject:
- Java
- 前端
- 大数据
enterprise:
name: zhangsan
age: 16
subject2:
- Java
- 前端
- 大数据
likes: [王者荣耀,刺激战场] # 数组书写缩略格式
users: # 对象数组格式
- name: Tom
age: 4
- name: Jerry
age: 5
users2: # 对象数组格式二
-
name: Tom
age: 4
-
name: Jerry
age: 5
# 对象数组缩略格式
users3: [ { name:Tom , age:4 } , { name:Jerry , age:5 } ]
@Value("${country}")
private String country1;
@Value("${user.age}")
private String age1;
@Value("${likes[1]}")
private String likes1;
@Value("${users[1].name}")
private String name1;
@GetMapping
public String getById() {
System.out.println("springboot is running2...");
System.out.println("country1=>" + country1);
System.out.println("age1=>" + age1);
System.out.println("likes1=>" + likes1);
System.out.println("name1=>" + name1);
return "springboot is running2...";
}
# 创建类,用于封装下面的数据
# 由spring帮我们去加载数据到对象中,一定要告诉spring加载这组信息
# 使用时候从spring中直接获取信息使用
datasource:
driver: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost/springboot_db
username: root
password: root666123
//1.定义数据模型封装yaml文件中对应的数据
//2.定义为spring管控的bean
@Component
//3.指定加载的数据
@ConfigurationProperties(prefix = "datasource")
public class MyDataSource {
private String driver;
private String url;
private String username;
private String password;
//省略get/set/tostring 方法
}
使用自动装配封装指定数据
@Autowired
private MyDataSource myDataSource;
输出查看
System.out.println(myDataSource);
添加Junit的起步依赖 Spring Initializr 创建时自带
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
SpringBoot整合JUnit
@SpringBootTest
class Springboot07JunitApplicationTests {
@Autowired
private BookService bookService;
@Test
public void testSave(){
bookService.save();
}
}
@SpringBootTest
名称:@SpringBootTest
类型:测试类注解
位置:测试类定义上方
作用:设置JUnit加载的SpringBoot启动类
范例:
@SpringBootTest
class Springboot05JUnitApplicationTests {}
@SpringBootTest(classes = Springboot04JunitApplication.class)
//@ContextConfiguration(classes = Springboot04JunitApplication.class)
class Springboot04JunitApplicationTests {
//1.注入你要测试的对象
@Autowired
private BookDao bookDao;
@Test
void contextLoads() {
//2.执行要测试的对象对应的方法
bookDao.save();
System.out.println("two...");
}
}
注意:
①:创建新模块,选择Spring初始化,并配置模块相关基础信息
②:选择当前模块需要使用的技术集(MyBatis、MySQL)
③:设置数据源参数
#DB Configuration:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db
username: root
password: 123456
④:创建user表
在 springboot_db 数据库中创建 user 表
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'zhangsan', '123', '张三');
INSERT INTO `user` VALUES ('2', 'lisi', '123', '李四');
⑤:创建实体Bean
public class User {
// 主键
private Long id;
// 用户名
private String username;
// 密码
private String password;
// 姓名
private String name;
//此处省略getter,setter,toString方法 .. ..
}
⑥: 定义数据层接口与映射配置
@Mapper
public interface UserDao {
@Select("select * from user")
public List<User> getAll();
}
⑦:测试类中注入dao接口,测试功能
@SpringBootTest
class Springboot05MybatisApplicationTests {
@Autowired
private UserDao userDao;
@Test
void contextLoads() {
List<User> userList = userDao.getAll();
System.out.println(userList);
}
}
⑧:运行如下
[User{id=1, username='zhangsan', password='123', name='张三'}, User{id=2, username='lisi', password='123', name='李四'}]
SpringBoot版本低于2.4.3(不含),Mysql驱动版本大于8.0时,需要在url连接串中配置时区
jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
或在MySQL数据库端配置时区解决此问题
1.MySQL 8.X驱动强制要求设置时区
修改url,添加serverTimezone设定
修改MySQL数据库配置(略)
2.驱动类过时,提醒更换为com.mysql.cj.jdbc.Driver
①:手动添加SpringBoot整合MyBatis-Plus的坐标,可以通过mvnrepository获取
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.3version>
dependency>
注意事项: 由于SpringBoot中未收录MyBatis-Plus的坐标版本,需要指定对应的Version
②:定义数据层接口与映射配置,继承BaseMapper
@Mapper
public interface UserDao extends BaseMapper {
}
③:其他同SpringBoot整合MyBatis
(略)
④:测试类中注入dao接口,测试功能
@SpringBootTest
class Springboot06MybatisPlusApplicationTests {
@Autowired
private UserDao userDao;
@Test
void contextLoads() {
List<User> users = userDao.selectList(null);
System.out.println(users);
}
}
⑤: 运行如下:
[User{id=1, username='zhangsan', password='123', name='张三'}, User{id=2, username='lisi', password='123', name='李四'}]
注意: 如果你的数据库表有前缀要在 application.yml 添加如下配制
#设置Mp相关的配置
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
①: 导入Druid对应的starter
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.6version>
dependency>
#DB Configuration:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: Lemon
type: com.alibaba.druid.pool.DruidDataSource
②: 指定数据源类型 (这种方式只需导入一个 Druid 的坐标)
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: 123456
或者 变更Druid的配置方式(推荐) 这种方式需要导入 Druid对应的starter
1. 案例实现方案分析
实体类开发————使用Lombok快速制作实体类
Dao开发————整合MyBatisPlus,制作数据层测试类
Service开发————基于MyBatisPlus进行增量开发,制作业务层测试类
Controller开发————基于Restful开发,使用PostMan测试接口功能
Controller开发————前后端开发协议制作
页面开发————基于VUE+ElementUI制作,前后端联调,页面数据处理,页面消息处理
列表、新增、修改、删除、分页、查询
项目异常处理
按条件查询————页面功能调整、Controller修正功能、Service修正功能
2. SSMP案例制作流程解析
先开发基础CRUD功能,做一层测一层
调通页面,确认异步提交成功后,制作所有功能
添加分页功能与查询功能
DROP TABLE IF EXISTS `tbl_book`;
CREATE TABLE `tbl_book` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` varchar(20) DEFAULT NULL,
`name` varchar(50) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of tbl_book
-- ----------------------------
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核心技术卷|基础知识(原书第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+');
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.3version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.6version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.3version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.2.6version>
dependency>
dependencies>
server:
port: 80
# druid 数据源配制
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC
username: root
password: Lemon
# mybatis-plus
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
id-type: auto # 主键策略
Book.class
@Data
public class Book {
private Integer id;
private String type;
private String name;
private String description;
}
BookDao.class
@Mapper
public interface BookDao extends BaseMapper<Book> {
/**
* 查询一个
* 这是 Mybatis 开发
* @param id
* @return
*/
@Select("select * from tbl_book where id = #{id}")
Book getById(Integer id);
}
测试类
@SpringBootTest
public class BookDaoTestCase {
@Autowired
private BookDao bookDao;
@Test
void testGetById() {
System.out.println(bookDao.getById(1));
System.out.println(bookDao.selectById(1));
}
@Test
void testSave() {
Book book = new Book();
book.setType("测试数据123");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookDao.insert(book);
}
@Test
void testUpdate() {
Book book = new Book();
book.setId(13);
book.setType("测试数据asfd");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookDao.updateById(book);
}
@Test
void testDelete() {
bookDao.deleteById(13);
}
@Test
void testGetAll() {
System.out.println(bookDao.selectList(null));
}
@Test
void testGetPage() {
}
@Test
void testGetBy() {
}
}
开启MybatisPlus运行日志
# mybatis-plus
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
id-type: auto # 主键策略
configuration:
# 开启MyBatisPlus的日志
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 MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
//1. 定义 Mp 拦截器
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//2. 添加具体的拦截器 分页拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
测试
@Test
void testGetPage() {
IPage page = new Page(1, 5);
bookDao.selectPage(page, null);
System.out.println(page.getCurrent());
System.out.println(page.getSize());
System.out.println(page.getPages());
System.out.println(page.getTotal());
System.out.println(page.getRecords());
}
@Test
void testGetBy2() {
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(Book::getName, "Spring");
bookDao.selectList(lambdaQueryWrapper);
}
@Test
void testGetBy() {
QueryWrapper<Book> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "Spring");
bookDao.selectList(queryWrapper);
}
@Test
void testGetBy2() {
String name = "1";
LambdaQueryWrapper<Book> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//if (name != null) lambdaQueryWrapper.like(Book::getName,name);
lambdaQueryWrapper.like(Strings.isNotEmpty(name), Book::getName, name);
bookDao.selectList(lambdaQueryWrapper);
}
Service层接口定义与数据层接口定义具有较大区别,不要混用
selectByUserNameAndPassword(String username,String password); 数据层接口
login(String username,String password); Service层接口
接口定义
public interface BookService {
Boolean save(Book book);
Boolean update(Book book);
Boolean delete(Integer id);
Book getById(Integer id);
List<Book> getAll();
IPage<Book> getPage(int currentPage,int pageSize);
}
实现类定义
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public Boolean save(Book book) {
return bookDao.insert(book) > 0;
}
@Override
public Boolean update(Book book) {
return bookDao.updateById(book) > 0;
}
@Override
public Boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
@Override
public Book getById(Integer id) {
return bookDao.selectById(id);
}
@Override
public List<Book> getAll() {
return bookDao.selectList(null);
}
@Override
public IPage<Book> getPage(int currentPage, int pageSize) {
IPage page = new Page(currentPage, pageSize);
bookDao.selectPage(page, null);
return page;
}
}
@SpringBootTest
public class BookServiceTestCase {
@Autowired
private BookService bookService;
@Test
void testGetById() {
System.out.println(bookService.getById(4));
}
@Test
void testSave() {
Book book = new Book();
book.setType("测试数据123");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookService.save(book);
}
@Test
void testUpdate() {
Book book = new Book();
book.setId(14);
book.setType("测试数据asfd");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookService.update(book);
}
@Test
void testDelete() {
bookService.delete(14);
}
@Test
void testGetAll() {
System.out.println(bookService.getAll());
}
@Test
void testGetPage() {
IPage<Book> page = bookService.getPage(2, 5);
System.out.println(page.getCurrent());
System.out.println(page.getSize());
System.out.println(page.getPages());
System.out.println(page.getTotal());
System.out.println(page.getRecords());
}
}
快速开发方案
使用MyBatisPlus提供有业务层通用接口(ISerivce)与业务层通用实现类(ServiceImpl
在通用类基础上做功能重载或功能追加
注意重载时不要覆盖原始操作,避免原始提供的功能丢失
接口定义
public interface IBookService extends IService<Book> {
}
public interface IBookService extends IService<Book> {
// 追加的操作与原始操作通过名称区分,功能类似
Boolean delete(Integer id);
Boolean insert(Book book);
Boolean modify(Book book);
Book get(Integer id);
}
@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
}
@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
@Autowired
private BookDao bookDao;
public Boolean insert(Book book) {
return bookDao.insert(book) > 0;
}
public Boolean modify(Book book) {
return bookDao.updateById(book) > 0;
}
public Boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
public Book get(Integer id) {
return bookDao.selectById(id);
}
}
@SpringBootTest
public class BookServiceTest {
@Autowired
private IBookService bookService;
@Test
void testGetById() {
System.out.println(bookService.getById(4));
}
@Test
void testSave() {
Book book = new Book();
book.setType("测试数据123");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookService.save(book);
}
@Test
void testUpdate() {
Book book = new Book();
book.setId(14);
book.setType("===========");
book.setName("测试数据123");
book.setDescription("测试数据123");
bookService.updateById(book);
}
@Test
void testDelete() {
bookService.removeById(14);
}
@Test
void testGetAll() {
System.out.println(bookService.list());
}
@Test
void testGetPage() {
IPage<Book> page = new Page<>(2, 5);
bookService.page(page);
System.out.println(page.getCurrent());
System.out.println(page.getSize());
System.out.println(page.getPages());
System.out.println(page.getTotal());
System.out.println(page.getRecords());
}
}
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private IBookService bookService;
@GetMapping
public List<Book> getAll() {
return bookService.list();
}
@PostMapping
public Boolean save(@RequestBody Book book) {
return bookService.save(book);
}
@PutMapping
public Boolean update(@RequestBody Book book) {
return bookService.modify(book);
}
@DeleteMapping("{id}")
public Boolean delete(@PathVariable Integer id) {
return bookService.delete(id);
}
@GetMapping("{id}")
public Book getById(@PathVariable Integer id) {
return bookService.getById(id);
}
@GetMapping("{currentPage}/{pageSize}")
public IPage<Book> getPage(@PathVariable Integer currentPage, @PathVariable int pageSize) {
return bookService.getPage(currentPage, pageSize);
}
}
添加 分页的业务层方法
IBookService
IPage<Book> getPage(int currentPage,int pageSize);
BookServiceImpl
@Override
public IPage<Book> getPage(int currentPage, int pageSize) {
IPage page = new Page(currentPage, pageSize);
bookDao.selectPage(page, null);
return page;
}
统一格式
@Data
public class R {
private Boolean flag;
private Object data;
public R() {
}
/**
* 不返回数据的构造方法
*
* @param flag
*/
public R(Boolean flag) {
this.flag = flag;
}
/**
* 返回数据的构造方法
*
* @param flag
* @param data
*/
public R(Boolean flag, Object data) {
this.flag = flag;
this.data = data;
}
}
@RestController
@RequestMapping("/books")
public class BookController {
@Autowired
private IBookService bookService;
@GetMapping
public R getAll() {
return new R(true, bookService.list());
}
@PostMapping
public R save(@RequestBody Book book) {
return new R(bookService.save(book));
}
@PutMapping
public R update(@RequestBody Book book) {
return new R(bookService.modify(book));
}
@DeleteMapping("{id}")
public R delete(@PathVariable Integer id) {
return new R(bookService.delete(id));
}
@GetMapping("{id}")
public R getById(@PathVariable Integer id) {
return new R(true, bookService.getById(id));
}
@GetMapping("{currentPage}/{pageSize}")
public R getPage(@PathVariable Integer currentPage, @PathVariable int pageSize) {
return new R(true, bookService.getPage(currentPage, pageSize));
}
}
前端部分省略
步骤
①:对SpringBoot项目打包(执行Maven构建指令package)
执行 package 打包命令之前 先执行 mvn clean 删除 target 目录及内容
mvn package
可能出现的问题: IDEA下 执行 Maven 命令控制台中文乱码
Ctr+Alt+S 打开设置,在Build,Execution ,Deployment找到Build Tools下Maven项下的Runner ,在VM Options 添加
-Dfile.encoding=GB2312 ,点击OK。
②:运行项目(执行启动指令) java -jar <打包文件名>
java –jar springboot.jar
注意事项:
jar支持命令行启动需要依赖maven插件支持,请确认打包时是否具有SpringBoot对应的maven插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
执行 java -jar springboot_08_ssmp-0.0.1-SNAPSHOT.jar
如果没有配制spring boot 打包插件可能遇到下面的问题:
使用SpringBoot提供的maven插件可以将工程打包成可执行jar包
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
ar包描述文件(MANIFEST.MF)
普通工程
Manifest-Version: 1.0
Implementation-Title: springboot_08_ssmp
Implementation-Version: 0.0.1-SNAPSHOT
Build-Jdk-Spec: 1.8
Created-By: Maven Jar Plugin 3.2.0
基于spring-boot-maven-plugin打包的工程
Manifest-Version: 1.0
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: springboot_08_ssmp
Implementation-Version: 0.0.1-SNAPSHOT
Spring-Boot-Layers-Index: BOOT-INF/layers.idx
Start-Class: com.example.SSMPApplication 启动类
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.5.6
Created-By: Maven Jar Plugin 3.2.0
Main-Class: org.springframework.boot.loader.JarLauncher jar启动器
命令行启动常见问题及解决方案
# 查询端口
netstat -ano
# 查询指定端口
netstat -ano |findstr "端口号"
# 根据进程PID查询进程名称
tasklist |findstr "进程PID号"
# 根据PID杀死任务
taskkill /F /PID "进程PID号"
# 根据进程名称杀死任务
taskkill -f -t -im "进程名称"
基于Linux(CenterOS7)
安装JDK,且版本不低于打包时使用的JDK版本
安装 MySQL
安装包保存在/usr/local/自定义目录中或$HOME下
其他操作参照Windows版进行
启动成功无法访问
添加 80 端口
firewall-cmd --zone=public --permanent --add-port=80/tcp
重启
systemctl restart firewalld
后台启动命令
nohup java -jar springboot_08_ssmp-0.0.1-SNAPSHOT.jar > server.log 2>&1 &
停止服务
[root@cjbCentos01 app]# ps -ef | grep "java -jar"
UID PID PPID C STIME TTY TIME CMD
root 6848 6021 7 14:45 pts/2 00:00:19 java -jar springboot_08_ssmp-0.0.1-SNAPSHOT.jar
root 6919 6021 0 14:49 pts/2 00:00:00 grep --color=auto java -jar
[root@cjbCentos01 app]# kill -9 6848
[root@cjbCentos01 app]# ps -ef | grep "java -jar"
root 7016 6021 0 14:52 pts/2 00:00:00 grep --color=auto java -jar
[1]+ 已杀死 nohup java -jar springboot_08_ssmp-0.0.1-SNAPSHOT.jar > server.log 2>&1
[root@cjbCentos01 app]#
java -jar springboot_08_ssmp-0.0.1-SNAPSHOT.jar --server.port=8080
属性加载优先顺序
在启动类中 main 可以通过 System.out.println(Arrays.toString(args)); 查看配制的属性
通过编程形式带参数启动SpringBoot程序,为程序添加运行参数
public static void main(String[] args) {
String[] arg = new String[1];
arg[0] = "--server.port=8080";
SpringApplication.run(SSMPApplication.class, arg);
}
不携带参数启动SpringBoot程序
public static void main(String[] args) {
//可以在启动boot程序时断开读取外部临时配置对应的入口,也就是去掉读取外部参数的形参
SpringApplication.run(SSMPApplication.class);
}
SpringBoot中4级配置文件
1级:file :config/application.yml 【最高】
2级:file :application.yml
3级:classpath:config/application.yml
4级:classpath:application.yml 【最低】
作用:
1级与2级留做系统打包后设置通用属性,1级常用于运维经理进行线上整体项目部署方案调控
3级与4级用于系统开发阶段设置通用属性,3级常用于项目经理进行整体项目属性调控
通过启动参数加载配置文件(无需书写配置文件扩展名) --spring.config.name=eban
properties与yml文件格式均支持
properties与yml文件格式均支持
--spring.config.location=classpath:/ebank.yml,classpath:/ebank-server.yml
注意事项:
多配置文件常用于将配置进行分类,进行独立管理,或将可选配置单独制作便于上线更新维护
自定义配置文件——重要说明
单服务器项目:使用自定义配置文件需求较低
多服务器项目:使用自定义配置文件需求较高,将所有配置放置在一个目录中,统一管理
基于SpringCloud技术,所有的服务器将不再设置配置文件,而是通过配置中心进行设定,动态加载配置信息
#应用环境
#公共配制
spring:
profiles:
active: dev
#设置环境
#开发环境
---
spring:
config:
activate:
on-profile: dev
server:
port: 81
#生产环境
---
spring:
profiles: pro
server:
port: 80
#测试环境
---
spring:
profiles: test
server:
port: 82
多环境开发(YAML版)多配置文件格式
主启动配置文件application.yml
#应用环境
#公共配制
spring:
profiles:
active: test
环境分类配置文件application-pro.yml
server:
port: 81
环境分类配置文件application-dev.yml
server:
port: 82
环境分类配置文件application-test.yml
server:
port: 83
根据功能对配置文件中的信息进行拆分,并制作成独立的配置文件,命名规则如下
application-devDB.yml
application-devRedis.yml
application-devMVC.yml
使用include属性在激活指定环境的情况下,同时对多个环境进行加载使其生效,多个环境间使用逗号分隔
spring:
profiles:
active: dev
include: devDB,devMVC
注意事项:
当主环境dev与其他环境有相同属性时,主环境属性生效;其他环境中有相同属性时,最后加载的环境属性生效
The following profiles are active: devDB,devMVC,dev
从Spring2.4版开始使用group属性替代include属性,降低了配置书写量
使用group属性定义多种主环境与子环境的包含关系
spring:
profiles:
active: dev
group:
"dev": devDB,devMVC
"pro": proDB,proMVC
"test": testDB,testRedis,testMVC
注意事项:
使用group属性,会覆盖 主环境dev (active) 的内容,最后加载的环境属性生效
The following profiles are active: dev,devDB,devMVC
Maven与SpringBoot多环境兼容
①:Maven中设置多环境属性
<profiles>
<profile>
<id>dev_envid>
<properties>
<profile.active>devprofile.active>
properties>
<activation>
<activeByDefault>trueactiveByDefault>
activation>
profile>
<profile>
<id>pro_envid>
<properties>
<profile.active>proprofile.active>
properties>
profile>
<profile>
<id>test_envid>
<properties>
<profile.active>testprofile.active>
properties>
profile>
profiles>
②:SpringBoot中引用Maven属性
spring:
profiles:
active: @profile.active@
group:
"dev": devDB,devMVC
"pro": proDB,proMVC
③:执行Maven打包指令,并在生成的boot打包文件.jar文件中查看对应信息
问题:修改pom.xml 文件后,启动没有生效 手动 compile 即可
日志(log)作用
代码中使用日志工具记录日志
先引入 Lombok 工具类
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
①:添加日志记录操作
@RestController
@RequestMapping("/books")
public class BookController {
private static final Logger log = LoggerFactory.getLogger(BookController.class);
@GetMapping
public String getById() {
System.out.println("springboot is running...");
log.debug("debug ...");
log.info("info ...");
log.warn("warn ...");
log.error("error ...");
return "springboot is running...";
}
}
日志级别
TRACE:运行堆栈信息,使用率低
DEBUG:程序员调试代码使用
INFO:记录运维过程数据
WARN:记录运维过程报警数据
ERROR:记录错误堆栈信息
FATAL:灾难信息,合并计入ERRO
②:设置日志输出级别
# 开启 debug 模式,输出调试信息,常用于检查系统运行状况
debug: true
# 设置日志级别, root 表示根节点,即整体应用日志级别
logging:
level:
root: debug
③:设置日志组,控制指定包对应的日志输出级别,也可以直接控制指定包对应的日志输出级别
logging:
# 设置分组
group:
# 自定义组名,设置当前组中所包含的包
ebank: com.example.controller,com.example.service,com.example.dao
iservice: com.alibaba
level:
root: info
# 设置某个包的日志级别
# com.example.controller: debug
# 为对应组设置日志级别
ebank: warn
使用lombok提供的注解@Slf4j简化开发,减少日志对象的声明操作
@Slf4j
//Rest模式
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public String getById(){
System.out.println("springboot is running...2");
log.debug("debug...");
log.info("info...");
log.warn("warn...");
log.error("error...");
return "springboot is running...2";
}
}
logging:
pattern:
console: "%d - %m%n"
logging:
pattern:
# console: "%d - %m%n"
console: "%d %clr(%5p) --- [%16t] %clr(%-40.40c){cyan} : %m %n"
logging:
file:
name: server.log
logging:
file:
name: server.log
logback:
rollingpolicy:
max-file-size: 4KB
file-name-pattern: server.%d{yyyy-MM-dd}.%i.log
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<optional>trueoptional>
dependency>
spring:
# 热部署范围配置
devtools:
restart:
# 设置不参与热部署的文件和文件夹(即修改后不重启)
exclude: static/**,public/**,config/application.yml
#是否可用
enabled: false
如果配置文件比较多的时候找热部署对应配置比较麻烦,可以在springboot
启动类的main方法中设置,此处设置的优先级将比配置文件高,一定会生效。
System.setProperty("spring.devtools.restart.enabled", "false");
@ConfigurationProperties(prefix="xxx")
,xxx跟application.yml配置文件中的属性对应;@EnableConfigurationProperties({xxx.class, yyy.class})
的方式完成配置,不过该注解会与@Component配置发生冲突,二选一即可;@ConfigurationProperties(prefix="xxx")
注解,然后方法返回这个第三方对象的方式。@ConfigurationProperties(prefix="xxx")
注解后idea工具会报一个警告Spring Boot Configuration Annotation Processor not configured@ConfigurationProperties(prefix="xxx")
@Data
public class ServerConfig {
private String inAddress;
private int port;
private long timeout;
}
@ConfigurationProperties
绑定属性支持属性名宽松绑定,又叫松散绑定。
比如要将ServerConfig.class
作为配置类,并通过配置文件application.yml
绑定属性
ServerConfig.class
serverConfig:
# ipAddress: 192.168.0.1 # 驼峰模式
# ipaddress: 192.168.0.1
# IPADDRESS: 192.168.0.1
ip-address: 192.168.0.1 # 主流配置方式,烤肉串模式
# ip_address: 192.168.0.1 # 下划线模式
# IP_ADDRESS: 192.168.0.1 # 常量模式
# ip_Add_rEss: 192.168.0.1
# ipaddress: 192.168.0.1
port: 8888
timeout: -1
以ipAddress属性为例,上面的多种配置方式皆可生效,这就是松散绑定。而@Value不支持松散绑定,必须一一对应。
@ConfigurationProperties(prefix="serverconfig")
中的prefix的值为serverconfig或者server-config,如果是serverConfig就会报错,这与松散绑定的前缀命名规范有关:仅能使用纯小写字母、数字、中划线作为合法的字符
//@Component
@ConfigurationProperties(prefix = "server-config")
@Data
public class ServerConfig {
private String ipAddress;
private int port;
@DurationUnit(ChronoUnit.MINUTES)
private Duration timeout;
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize dataSize;
}
引入Bean
属性校验框架的步骤:
pom.xml
中添加JSR303
规范和hibernate
校验框架的依赖:
<dependency>
<groupId>javax.validationgroupId>
<artifactId>validation-apiartifactId>
dependency>
<dependency>
<groupId>org.hibernate.validatorgroupId>
<artifactId>hibernate-validatorartifactId>
dependency>
在要校验的类上加@Validated
注解
设置具体的校验规则,如:@Max(value=8888, message="最大值不能超过8888")
@ConfigurationProperties(prefix = "server-config")
@Data
// 2.开启对当前bean的属性注入校验
@Validated
public class ServerConfig {
private String ipAddress;
// 设置具体的规则
@Max(value = 8888, message = "最大值不能超过8888")
@Min(value = 1000, message = "最小值不能低于1000")
private int port;
@DurationUnit(ChronoUnit.MINUTES)
private Duration timeout;
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize dataSize;
}
进制转换中的一些问题:
如application.yml
文件中对数据库有如下配置:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver123
# 不加引号读取的时候默认解析为了8进制数,转成十进制就是87
# 所以想让这里正确识别,需要加上引号
# password: 0127
password: "0127"
@SpringBootTest注解中可以设置properties和args属性,这里的args属性的作用跟idea工具中自带的程序参数类似,只不过这里的配置是源码级别的,会随着源码的移动而跟随,而idea中的程序参数的配置会丢失。并且这里的args属性的配置的作用范围比较小,仅在当前测试类生效。
application.yml
test:
prop: testValue
// properties属性可以为当前测试用例添加临时的属性配置
//@SpringBootTest(properties = {"test.prop=testValue1"})
// args属性可以为当前测试用例添加临时的命令行参数
//@SpringBootTest(args = {"--test.prop=testValue2"})
// 优先级排序: args > properties > 配置文件
@SpringBootTest(args = {"--test.prop=testValue2"}, properties = {"test.prop=testValue1"})
class PropertiesAndArgsTest {
@Value("${test.prop}")
private String prop;
@Test
public void testProperties() {
System.out.println("prop = " + prop);
}
}
某些测试类中需要用到第三方的类,而其他测试类则不需要用到,这里可以在类上加载@Import({xxx.class, yyy.class})
@RestController
@RequestMapping("/books")
public class BookController {
/*@GetMapping("/{id}")
public String getById(@PathVariable int id) {
System.out.println("id = " + id);
return "getById...";
}*/
@GetMapping("/{id}")
public Book getById(@PathVariable int id) {
System.out.println("id = " + id);
Book book = new Book();
book.setId(5);
book.setName("springboot");
book.setType("springboot");
book.setDescription("springboot");
return book;
}
}
相应测试类
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// 开启虚拟mvc调用
@AutoConfigureMockMvc
public class WebTest {
@Test
public void testRandomPort() {
}
@Test
public void testWeb(@Autowired MockMvc mvc) throws Exception {
// 创建虚拟请求,当前访问 /books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/5");
mvc.perform(builder);
}
@Test
public void testStatus(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books1/6");
ResultActions action = mvc.perform(builder);
// 设定预期值,与真实值进行比较,成功测试通过,失败测试不通过
// 定义本次调用的预期值
StatusResultMatchers srm = MockMvcResultMatchers.status();
// 预计本次调用成功的状态码:200
ResultMatcher ok = srm.isOk();
// 添加预计值到本次调用过程中进行匹配
action.andExpect(ok);
}
@Test
public void testBody(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/6");
ResultActions action = mvc.perform(builder);
// 设定预期值,与真实值进行比较,成功测试通过,失败测试不通过
// 定义本次调用的预期值
ContentResultMatchers crm = MockMvcResultMatchers.content();
// 预计本次调用成功的状态码:200
ResultMatcher rm = crm.string("getById...");
// 添加预计值到本次调用过程中进行匹配
action.andExpect(rm);
}
@Test
public void testJson(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/7");
ResultActions action = mvc.perform(builder);
// 设定预期值,与真实值进行比较,成功测试通过,失败测试不通过
// 定义本次调用的预期值
ContentResultMatchers jsonMatcher = MockMvcResultMatchers.content();
ResultMatcher rm = jsonMatcher.json("{\"id\":5,\"name\":\"springboot\",\"type\":\"springboot\",\"description\":\"springboot1\"}");
action.andExpect(rm);
}
@Test
public void testContentType(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/7");
ResultActions action = mvc.perform(builder);
// 设定预期值,与真实值进行比较,成功测试通过,失败测试不通过
// 定义本次调用的预期值
HeaderResultMatchers hrm = MockMvcResultMatchers.header();
ResultMatcher rm = hrm.string("Content-Type", "application/json");
action.andExpect(rm);
}
@Test
// 完整测试
public void testGetById(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/8");
ResultActions action = mvc.perform(builder);
// 1、比较状态码
StatusResultMatchers statusResultMatchers = MockMvcResultMatchers.status();
ResultMatcher statusResultMatcher = statusResultMatchers.isOk();
action.andExpect(statusResultMatcher);
// 2、比较返回值类型
HeaderResultMatchers headerResultMatchers = MockMvcResultMatchers.header();
ResultMatcher headerResultMatcher = headerResultMatchers.string("Content-Type", "application/json");
action.andExpect(headerResultMatcher);
/// 3、比较json返回值
ContentResultMatchers contentResultMatchers = MockMvcResultMatchers.content();
ResultMatcher jsonResultMatcher = contentResultMatchers.json("{\"id\":5,\"name\":\"springboot\",\"type\":\"springboot\",\"description\":\"springboot\"}");
action.andExpect(jsonResultMatcher);
}
}
为测试用例添加事务,SpringBoot会对测试用例对应的事务提交操作进行回滚
@SpringBootTest
@Transactional public class DaoTest {
@Autowired
private BookService bookService; }
l如果想在测试用例中提交事务,可以通过@Rollback注解设置
@SpringBootTest
@Transactional
@Rollback(false)
public class DaoTest {
}
testcast:
book:
id: ${random.int} # 随机整数
id2: ${random.int(10)} # 10以内随机数
type: ${random.int(10,20)} # 10到20随机数
uuid: ${random.uuid} # 随机uuid
name: ${random.value} # 随机字符串,MD5字符串,32位
publishTime: ${random.long} # 随机整数(long范围)
u${random.int}表示随机整数
u${random.int(10)}表示10以内的随机数
u${random.int(10,20)}表示10到20的随机数
u其中()可以是任意字符,例如[],!!均可
现有数据层解决方案技术选型
Druid + MyBatis-Plus + MySQL
格式一:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: root
格式二:
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: root
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: root
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: root
SpringBoot提供了3种内嵌的数据源对象供开发者选择
HikariCP:默认内置数据源对象
Tomcat提供DataSource:HikariCP不可用的情况下,且在web环境中,将使用tomcat服务器配置的数据源对象
Commons DBCP:Hikari不可用,tomcat数据源也不可用,将使用dbcp数据源
通用配置无法设置具体的数据源配置信息,仅提供基本的连接相关配置,如需配置,在下一级配置中设置具体设定
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db
username: root
password: root
hikari:
maximum-pool-size: 50
@SpringBootTest
class Springboot15SqlApplicationTests {
@Autowired
private JdbcTemplate jdbcTemplate;
@Test
void testJdbc(){
String sql = "select * from tbl_book where id = 1";
List<Book> query = jdbcTemplate.query(sql, new RowMapper<Book>() {
@Override
public Book mapRow(ResultSet rs, int rowNum) throws SQLException {
Book temp = new Book();
temp.setId(rs.getInt("id"));
temp.setName(rs.getString("name"));
temp.setType(rs.getString("type"));
temp.setDescription(rs.getString("description"));
return temp;
}
});
System.out.println(query);
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
spring:
jdbc:
template:
query-timeout: -1 # 查询超时时间
max-rows: 500 # 最大行数
fetch-size: -1 # 缓存行数
SpringBoot提供了3种内嵌数据库供开发者选择,提高开发测试效率
导入H2相关坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>com.h2databasegroupId>
<artifactId>h2artifactId>
<scope>runtimescope>
dependency>
设置当前项目为web工程,并配置H2管理控制台参数
server:
port: 80
spring:
h2:
console:
path: /h2
enabled: true
访问用户名sa,默认密码123456
操作数据库(创建表)
create table tbl_book (id int,name varchar,type varchar,description varchar)
#设置访问数据源
server:
port: 80
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:~/test
username: sa
password: 123456
h2:
console:
path: /h2
enabled: true
H2数据库控制台仅用于开发阶段,线上项目请务必关闭控制台功能
server:
port: 80
spring:
h2:
console:
path: /h2
enabled: false
SpringBoot可以根据url地址自动识别数据库种类,在保障驱动类存在的情况下,可以省略配置
server:
port: 80
spring:
datasource:
# driver-class-name: org.h2.Driver
url: jdbc:h2:~/test
username: sa
password: 123456
h2:
console:
path: /h2
enabled: true
MongoDB是一个开源、高性能、无模式的文档型数据库。NoSQL数据库产品中的一种,是最像关系型数据库的非关系型数据库
https://www.mongodb.com/try/download
解压缩后设置数据目录
服务端启动
在bin目录下
mongod --dbpath=..\data\db
客户端启动
mongo --host=127.0.0.1 --port=27017
导入MongoDB驱动
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
dependency>
配置客户端
spring:
data:
mongodb:
uri: mongodb://localhost/itheima
客户端读写MongoDB
@Test
void testSave(@Autowired MongoTemplate mongoTemplate){
Book book = new Book();
book.setId(1);
book.setType("springboot");
book.setName("springboot");
book.setDescription("springboot");
mongoTemplate.save(book);
}
@Test
void testFind(@Autowired MongoTemplate mongoTemplate){
List<Book> all = mongoTemplate.findAll(Book.class);
System.out.println(all);
}
Elasticsearch是一个分布式全文搜索引擎
https://www.elastic.co/cn/downloads/elasticsearch
运行:elasticsearch.bat
put:http://localhost:9200/books
get:http://localhost:9200/books
delete:http://localhost:9200/books
IK分词器
下载:https://github.com/medcl/elasticsearch-analysis-ik/releases
创建索引并指定规则
{
"mappings":{
"properties":{
"id":{
"type":"keyword"
},
"name":{
"type":"text", "analyzer":"ik_max_word", "copy_to":"all"
},
"type":{
"type":"keyword"
},
"description":{
"type":"text", "analyzer":"ik_max_word", "copy_to":"all"
},
"all":{
"type":"text", "analyzer":"ik_max_word"
}
}
}
}
创建文档
post:http://localhost:9200/books/_doc
(使用系统生成的id)
post:http://localhost:9200/books/_create/1
(使用指定id)
post:http://localhost:9200/books/_doc/1
(使用指定id,不存在创建,存在更新,版本递增)
{
"name":"springboot",
"type":"springboot",
"description":"springboot"
}
查询文档
get:http://localhost:9200/books/_doc/1
get:http://localhost:9200/books/_search
条件查询
get:http://localhost:9200/books/_search?q=name:springboot
删除文档
delete:http://localhost:9200/books/_doc/1
修改文档(全量修改)
put:http://localhost:9200/books/_doc/1
{
"name":"springboot",
"type":"springboot",
"description":"springboot"
}
修改文档(部分修改)
post:http://localhost:9200/books/_update/1
{
"doc":{
"name":"springboot"
}
}
导入坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
配置
spring:
elasticsearch:
rest:
uris: http://localhost:9200
客户端
@SpringBootTest
class Springboot18EsApplicationTests {
@Autowired
private ElasticsearchRestTemplate template;
}
SpringBoot平台并没有跟随ES的更新速度进行同步更新,ES提供了High Level Client操作ES
导入坐标
<dependency>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>elasticsearch-rest-high-level-clientartifactId>
dependency>
无需配置
客户端
@Test
void test() throws IOException {
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
RestHighLevelClient client = new RestHighLevelClient(builder);
//客户端操作
CreateIndexRequest request = new CreateIndexRequest("books");
//获取操作索引的客户端对象,调用创建索引操作
client.indices().create(request, RequestOptions.DEFAULT);
//关闭客户端
client.close();
}
客户端改进
@SpringBootTest
class Springboot18EsApplicationTests {
@Autowired
private BookDao bookDao;
@Autowired
RestHighLevelClient client;
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(HttpHost.create("http://localhost:9200")));
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
@Test
void test() throws IOException {
//客户端操作
CreateIndexRequest request = new CreateIndexRequest("books");
//获取操作索引的客户端对象,调用创建索引操作
client.indices().create(request, RequestOptions.DEFAULT);
}
}
创建索引
//创建索引
@Test
void testCreateIndexByIK() throws IOException {
HttpHost host = HttpHost.create("http://localhost:9200");
RestClientBuilder builder = RestClient.builder(host);
RestHighLevelClient client = new RestHighLevelClient(builder);
//客户端操作
CreateIndexRequest request = new CreateIndexRequest("books");
//设置要执行操作
String json = "";
//设置请求参数,参数类型json数据
request.source(json,XContentType.JSON);
//获取操作索引的客户端对象,调用创建索引操作
client.indices().create(request, RequestOptions.DEFAULT);
//关闭客户端
client.close();
}
String json = "{\n" +
" \"mappings\":{\n" +
" \"properties\":{\n" +
" \"id\":{\n" +
" \"type\":\"keyword\"\n" +
" },\n" +
" \"name\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\",\n" +
" \"copy_to\":\"all\"\n" +
" },\n" +
" \"type\":{\n" +
" \"type\":\"keyword\"\n" +
" },\n" +
" \"description\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\",\n" +
" \"copy_to\":\"all\"\n" +
" },\n" +
" \"all\":{\n" +
" \"type\":\"text\",\n" +
" \"analyzer\":\"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
添加文档
//添加文档
@Test
void testCreateDoc() throws IOException {
Book book = bookDao.selectById(1);
IndexRequest request = new IndexRequest("books").id(book.getId().toString());
String json = JSON.toJSONString(book);
request.source(json,XContentType.JSON);
client.index(request, RequestOptions.DEFAULT);
}
批量添加文档
//批量添加文档
@Test
void testCreateDocAll() throws IOException {
List<Book> bookList = bookDao.selectList(null);
BulkRequest bulk = new BulkRequest();
for (Book book : bookList) {
IndexRequest request = new IndexRequest("books").id(book.getId().toString());
String json = JSON.toJSONString(book);
request.source(json,XContentType.JSON);
bulk.add(request);
}
client.bulk(bulk,RequestOptions.DEFAULT);
}
按id查询文档
@Test
void testGet() throws IOException {
GetRequest request = new GetRequest("books","1");
GetResponse response = client.get(request, RequestOptions.DEFAULT);
String json = response.getSourceAsString();
System.out.println(json);
}
按条件查询文档
@Test
void testSearch() throws IOException {
SearchRequest request = new SearchRequest("books");
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(QueryBuilders.termQuery("all",“java"));
request.source(builder);
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
for (SearchHit hit : hits) {
String source = hit.getSourceAsString();
Book book = JSON.parseObject(source, Book.class);
System.out.println(book);
}
}
缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质
缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质
使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能
SpringBoot提供了缓存技术,方便缓存使用
启用缓存
设置进入缓存的数据
设置读取缓存的数据
导入缓存技术对应的starter
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
dependency>
启用缓存
@SpringBootApplication
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
设置当前操作的结果数据进入缓存
@Cacheable(value="cacheSpace",key="#id")
public Book getById(Integer id) {
return bookDao.selectById(id);
}
需求
输入手机号获取验证码,组织文档以短信形式发送给用户(页面模拟)
输入手机号和验证码验证结果
需求分析
提供controller,传入手机号,业务层通过手机号计算出独有的6位验证码数据,存入缓存后返回此数据
提供controller,传入手机号与验证码,业务层通过手机号从缓存中读取验证码与输入验证码进行比对,返回比对结果
开启缓存
@SpringBootApplication
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
业务层接口
public interface SMSCodeService {
/**
* 传入手机号获取验证码,存入缓存
* @param tele
* @return
*/
String sendCodeToSMS(String tele);
/**
* 传入手机号与验证码,校验匹配是否成功
* @param smsCode
* @return
*/
boolean checkCode(SMSCode smsCode);
}
业务层设置获取验证码操作,并存储缓存,手机号为key,验证码为value
@Autowired
private CodeUtils codeUtils;
@CachePut(value = "smsCode",key="#tele")
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
return code;
}
业务层设置校验验证码操作,校验码通过缓存读取,返回校验结果
@Autowired
private CodeUtils codeUtils;
public boolean checkCode(SMSCode smsCode) {
//取出内存中的验证码与传递过来的验证码比对,如果相同,返回true
String code = smsCode.getCode();
String cacheCode = codeUtils.get(smsCode.getTele());
return code.equals(cacheCode);
}
@Component
public class CodeUtils {
@Cacheable(value = "smsCode",key="#tele")
public String get(String tele){
return null;
}
}
加入Ehcache坐标(缓存供应商实现)
<dependency>
<groupId>net.sf.ehcachegroupId>
<artifactId>ehcacheartifactId>
dependency>
缓存设定为使用Ehcache
spring:
cache:
type: ehcache
ehcache:
config: ehcache.xml
提供ehcache配置文件ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<defaultCache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LRU" />
<cache
name="smsCode"
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
memoryStoreEvictionPolicy="LRU" />
ehcache>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
配置Redis服务器,缓存设定为使用Redis
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
设置Redis相关配置
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
redis:
use-key-prefix: true # 是否使用前缀名(系统定义前缀名)
key-prefix: sms_ # 追加自定义前缀名
time-to-live: 10s # 有效时长
cache-null-values: false # 是否允许存储空值
下载memcached
地址:https://www.runoob.com/memcached/window-install-memcached.html
安装memcached
使用管理员身份运行cmd指令
安装
memcached.exe -d install
运行
启动服务
memcached.exe -d start
定制服务
memcached.exe -d stop
memcached客户端选择
SpringBoot未提供对memcached的整合,需要使用硬编码方式实现客户端初始化管理
加入Xmemcached坐标(缓存供应商实现)
<dependency>
<groupId>com.googlecode.xmemcachedgroupId>
<artifactId>xmemcachedartifactId>
<version>2.4.7version>
dependency>
配置memcached服务器必要属性
memcached:
# memcached服务器地址
servers: localhost:11211
# 连接池的数量
poolSize: 10
# 设置默认操作超时
opTimeout: 3000
创建读取属性配置信息类,加载配置
@Component
@ConfigurationProperties(prefix = "memcached")
@Data
public class XMemcachedProperties {
private String servers;
private Integer poolSize;
private Long opTimeout;
}
创建客户端配置类
@Configuration
public class XMemcachedConfig {
@Autowired
private XMemcachedProperties xMemcachedProperties;
@Bean
public MemcachedClient getMemcachedClinet() throws IOException {
MemcachedClientBuilder builder = new XMemcachedClientBuilder(xMemcachedProperties.getServers());
MemcachedClient memcachedClient = builder.build();
return memcachedClient;
}
}
配置memcached属性
@Service
public class SMSCodeServiceMemcacheImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private MemcachedClient memcachedClient;
@Override
public String sendCodeToSMS(String tele) {
String code = this.codeUtils.generator(tele);
//将数据加入memcache
try {
memcachedClient.set(tele,0,code); // key,timeout,value
} catch (Exception e) {
e.printStackTrace();
}
return code;
}
}
@Service
public class SMSCodeServiceMemcacheImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private MemcachedClient memcachedClient;
@Override
public boolean checkCode(CodeMsg codeMsg) {
String value = null;
try {
value = memcachedClient.get(codeMsg.getTele()).toString();
} catch (Exception e) {
e.printStackTrace();
}
return codeMsg.getCode().equals(value);
}
}
jetCache对SpringCache进行了封装,在原有功能基础上实现了多级缓存、缓存统计、自动刷新、异步调用、数据报表等功能
jetCache设定了本地缓存与远程缓存的多级缓存解决方案
本地缓存(local)
远程缓存(remote)
加入jetcache坐标
<dependency>
<groupId>com.alicp.jetcachegroupId>
<artifactId>jetcache-starter-redisartifactId>
<version>2.6.2version>
dependency>
配置远程缓存必要属性
jetcache:
remote:
default:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
jetcache:
remote:
default:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
sms:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
配置本地缓存必要属性
jetcache:
local:
default:
type: linkedhashmap
keyConvertor: fastjson
配置范例
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
default:
type: linkedhashmap
keyConvertor: fastjson
limit: 100
remote:
default:
host: localhost
port: 6379
type: redis
keyConvertor: fastjson
valueEncoder: java
valueDecoder: java
poolConfig:
minIdle: 5
maxIdle: 20
maxTotal: 50
开启jetcache注解支持
@SpringBootApplication
@EnableCreateCacheAnnotation
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
声明缓存对象
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@CreateCache(name = "smsCache", expire = 3600)
private Cache<String, String> jetSMSCache;
}
操作缓存
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Override
public String sendCodeToSMS(String tele) {
String code = this.codeUtils.generator(tele);
jetSMSCache.put(tele,code);
return code;
}
@Override
public boolean checkCode(CodeMsg codeMsg) {
String value = jetSMSCache.get(codeMsg.getTele());
return codeMsg.getCode().equals(value);
}
}
启用方法注解
@SpringBootApplication
@EnableCreateCacheAnnotation
@EnableMethodCache(basePackages = "com.itheima")
public class Springboot20JetCacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot20JetCacheApplication.class, args);
}
}
使用方法注解操作缓存
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Cached(name = "smsCache_", key = "#id", expire = 3600)
@CacheRefresh(refresh = 10,timeUnit = TimeUnit.SECONDS)
public Book getById(Integer id) {
return bookDao.selectById(id);
}
}
@Service
public class BookServiceImpl implements BookService {
@CacheUpdate(name = "smsCache_", key = "#book.id", value = "#book")
public boolean update(Book book) {
return bookDao.updateById(book) > 0;
}
@CacheInvalidate(name = "smsCache_", key = "#id")
public boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
}
缓存对象必须保障可序列化
@Data
public class Book implements Serializable {
}
jetcache:
remote:
default:
type: redis
keyConvertor: fastjson
valueEncoder: java
valueDecoder: java
查看缓存统计报告
jetcache:
statIntervalMinutes: 15
j2cache是一个缓存整合框架,可以提供缓存的整合方案,使各种缓存搭配使用,自身不提供缓存功能
基于 ehcache + redis 进行整合
加入j2cache坐标,加入整合缓存的坐标
<dependency>
<groupId>net.oschina.j2cachegroupId>
<artifactId>j2cache-spring-boot2-starterartifactId>
<version>2.8.0-releaseversion>
dependency>
<dependency>
<groupId>net.oschina.j2cachegroupId>
<artifactId>j2cache-coreartifactId>
<version>2.8.4-releaseversion>
dependency>
<dependency>
<groupId>net.sf.ehcachegroupId>
<artifactId>ehcacheartifactId>
dependency>
配置使用j2cache(application.yml)
j2cache:
config-location: j2cache.properties
配置一级缓存与二级缓存以及一级缓存数据到二级缓存的发送方式(j2cache.properties)
# 配置1级缓存
j2cache.L1.provider_class = ehcache
ehcache.configXml = ehcache.xml
# 配置1级缓存数据到2级缓存的广播方式:可以使用redis提供的消息订阅模式,也可以使用jgroups多播实现
j2cache.broadcast = net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
# 配置2级缓存
j2cache.L2.provider_class = net.oschina.j2cache.cache.support.redis.SpringRedisProvider
j2cache.L2.config_section = redis
redis.hosts = localhost:6379
设置使用缓存
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private CacheChannel cacheChannel;
}
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Override
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
cacheChannel.set("sms",tele,code);
return code;
}
@Override
public boolean checkCode(SMSCode smsCode) {
String code = cacheChannel.get("sms",smsCode.getTele()).asString();
return smsCode.getCode().equals(code);
}
}
任务
定时任务是企业级应用中的常见操作
市面上流行的定时任务技术
相关概念
导入SpringBoot整合quartz的坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-quartzartifactId>
dependency>
定义具体要执行的任务,继承QuartzJobBean
public class QuartzTaskBean extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println(“quartz job run... ");
}
}
定义工作明细与触发器,并绑定对应关系
@Configuration
public class QuartzConfig {
@Bean
public JobDetail printJobDetail(){
return JobBuilder.newJob(QuartzTaskBean.class).storeDurably().build();
}
@Bean
public Trigger printJobTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/3 * * * * ?");
return TriggerBuilder.newTrigger().forJob(printJobDetail())
.withSchedule(cronScheduleBuilder).build();
}
}
开启定时任务功能
@SpringBootApplication
@EnableScheduling
public class Springboot22TaskApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot22TaskApplication.class, args);
}
}
设置定时执行的任务,并设定执行周期
@Component
public class ScheduledBean {
@Scheduled(cron = "0/5 * * * * ?")
public void printLog(){
System.out.println(Thread.currentThread().getName()+":run...");
}
}
定时任务相关配置
spring:
task:
scheduling:
# 任务调度线程池大小 默认 1
pool:
size: 1
# 调度线程名称前缀 默认 scheduling-
thread-name-prefix: ssm_
shutdown:
# 线程池关闭时等待所有任务完成
await-termination: false
# 调度线程关闭前最大等待时间,确保最后一定关闭
await-termination-period: 10s
SMTP(Simple Mail Transfer Protocol):简单邮件传输协议,用于发送电子邮件的传输协议
POP3(Post Office Protocol - Version 3):用于接收电子邮件的标准协议
IMAP(Internet Mail Access Protocol):互联网消息协议,是POP3的替代协议
导入SpringBoot整合JavaMail的坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
dependency>
配置JavaMail
spring:
mail:
host: smtp.qq.com
username: *********@qq.com
password: *********
开启定时任务功能
@Service
public class SendMailServiceImpl implements SendMailService {
private String from = “********@qq.com"; // 发送人
private String to = "********@126.com"; // 接收人
private String subject = "测试邮件"; // 邮件主题
private String text = "测试邮件正文"; // 邮件内容
}
开启定时任务功能
@Service
public class SendMailServiceImpl implements SendMailService {
@Autowired
private JavaMailSender javaMailSender;
@Override
public void sendMail() {
SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setFrom(from);
mailMessage.setTo(to);
mailMessage.setSubject(subject);
mailMessage.setText(text);
javaMailSender.send(mailMessage);
}
}
附件与HTML文本支持
private String text = "传智教育";
@Override
public void sendMail() {
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage,true);
mimeMessageHelper.setFrom(from);
mimeMessageHelper.setTo(to);
mimeMessageHelper.setSubject(subject);
mimeMessageHelper.setText(text,true);
File file = new File("logo.png");
mimeMessageHelper.addAttachment("美图.png",file);
javaMailSender.send(mimeMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
JMS(Java Message Service):一个规范,等同于JDBC规范,提供了与消息服务相关的API接口
JMS消息模型
JMS消息种类
JMS实现:ActiveMQ、Redis、HornetMQ、RabbitMQ、RocketMQ(没有完全遵守JMS规范
AMQP(advanced message queuing protocol):一种协议(高级消息队列协议,也是消息代理规范),规范了网络交换的数据格式,兼容JMS
优点:具有跨平台性,服务器供应商,生产者,消费者可以使用不同的语言来实现
AMQP消息模型
AMQP消息种类:byte[]
AMQP实现:RabbitMQ、StormMQ、RocketMQ
下载地址:https://activemq.apache.org/components/classic/download/
安装:解压缩
启动服务
activemq.bat
访问服务器
http://127.0.0.1:8161/
导入SpringBoot整合ActiveMQ坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-activemqartifactId>
dependency>
配置ActiveMQ(采用默认配置)
spring:
activemq:
broker-url: tcp://localhost:61616
jms:
pub-sub-domain: true
template:
default-destination: itheima
生产与消费消息(使用默认消息存储队列)
@Service
public class MessageServiceActivemqImpl implements MessageService {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
public void sendMessage(String id) {
System.out.println("使用Active将待发送短信的订单纳入处理队列,id:"+id);
jmsMessagingTemplate.convertAndSend(id);
}
public String doMessage() {
return jmsMessagingTemplate.receiveAndConvert(String.class);
}
}
生产与消费消息(指定消息存储队列)
@Service
public class MessageServiceActivemqImpl implements MessageService {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
public void sendMessage(String id) {
System.out.println("使用Active将待发送短信的订单纳入处理队列,id:"+id);
jmsMessagingTemplate.convertAndSend("order.sm.queue.id",id);
}
public String doMessage() {
return jmsMessagingTemplate.receiveAndConvert("order.sm.queue.id",String.class);
}
}
使用消息监听器对消息队列监听
@Component
public class MessageListener {
@JmsListener(destination = "order.sm.queue.id")
public void receive(String id){
System.out.println("已完成短信发送业务,id:"+id);
}
}
流程性业务消息消费完转入下一个消息队列
@Component
public class MessageListener {
@JmsListener(destination = "order.sm.queue.id")
@SendTo("order.other.queue.id")
public String receive(String id){
System.out.println("已完成短信发送业务,id:"+id);
return "new:"+id;
}
}
RabbitMQ基于Erlang语言编写,需要安装Erlang
Erlang
下载地址:https://rabbitmq.com/install-windows.html
安装:一键傻瓜式安装
启动服务
rabbitmq-service.bat start
关闭服务
rabbitmq-service.bat stop
查看服务状态
rabbitmqctl status
服务管理可视化(插件形式)
查看已安装的插件列表
开启服务管理插件
rabbitmq-plugins.bat enable rabbitmq_management
访问服务器
http://localhost:15672
导入SpringBoot整合RabbitMQ坐标
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
配置RabbitMQ (采用默认配置)
spring:
rabbitmq:
host: localhost
port: 5672
定义消息队列(direct)
@Configuration
public class RabbitDirectConfig {
@Bean
public Queue queue(){
return new Queue("simple_queue");
}
}
@Configuration
public class RabbitDirectConfig {
@Bean
public Queue queue(){
// durable:是否持久化,默认false
// exclusive:是否当前连接专用,默认false,连接关闭后队列即被删除
// autoDelete:是否自动删除,当生产者或消费者不再使用此队列,自动删除
return new Queue("simple_queue",true,false,false);
}
}
@Configuration
public class RabbitDirectConfig {
@Bean
public Queue directQueue(){
return new Queue("direct_queue");
}
@Bean
public Queue directQueue2(){
return new Queue("direct_queue2");
}
@Bean
public DirectExchange directExchange(){
return new DirectExchange("directExchange");
}
@Bean
public Binding bindingDirect(){
return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct");
}
@Bean
public Binding bindingDirect2(){
return BindingBuilder.bind(directQueue2()).to(directExchange()).with("direct2");
}
}
生产与消费消息(direct)
@Service
public class MessageServiceRabbitmqDirectImpl implements MessageService {
@Autowired
private AmqpTemplate amqpTemplate;
@Override
public void sendMessage(String id) {
System.out.println("使用Rabbitmq将待发送短信的订单纳入处理队列,id:"+id);
amqpTemplate.convertAndSend("directExchange","direct",id);
}
}
使用消息监听器对消息队列监听(direct)
@Component
public class RabbitMessageListener {
@RabbitListener(queues = "direct_queue")
public void receive(String id){
System.out.println("已完成短信发送业务,id:"+id);
}
}
使用多消息监听器对消息队列监听进行消息轮循处理(direct)
@Component
public class RabbitMessageListener2 {
@RabbitListener(queues = "direct_queue")
public void receive(String id){
System.out.println("已完成短信发送业务(two),id:"+id);
}
}
定义消息队列(topic)
@Configuration
public class RabbitTopicConfig {
@Bean
public Queue topicQueue(){ return new Queue("topic_queue"); }
@Bean
public Queue topicQueue2(){ return new Queue("topic_queue2"); }
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topicExchange");
}
@Bean
public Binding bindingTopic(){
return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("topic.*.*");
}
@Bean
public Binding bindingTopic2(){
return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("topic.#");
}
}
绑定键匹配规则
*
(星号): 用来表示一个单词 ,且该单词是必须出现的#
(井号): 用来表示任意数量生产与消费消息(topic)
@Service
public class MessageServiceRabbitmqTopicmpl implements MessageService {
@Autowired
private AmqpTemplate amqpTemplate;
@Override
public void sendMessage(String id) {
System.out.println("使用Rabbitmq将待发送短信的订单纳入处理队列,id:"+id);
amqpTemplate.convertAndSend("topicExchange","topic.order.id",id);
}
}
使用消息监听器对消息队列监听(topic)
@Component
public class RabbitTopicMessageListener {
@RabbitListener(queues = "topic_queue")
public void receive(String id){
System.out.println("已完成短信发送业务,id:"+id);
}
@RabbitListener(queues = "topic_queue2")
public void receive2(String id){
System.out.println("已完成短信发送业务(two),id:"+id);
}
}
下载地址:https://rocketmq.apache.org/
安装:解压缩
环境变量配置
ROCKETMQ_HOME
PATH
NAMESRV_ADDR (建议): 127.0.0.1:9876
启动命名服务器
mqnamesrv
启动broker
mqbroker
服务器功能测试:生产者
tools org.apache.rocketmq.example.quickstart.Producer
服务器功能测试:消费者
tools org.apache.rocketmq.example.quickstart.Consumer
<dependency>
<groupId>org.apache.rocketmqgroupId>
<artifactId>rocketmq-spring-boot-starterartifactId>
<version>2.2.1version>
dependency>
配置RocketMQ (采用默认配置)
rocketmq:
name-server: localhost:9876
producer:
group: group_rocketmq
生产消息
@Service
public class MessageServiceRocketmqImpl implements MessageService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public void sendMessage(String id) {
rocketMQTemplate.convertAndSend("order_sm_id",id);
System.out.println("使用Rabbitmq将待发送短信的订单纳入处理队列,id:"+id);
}
}
生产异步消息
@Service
public class MessageServiceRocketmqImpl implements MessageService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public void sendMessage(String id) {
SendCallback callback = new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("消息发送成功");
}
@Override
public void onException(Throwable throwable) {
System.out.println("消息发送失败!!!!!!!!!!!");
}
};
System.out.println("使用Rabbitmq将待发送短信的订单纳入处理队列,id:"+id);
rocketMQTemplate.asyncSend("order_sm_id",id,callback);
}
}
使用消息监听器对消息队列监听
@Component
@RocketMQMessageListener(topic="order_sm_id",consumerGroup = "group_rocketmq")
public class RocketmqMessageListener implements RocketMQListener<String> {
@Override
public void onMessage(String id) {
System.out.println("已完成短信发送业务,id:"+id);
}
}
下载地址:https://kafka.apache.org/downloads
windows 系统下3.0.0版本存在BUG,建议使用2.X版本
安装:解压缩
启动zookeeper
zookeeper-server-start.bat ..\..\config\zookeeper.properties
启动kafka
kafka-server-start.bat ..\..\config\server.properties
创建topic
kafka-topics.bat --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic itheima
查看topic
kafka-topics.bat --zookeeper 127.0.0.1:2181 --list
删除topic
kafka-topics.bat --delete --zookeeper localhost:2181 --topic itheima
生产者功能测试
kafka-console-producer.bat --broker-list localhost:9092 --topic itheima
消费者功能测试
kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic itheima --from-beginning
导入SpringBoot整合Kafka坐标
<dependency>
<groupId>org.springframework.kafkagroupId>
<artifactId>spring-kafkaartifactId>
dependency>
配置Kafka (采用默认配置)
spring:
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: order
生产消息
@Service
public class MessageServiceKafkaImpl implements MessageService {
@Autowired
private KafkaTemplate<String ,String> kafkaTemplate;
@Override
public void sendMessage(String id) {
System.out.println("使用Kafka将待发送短信的订单纳入处理队列,id:"+id);
kafkaTemplate.send("kafka_topic",id); }
}
使用消息监听器对消息队列监听
@Component
public class KafkaMessageListener{
@KafkaListener(topics = {"kafka_topic"})
public void onMessage(ConsumerRecord<?, ?> record) {
System.out.println("已完成短信发送业务,id:"+record.value());
}
}
监控的意义:
监控的实施方式:
Spring Boot Admin,开源社区项目,用于管理和监控SpringBoot应用程序。 客户端注册到服务端后,通过HTTP请求方式,服务端定期从客户端获取对应的信息,并通过UI界面展示对应信息。
Admin服务端
<properties>
<spring-boot-admin.version>2.5.4spring-boot-admin.version>
properties>
<dependencies>
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-serverartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-dependenciesartifactId>
<version>${spring-boot-admin.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
Admin客户端
<properties>
<spring-boot-admin.version>2.5.4spring-boot-admin.version>
properties>
<dependencies>
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-clientartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-dependenciesartifactId>
<version>${spring-boot-admin.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
Admin服务端
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-serverartifactId>
<version>2.5.4version>
dependency>
Admin客户端
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-clientartifactId>
<version>2.5.4version>
dependency>
Admin服务端
server:
port: 8080
设置启用Spring-Admin
@SpringBootApplication
@EnableAdminServer
public class Springboot25ActuatorServerApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot25ActuatorServerApplication.class, args);
}
}
Admin客户端
spring:
boot:
admin:
client:
url: http://localhost:8080
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: "*"
Actuator提供了SpringBoot生产就绪功能,通过端点的配置与访问,获取端点信息
端点描述了一组监控信息,SpringBoot提供了多个内置端点,也可以根据需要自定义端点信息
访问当前应用所有端点信息:/actuator
启用指定端点
management:
endpoint:
health: # 端点名称
enabled: true show-details: always beans: # 端点名称 enabled: true
启用所有端点
management:
endpoints:
enabled-by-default: true
暴露端点功能
为info端点添加自定义指标
info:
appName: @project.artifactId@
version: @project.version@
author: itheima
@Component
public class AppInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
Map<String,Object> infoMap = new HashMap<>();
infoMap.put("buildTime","2006");
builder.withDetail("runTime",System.currentTimeMillis())
.withDetail("company","传智教育");
builder.withDetails(infoMap);
}
}
为Health端点添加自定义指标
@Component
public class AppHealthContributor extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
boolean condition = true;
if(condition){
Map<String,Object> infoMap = new HashMap<>();
infoMap.put("buildTime","2006");
builder.withDetail("runTime",System.currentTimeMillis())
.withDetail("company","传智教育");
builder.withDetails(infoMap);
builder.status(Status.UP);
}else{
builder.status(Status.DOWN);
}
}
}
为Metrics端点添加自定义指标
@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
private Counter counter;
public BookServiceImpl(MeterRegistry meterRegistry){
counter = meterRegistry.counter("用户付费操作次数:");
}
@Override
public boolean delete(Integer id) {
counter.increment();
return bookDao.deleteById(id) > 0;
}
}
自定义端点
@Component
@Endpoint(id="pay")
public class PayEndPoint {
@ReadOperation
public Object getPay(){
//调用业务操作,获取支付相关信息结果,最终return出去
Map payMap = new HashMap();
payMap.put("level 1",103);
payMap.put("level 2",315);
payMap.put("level 3",666);
return payMap;
}
}
pom
import
- Admin客户端
```xml
2.5.4
de.codecentric
spring-boot-admin-starter-client
de.codecentric
spring-boot-admin-dependencies
${spring-boot-admin.version}
pom
import
Admin服务端
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-serverartifactId>
<version>2.5.4version>
dependency>
Admin客户端
<dependency>
<groupId>de.codecentricgroupId>
<artifactId>spring-boot-admin-starter-clientartifactId>
<version>2.5.4version>
dependency>
Admin服务端
server:
port: 8080
设置启用Spring-Admin
@SpringBootApplication
@EnableAdminServer
public class Springboot25ActuatorServerApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot25ActuatorServerApplication.class, args);
}
}
Admin客户端
spring:
boot:
admin:
client:
url: http://localhost:8080
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: "*"
[外链图片转存中…(img-cXWfQSEx-1657811363485)]
Actuator提供了SpringBoot生产就绪功能,通过端点的配置与访问,获取端点信息
端点描述了一组监控信息,SpringBoot提供了多个内置端点,也可以根据需要自定义端点信息
访问当前应用所有端点信息:/actuator
访问端点详细信息:/actuator**/**端点名称
[外链图片转存中…(img-KSdMaD18-1657811363486)]
[外链图片转存中…(img-UlUbALwe-1657811363487)]
Web程序专用端点
[外链图片转存中…(img-maGlhCAb-1657811363487)]
启用指定端点
management:
endpoint:
health: # 端点名称
enabled: true show-details: always beans: # 端点名称 enabled: true
启用所有端点
management:
endpoints:
enabled-by-default: true
暴露端点功能
端点中包含的信息存在敏感信息,需要对外暴露端点功能时手动设定指定端点信息
[外链图片转存中…(img-6UeTKYgJ-1657811363488)]
[外链图片转存中…(img-bLPYJP4S-1657811363489)]
[外链图片转存中…(img-RwCI70cm-1657811363490)]
为info端点添加自定义指标
info:
appName: @project.artifactId@
version: @project.version@
author: itheima
@Component
public class AppInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
Map<String,Object> infoMap = new HashMap<>();
infoMap.put("buildTime","2006");
builder.withDetail("runTime",System.currentTimeMillis())
.withDetail("company","传智教育");
builder.withDetails(infoMap);
}
}
为Health端点添加自定义指标
@Component
public class AppHealthContributor extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
boolean condition = true;
if(condition){
Map<String,Object> infoMap = new HashMap<>();
infoMap.put("buildTime","2006");
builder.withDetail("runTime",System.currentTimeMillis())
.withDetail("company","传智教育");
builder.withDetails(infoMap);
builder.status(Status.UP);
}else{
builder.status(Status.DOWN);
}
}
}
为Metrics端点添加自定义指标
@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
private Counter counter;
public BookServiceImpl(MeterRegistry meterRegistry){
counter = meterRegistry.counter("用户付费操作次数:");
}
@Override
public boolean delete(Integer id) {
counter.increment();
return bookDao.deleteById(id) > 0;
}
}
自定义端点
@Component
@Endpoint(id="pay")
public class PayEndPoint {
@ReadOperation
public Object getPay(){
//调用业务操作,获取支付相关信息结果,最终return出去
Map payMap = new HashMap();
payMap.put("level 1",103);
payMap.put("level 2",315);
payMap.put("level 3",666);
return payMap;
}
}