目录
9.热部署
9.1手动启动热部署
笔记小结
1.开启开发者工具后启用热部署
2.使用构建项目操作启动热部署(Ctrl+F9)
3.热部署仅仅加载当前开发者自定义开发的资源,不加载jar资源
-
开启开发者工具
添加pom.xml依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
-
激活热部署:Ctrl + F9进行编译
-
关于热部署
- 重启(Restart):自定义开发代码,包含类、页面、配置文件等,加载位置restart类加载器
- 重载(ReLoad):jar包,加载位置base类加载器
重启和重载是两个不同的概念
9.2自动启动热部署
笔记小结
设置自动构建用于自动热部署
-
设置自动构建项目
-
Settings(设置自动构建项目)
找到idea中的setting,并勾选自动构建项目按钮
- Rejistry(使编辑器运行时自动构建)
注意:此版本为idea2022。
补充:idea2020设置为 alt+ctrl+shift+/
- 激活方式:Idea失去焦点5秒后启动热部署
9.3热部署范围配置
1.默认不触发重启的目录列表
- /META-INF/maven
- /META-INF/resources
- /resources
- /static
- /public
- /templates
2.自定义不参与重启排除项(也就是,自定义配置不参与热重启)
devtools:
restart:
exclude: public/**,static/**,config/application.yml
9.4关闭热部署
若项目中设置热部署后失效,可能是因为别的高优先级的属性覆盖了该属性
-
设置高优先级属性禁用热部署
public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication.run(SSMPApplication.class); }
10.配置高级
笔记小结
1.@ConfigurationProperties
2.宽松绑定/松散绑定
3.常用计量单位绑定
4.数据校验
10.1@ConfigurationProperties✳
笔记小结
@ConfigurationProperties可以为第三方bean绑定属性
-
使用@ConfigurationProperties为第三方bean绑定属性
@Bean @ConfigurationProperties(prefix = "datasource") //第三方bean定义 public DruidDataSource druidDataSource() { DruidDataSource dds = new DruidDataSource(); return dds; }
绑定第三方Bean需要通过new方法的方式
application.yml
#定义第三方bean获取 datasource: driverClassName: com.mysql.jdbc.Driver
-
解除使用@ConfigurationProperties注释警告
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
获取:
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Springboot13ConfigurationApplication.class, args);
//获取自定义bean
ServerConfig bean = run.getBean(ServerConfig.class);
System.out.println(bean);
//获取第三方bean
DruidDataSource bean1 = run.getBean(DruidDataSource.class);
System.out.println(bean1.getDriverClassName());
}
10.2@EnableConfigurationProperties
-
@EnableConfigurationProperties注解可以将使用@ConfigurationProperties注解对应的类加入Spring容器
@SpringBootApplication @EnableConfigurationProperties(ServerConfig.class) public class DemoApplication { }
//@Component @Data @ConfigurationProperties(prefix = "servers") public class ServerConfig { }
注意:
@EnableConfigurationProperties与@Component不能同时使用。@EnableConfigurationProperties注解,可以直接将开启的类变为@Componet而被spring容器管理,可以少书写一个@Componet步骤,并且同时可以开启对@ConfigurationProperties注解的使用
补充:
@EnableConfigurationProperties
是一个注解,它的作用是开启对@ConfigurationProperties
注解配置属性类的支持。它通常被用于配置类上,表示这个配置类需要被注册到 Spring 容器中,并且需要通过@ConfigurationProperties
注解来绑定配置文件中的属性。@ConfigurationProperties
也是一个注解,它的作用是将配置文件中的属性值绑定到一个 JavaBean 对象中。它通常被用于在一个配置类中定义属性,并通过@EnableConfigurationProperties
注解开启对这个配置类的支持。@EnableConfigurationProperties
是用于开启对@ConfigurationProperties
注解配置属性类的支持,而@ConfigurationProperties
则是用于将配置文件中的属性值绑定到一个 JavaBean 对象中。
10.3宽松绑定/松散绑定
笔记小结
1.@ConfigurationProperties绑定属性支持属性名宽松绑定
2.@Value注解不支持松散绑定
3.绑定前缀命名命名规则
-
@ConfigurationProperties绑定属性支持属性名宽松绑定
1.application.yml
注意:宽松绑定不支持注解**@Value引**用单个属性的方式
补充:常用的方式为中划线模式
2.@ConfigurationProperties位置
@Bean
@ConfigurationProperties(prefix = "datasource")
public DruidDataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
return ds;
}
绑定前缀名命名规范:仅能使用纯小写字母、数字、下划线作为合法的字符
否则报错
10.4常用计量单位绑定
-
JDK8支持的时间与空间计量单位
//jdk8提供的新的数据类型 //时间 @DurationUnit(ChronoUnit.HOURS) private Duration serverTimeout; //空间 @DataSizeUnit(DataUnit.MEGABYTES) private DataSize dataSize;
补充:在application.yml中,DataSize类型在使用时可以用单位的方式进行表示 例如 datasize:10MB,但单位不能与注解同时使用
-
常量单位坐标可点击next提示进行查看
10.5数据校验
笔记小结
- 导入JSR303与Hibernate校验框架坐标
- 使用**@Validated**注解启用校验功能
- 使用具体校验规则规范数据校验格式
参考链接:(38条消息) 如何使用JSR 303 进行后台数据校验?_菜鸟—程序员的博客-CSDN博客
开启Bean数据校验
-
添加JSR303规范坐标与Hibernate校验框架对应坐标
步骤一:添加pom.xml中的坐标
<!--导入JSR303规范--> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> </dependency> <!--使用Hibernate框架提供的校验器做实现类--> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> </dependency>
JSR303规范,是专门用于做数据规范的
补充:validation相当于一套接口规范。hibernate用来做实现类
步骤二:开启对当前bean的属性注入校验
@Component @Data @ConfigurationProperties(prefix = "servers") //开启对当前bean的属性注入校验 @Validated public class ServerConfig { …… }
为Bean添加**@Validated注解**
Bean可以为实体类
步骤三:设置具体的规则
@Max(value = 8888, message = "最大值不能超过8888") private int port;
补充,若省去步骤三,也就是说,没有设置具体的规则,那么在会报错。因为定义了JSR303规范但未使用,因此需要Hibernate校验框架进行完成接口的实现类
报错内容:
此时可添加Hibernate校验器。
示例结果
11.测试
笔记小结
本节测试分为Controller层的测试与Service层测试
1.加载测试专用属性
- 加载本次测试小范围的配置
2.加载测试专用配置
- 加载测试专用的配置类
3.Web环境模拟测试
- 如何在测试类中启动虚拟的web环境
- 如何在测试类中发起虚拟的web请求调用
- 如何在测试类中发完虚拟的web请求调用后设定匹配的条件
4.数据层测试回滚
- 使用**@Rollback联合@Transactional**进行细微控制
5.测试用例数据设定
- 使用随机值进行测试
11.1加载测试专用属性
笔记小结
加载测试临时属性应用于小范围测试环境
-
在启动测试环境时可以通过properties参数设置测试环境专用的属性
@SpringBootTest(properties = {"test.prop=testValue1"}) public class PropertiesAndArgsTest { @Value("${test.prop}") private String msg; @Test void testProperties(){ System.out.println(msg); } }
优势:比多环境开发中的测试环境影响范围更小,仅对当前测试类有效
-
在启动测试环境时可以通过args参数设置测试环境专用的传入参数
@SpringBootTest(args = {"--test.arg=testValue2"}) public class PropertiesAndArgsTest { @Value("${test.arg}") private String msg; @Test void testArgs(){ System.out.println(msg); } }
示例:
//properties属性可以为当前测试用例添加临时的属性配置
//@SpringBootTest(properties = {"test.prop=success1"})
//args属性可以为当前测试用力添加临时的命令行参数
@SpringBootTest(args = {"--test.prop=success2"})
public class PropertiesAndArgsTest {
@Value("${test.prop}")
private String msg;
@Test
void testProperties() {
System.out.println(msg);
}
}
arg配置的是使用命令行的方式进行添加属性,因此需要添加 –
例如:
java –jar springboot.jar –-server.port=80
11.2加载测试专用配置
笔记小结
加载测试范围配置应用于小范围测试环境
-
使用@Import注解加载当前测试类专用的配置
@SpringBootTest @Import(MsgConfig.class) public class ConfigurationTest { @Autowired private String msg; @Test void testConfiguration(){ System.out.println(msg); } }
补充:@Import导入配置
示例:
步骤一:添加测试bean
在test目录下创建config/MsgConfig类
@Configuration
public class MsgConfig {
@Bean
public String getMsg() {
return "test bean";
}
}
此时添加@Bean中仅为测试时使用,实际开发中不这样用。
步骤二:导入配置
@SpringBootTest
@Import({MsgConfig.class})
public class ConfigurationTest {
@Autowired
private String msg;
@Test
void testConfiguration() {
System.out.println(msg);
}
}
使用@Import进行配置的导入,若导入多个配置类时,需要用逗号分隔
11.3Web环境模拟测试
1.模拟端口
模拟web环境启动测试,启动时随机端口
//1.模拟端口
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WebTest {
@Test
void testRandomPort () {
}
}
在springboot注解中添加webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT。
模拟端口中,有四种启动模式 ,RANDOM_PORT会随机生成端口,NONE不能启动Web服务,DEFINED_PORT当自定义端口时则使用,同时也是默认值。
补充:若遇到测试类启动报错,留意pom.xml文件中是否添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
2.虚拟请求测试
模拟web环境请求测试,请求时访问/books目录
//1.模拟端口
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
//2.开启虚拟请求测试
@AutoConfigureMockMvc
public class WebTest {
@Test
//3.1注入虚拟MVC调用对象
void testWeb(@Autowired MockMvc mvc) throws Exception {
//3.2创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/books");
//3.3执行请求
mvc.perform(mockHttpServletRequestBuilder);
}
}
虚拟请求测试时可在方法中注入MockMvc,并且调用MockMvcRequestBuilders.get方法获取请求的路径,再有MockMvc来执行调用
- 虚拟请求状态匹配
@Test
//3.1注入虚拟MVC调用对象
void testStatus(@Autowired MockMvc mvc) throws Exception {
//3.2创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/books");
//3.3执行请求
ResultActions actions = mvc.perform(mockHttpServletRequestBuilder);
//3.4匹配执行状态(是否预期值)
//3.4.1定义执行状态匹配器
StatusResultMatchers status = MockMvcResultMatchers.status();
//3.4.2定义预期执行状态
ResultMatcher ok = status.isOk();
//3.5.3使用本次真实执行结果与预期结果进行比对
actions.andExpect(ok);
}
执行请求后可定义执行状态匹配器 MockMvcResultMatchers.status()来对预期的执行状态status.isOk()进行对比actions.andExpect();
测试报错显示
-
虚拟请求响应体匹配
@Test //3.1注入虚拟MVC调用对象 void testBody(@Autowired MockMvc mvc) throws Exception { //3.2创建虚拟请求,当前访问/books MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/books"); //3.3执行请求 ResultActions actions = mvc.perform(mockHttpServletRequestBuilder); //3.4匹配执行状态(是否预期值) //3.4.1定义执行状态匹配器 ContentResultMatchers content = MockMvcResultMatchers.content(); //3.4.2定义预期执行状态 ResultMatcher result = content.string("springboot"); //3.5.3使用本次真实执行结果与预期结果进行比对 actions.andExpect(result); }
执行请求后可定义执行状态匹配器 MockMvcResultMatchers.content()来对预期的执行状态content.string()进行对比actions.andExpect();
-
虚拟请求响应体(json)匹配
@Test //3.1注入虚拟MVC调用对象 void testJson(@Autowired MockMvc mvc) throws Exception { //3.2创建虚拟请求,当前访问/books MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/books"); //3.3执行请求 ResultActions actions = mvc.perform(mockHttpServletRequestBuilder); //3.4匹配执行状态(是否预期值) //3.4.1定义执行状态匹配器 ContentResultMatchers content = MockMvcResultMatchers.content(); //3.4.2定义预期执行状态 ResultMatcher result = content.json("{\"id\":1,\"type\":\"美女\",\"name\":\"玥玥\",\"description\":\"大美女只为你着迷\"}"); //3.5.3使用本次真实执行结果与预期结果进行比对 actions.andExpect(result); }
执行请求后可定义执行状态匹配器 MockMvcResultMatchers.content()来对预期的执行状态content.json()进行对比actions.andExpect();
- 虚拟请求响应头匹配
@Test //3.1注入虚拟MVC调用对象 void testHead(@Autowired MockMvc mvc) throws Exception { //3.2创建虚拟请求,当前访问/books MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/books"); //3.3执行请求 ResultActions actions = mvc.perform(mockHttpServletRequestBuilder); //3.4匹配执行状态(是否预期值) //3.4.1定义执行状态匹配器 HeaderResultMatchers header = MockMvcResultMatchers.header(); //3.4.2定义预期执行状态 ResultMatcher contentType = header.string("Content-Type", "application/json"); //3.5.3使用本次真实执行结果与预期结果进行比对 actions.andExpect(contentType); }
执行请求后可定义执行状态匹配器 MockMvcResultMatchers.header()来对预期的执行状态header.string()进行对比actions.andExpect();
示例:
一般情况下响应的测试
@Test
void testGetById(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
ResultActions actions = mvc.perform(builder);
//测试响应状态
StatusResultMatchers status = MockMvcResultMatchers.status();
ResultMatcher ok = status.isOk();
actions.andExpect(ok);
//测试响应头
HeaderResultMatchers header = MockMvcResultMatchers.header();
ResultMatcher string = header.string("Content-Type", "application/json");
actions.andExpect(string);
//测试响应体
ContentResultMatchers content = MockMvcResultMatchers.content();
ResultMatcher json = content.json("{\"id\":1,\"type\":\"美女\",\"name\":\"玥玥\",\"description\":\"大美女只为你着迷\"}");
actions.andExpect(json);
}
11.4业务层测试回滚
笔记小结
测试用例回滚事务
-
为测试用例添加事务,SpringBoot会对测试用例对应的事务提交操作进行回滚
@SpringBootTest @Transactional public class DaoTest { @Autowired private BookService bookService; }
-
如果想在测试用例中提交事务,可以通过@Rollback注解设置
@SpringBootTest @Transactional @Rollback(false) public class DaoTest { }
示例:
/*
* 测试业务层的方法时需要开启@Transactional注解并与@Rollback(value = true)连用,可以阻止测试数据的回滚操作。(换句话说,也就是数据库不会自动新增消息)
* */
@SpringBootTest
@Transactional
@Rollback(value = true)
public class DaoTest {
@Autowired
private IBookService service;
@Test
void testSave() {
Book book = new Book();
book.setType("meinv fd ");
book.setName("yueyue");
book.setDescription("dameinvzhiweinizhaomi");
service.saveBook(book);
}
}
11.5测试用例数据设定
测试用例数据通常采用随机值进行测试,使用SpringBoot提供的随机数为其赋值
在application.yml文件下
testcase:
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范围)
- ${random.int}表示随机整数
- ${random.int(10)}表示10以内的随机数
- ${random.int(10,20)}表示10到20的随机数
- 其中()可以是任意字符,例如[],!!均可
示例:
在
在test的testCase/domain下创建BookCase
@Component
@Data
@ConfigurationProperties(prefix = "testcase.book")
public class BookCase {
private int id;
private int id2;
private int type;
private String uuid;
private String name;
private long publishTime;
}
在test下创建testCaseTest
@SpringBootTest
public class testCaseTest {
@Autowired
private BookCase bookCase;
@Test
void testCase() {
System.out.println(bookCase);
}
}
12.数据层解决方案
关系型数据库的解决方案
现有数据层解决方案技术选型
Druid + MyBatis-Plus + MySQL
- 数据源:DruidDataSource
- 持久化技术:MyBatis-Plus / MyBatis
- 数据库:MySQL
12.1SQL
笔记小结
数据源配置(Hikari)
SpringBoot内置3款数据源可供选择
HikariCP(默认)
Tomcat提供DataSource
Commons DBCP
持久化技术(JdbcTemplate)
- SpringBoot内置JdbcTemplate持久化解决方案
- 使用JdbcTemplate需要导入spring-boot-starter-jdbc
数据库(H2)
- H2内嵌式数据库启动方式
- H2数据库线上运行时请务必关闭
12.1.1数据源配置
笔记小结
1.SpringBoot内置3款数据源可供选择
HikariCP(默认)
Tomcat提供DataSource
Commons DBCP
格式一:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: qweasdzxc
type: com.alibaba.druid.pool.DruidDataSource
配置type的方式实现druid的配置
格式二:
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC
username: root
password: qweasdzxc
druid配置标准写法
-
SpringBoot提供了3种内嵌的数据源对象供开发者选择
-
HikariCP:默认内置数据源对象
-
Tomcat提供DataSource:HikariCP不可用的情况下,且在web环境中,将使用tomcat服务器配置的数据源对象
-
Commons DBCP:Hikari不可用,tomcat数据源也不可用,将使用dbcp数据源
-
-
通用配置无法设置具体的数据源配置信息,仅提供基本的连接相关配置,如需配置,在下一级配置中设置具体设定
#hikari数据源 spring: datasource: url: jdbc:mysql://localhost:3306/ssm_db?serverTimezone=UTC driver-class-name: com.mysql.cj.jdbc.Driver username: root password: qweasdzxc #最大连接池 hikari: maximum-pool-size: 50
12.1.2持久化技术配置
笔记小结
1.SpringBoot内置JdbcTemplate持久化解决方案
2.使用JdbcTemplate需要导入spring-boot-starter-jdbc
内置持久化解决方案——JdbcTemplate
1.Springboot15SqlApplicationTests
@Test
void testJdbc(@Autowired JdbcTemplate jdbcTemplate) {
String sql = "select * from tb_book ";
// jdbcTemplate的query参数1为sql语句,参数2为对返回的参数进行封装
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);
}
2.pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
3.application.yml
spring:
jdbc:
template:
query-timeout: -1 # 查询超时时间
max-rows: 500 # 最大行数
fetch-size: -1 # 缓存行数
JdbcTemplate配置:使用默认的持久化技术也可通过application.yml进行配置
12.1.3数据库配置
笔记小结
1.H2内嵌式数据库启动方式
2.H2数据库线上运行时请务必关闭
SpringBoot提供了3种内嵌数据库供开发者选择,提高开发测试效率
- H2
- HSQL
- Derby
内嵌数据库(H2)
-
导入xml坐标
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
-
设置当前项目为web工程,并配置H2管理控制台参数
spring: h2: #h2数据库 console: #控制台模式 enabled: true #可用 path: /h2 #访问地址名 server: port: 8080
访问用户名sa,默认密码123456
-
设置访问数据源
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数据库时需要配置datasource数据源(初始化完成后可取消数据源配置),否则会出现如下报错
再次重启项目即可连接
SpringBoot可以根据url地址自动识别数据库种类,在保障驱动类存在的情况下,可以省略配置driver-class-name
-
操作数据库(创建表)
create table tbl_book (id int,name varchar,type varchar,description varchar)
点击运行
-
插入数据,测试
insert into tbl_book values(1,'springboot1','springboot1','springboot1')
插入后进行查询
select * from tbl_book
-
实现测试
注意:H2数据库只能建立一个连接。因此测试时需要关闭服务器
12.2NoSQL
笔记小结
1.Redis
2.Mongodb
3.ElasticSearch
市面上常见的NoSQL解决方案
- Redis✳
- Mongo✳
- ES
- Solr
12.2.1Redis
Redis优点,响应速度够快。Redis缺点,不支持结构化数据。
笔记小结
1.Redis安装(Windows版)
2.Redis基本操作
-
Redis是一款key-value存储结构的内存级NoSQL数据库
-
支持多种数据存储格式
-
支持持久化
-
支持集群
-
-
Redis下载( Windows版)
- https://github.com/tporadowski/redis/releases
-
Redis安装与启动( Windows版)
-
Windows解压安装或一键式安装
-
服务端启动命令
redis-server.exe redis.windows.conf
启动时需要加载redis在windows下的配置文件redis.windows.conf
-
客户端启动命令
redis-cli.exe
-
12.2.1.1Redis安装与启动
安装示例:
安装完后会产生如下文件
启动示例:
1.启动服务端:
需要添加conf配置项
redis-server.exe redis.windows.conf
若未添加配置项,报错:
2.启动客户端
需要解决此版本的redis在windows下的启动bug
若启动了redis的服务端,但并未出现Running in standalone mode的提示字样,则需要打开客户端进行shutdown操作,如下所示
通过cmd打开客户端
redis-cli.exe
执行如下命令,如图
shutdown
3.启动服务端
redis-server.exe redis.windows.conf
重启后成功。
12.2.1.2Redis基本操作
Redis是一款key-value存储结构的内存级NoSQL数据库,因此常用get,set,hset,hget方式进行数据操作
设置key
-
普通方式
set name yueyue
-
哈希方式
hset ka a1 aaa
获取key
-
普通方式
get name
-
哈希方式
hget ka a1
查询所有key
keys *
删除所有key
/删除当前数据库中的所有Key
flushdb
//删除所有数据库中的key
flushall
12.2.3SpringBoot整合Redis
笔记小结
基本操作
- 导入redis对应的starter
- 配置
- 提供操作Redis接口对象RedisTemplate
- ops*:获取各种数据类型操作接口
接口对象
- RedisTemplate
- StringRedisTemplate(常用)
客户端选择
- lettuce(默认)
- jedis
12.2.3.1基本操作
1.导入SpringBoot整合Redis坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置Redis(采用默认配置)
spring:
redis:
host: localhost # 127.0.0.1
port: 6379
主机:localhost(默认)
端口:6379(默认)
RedisTemplate提供操作各种数据存储类型的接口API
3.客户端:RedisTemplate
@SpringBootTest
class Springboot16NosqlApplicationTests {
@Test
void set(@Autowired RedisTemplate redisTemplate) {
ValueOperations ops = redisTemplate.opsForValue();
ops.set("testKey","testValue");
}
@Test
void get(@Autowired RedisTemplate redisTemplate) {
ValueOperations ops = redisTemplate.opsForValue();
Object val = ops.get("testKey");
System.out.println(val);
}
@Test
void hset(@Autowired RedisTemplate redisTemplate) {
HashOperations opsH = redisTemplate.opsForHash();
opsH.put("testKeyH","testFieldH","testValueH");
}
@Test
void hget(@Autowired RedisTemplate redisTemplate) {
HashOperations opsH = redisTemplate.opsForHash();
Object valH = opsH.get("testKeyH", "testFieldH");
System.out.println(valH);
}
}
补充:
HashOperations和
ValueOperations区别
HashOperations
和ValueOperations
是Spring Framework提供的两种常见的Redis操作接口,它们的主要区别在于:
HashOperations
用于操作Redis中的Hash类型数据,可以对一个Redis键存储多个字段和值,适用于存储结构化数据。ValueOperations
用于操作Redis中的String类型数据,可以对一个Redis键存储单个值,适用于存储非结构化数据。具体来说,
HashOperations
提供了一系列的操作方法,例如put
(设置一个字段和值)、get
(获取一个字段的值)、delete
(删除一个或多个字段)等,可以方便地对Redis中的Hash类型数据进行读写操作。而ValueOperations
则提供了一些类似于set
、get
、increment
等操作方法,用于对Redis中的String类型数据进行读写操作。在实际使用中,需要根据具体的业务需求和数据类型选择合适的操作接口。如果需要存储结构化数据,应该使用
HashOperations
;如果只需要存储单个值,可以使用ValueOperations
。
12.2.3.2接口对象
笔记小结
1.RedisTemplate
2.StringRedisTemplate(常用)
客户端:RedisTemplate以对象作为key和value,内部对数据进行序列化
@SpringBootTest
class Springboot16NosqlApplicationTests {
@Test
void set(@Autowired RedisTemplate redisTemplate) {
ValueOperations ops = redisTemplate.opsForValue();
ops.set("testKey","testValue");
}
@Test
void get(@Autowired RedisTemplate redisTemplate) {
ValueOperations ops = redisTemplate.opsForValue();
Object val = ops.get("testKey");
System.out.println(val);
}
}
客户端:StringRedisTemplate以字符串作为key和value,与Redis客户端操作等效
@SpringBootTest
class Springboot16NosqlApplicationTests {
@Test
void set(@Autowired StringRedisTemplate redisTemplate) {
ValueOperations ops = redisTemplate.opsForValue();
ops.set("testKey","testValue");
}
@Test
void get(@Autowired StringRedisTemplate redisTemplate) {
ValueOperations ops = redisTemplate.opsForValue();
Object val = ops.get("testKey");
System.out.println(val);
}
}
StringRedisTemplate是常用的接口对象,与Redis客户端操作等效。如果是用RedisTemplate接口对象,那么则会出现Redis客户端读取不成功现象
12.2.3.3客户端选择
笔记小结
- lettuce(默认)
- jedis
jedis
1.导入maven
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
2.配置客户端
spring:
redis:
host: localhost # 127.0.0.1
port: 6379
client-type: jedis
client-type选择jedis。lettuce为默认值
3.配置客户端专用属性
spring:
redis:
port: 6379
host: localhost # 127.0.0.1
client-type: jedis
jedis:
pool:
max-active: 16
# lettuce:
# pool:
# max-active: 16
补充:
lettcus与jedis区别
- jedis连接Redis服务器是直连模式,当多线程模式下使用jedis会存在线程安全问题,解决方案可以通过配置连接池使每个连接专用,这样整体性能就大受影响。
- lettcus基于Netty框架进行与Redis服务器连接,底层设计中采用StatefulRedisConnection。 StatefulRedisConnection自身是线程安全的,可以保障并发访问安全问题,所以一个连接可以被多线程复用。当然lettcus也支持多连接实例一起工作。
12.2.2Mongodb
MongoDB是一个开源、高性能、无模式的文档型数据库。NoSQL数据库产品中的一种,是最像关系型数据库的非关系型数据库
1.Mongodb应用场景
淘宝用户数据
- 存储位置:数据库
- 特征:永久性存储,修改频度极低
游戏装备数据、游戏道具数据
- 存储位置:数据库、Mongodb
- 特征:永久性存储与临时存储相结合、修改频度较高
直播数据、打赏数据、粉丝数据
- 存储位置:数据库、Mongodb
- 特征:永久性存储与临时存储相结合,修改频度极高
物联网数据
- 存储位置:Mongodb
- 特征:临时存储,修改频度飞速
其他数据……
12.2.2.1MongoDB安装与启动
笔记小结
1.Mongodb安装、启动
2.可视化客户端Robo 3T安装与连接
Windows版Mongo下载
-
https://www.mongodb.com/try/download
-
解压缩后设置数据目录
Windows版Mongo启动
-
服务端启动
mongod --dbpath=..\data\db
注意:初次使用时需要启动一次,再次重启
-
客户端启动
mongo --host=127.0.0.1 --port=27017
注意:启动客户端时需要等待服务器完全启动成功后再次启动,否则会出现连接不上的状态
补充:若在安装时出现问题,Windows版Mongo安装问题及解决方案
步骤一:下载对应的dll文件(通过互联网搜索即可)
步骤二:拷贝到windows安装路径下的system32目录中
步骤三:执行命令注册对应dll文件regsvr32 vcruntime140_1.dll
12.2.2.2MongoDB基本操作
笔记小结
Mongodb基础CRUD
- 新增
db.集合名称.insert/save/insertOne(文档)
#例如
db.Book.save({"name":"yueyue2"})
- 删除
db.集合名称.remove(条件)
#例如
db.Book.remove({"name":"yueyue"})
- 修改
db.集合名称.update(条件,{操作种类:{文档}})
#例如
db.Book.update({"name":"yueyue2"},{$set:{"name":"jiege"}})
db.Book.update({"name":"yueyue2"},{$set:{"type":"gril"}})
- 查询
db.集合名称.finde(条件)
#例如
db.Book.find()
db.Book.find({"name":"jiege"})
补充:
基础查询
- 查询全部:db.集合.find();
- 查第一条:db.集合.findOne()
- 查询指定数量文档:db.集合.find().limit(10) //查10条文档
- 跳过指定数量文档:db.集合.find().skip(20) //跳过20条文档
- 统计:db.集合.count()
- 排序:db.集合.sort({age:1}) //按age升序排序
- 投影:db.集合名称.find(条件,{name:1,age:1}) //仅保留name与age域
条件查询
- 基本格式:db.集合.find({条件})
- 模糊查询:db.集合.find({域名:/正则表达式/}) //等同SQL中的like,比like强大,可以执行正则所有规则
- 条件比较运算:db.集合.find({域名:{$gt:值}}) //等同SQL中的数值比较操作,例如:name>18
- 包含查询:db.集合.find({域名:{$in:[值1,值2]}}) //等同于SQL中的in
- 条件连接查询:db.集合.find({$and:[{条件1},{条件2}]}) //等同于SQL中的and、or
12.2.2.3SpringBoot整合MongoDB
笔记小结
SpringBoot整合Mongodb
- 导入Mongodb对应的starter
- 配置mongodb访问uri
- 提供操作Mongodb接口对象MongoTemplate
1.导入Mongodb驱动坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
2.配置客户端
spring:
data:
mongodb:
uri: mongodb://localhost/itheima
3.客户端读写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);
}
12.2.3ElasticSearch(ES)
笔记小结
1.ES应用场景
2.ES相关概念
ES应用场景
Elasticsearch是一个分布式全文搜索引擎,某宝常用
ES相关概念
- 索引
- 倒排索引
- 创建文档
- 使用文档
12.2.3.1ES安装与启动
Windows版ES下载
- https://www.elastic.co/cn/downloads/elasticsearch
Windows版ES安装与启动
-
运行 elasticsearch.bat
当访问浏览器页面出现一下字符表示启动成功
http://localhost:9200/
12.2.3.2ES基本操作
笔记小结
1.索引操作
2.IK分词器安装
3.设置索引创建规则(应用)
12.2.3.2.1索引
1.创建/查询/删除索引
PUT http://localhost:9200/books
此时使用postman进行创建测试
GET http://localhost:9200/books
DELETE http://localhost:9200/books
2.IK分词器
- 下载:https://github.com/medcl/elasticsearch-analysis-ik/releases
3.创建索引并指定规则
{
"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"
}
}
}
}
注意:配置项"analyzer":"ik_max_word"不要打错
“analyzer”:“ik_max_word”,表示配置ik分词器
“copy_to”:“all”,表示拷贝至all索引项
12.2.3.2.2文档
1.创建文档
创建文档三种方式
POST http://localhost:9200/books/_doc #使用系统生成id
POST http://localhost:9200/books/_create/1 #使用指定id,
POST http://localhost:9200/books/_doc/1 #使用指定id,不存在创建,存在更新(版本递增)
body部分
{
"name":"springboot",
"type":"springboot",
"description":"springboot"
}
注意:body部分 “name”等json的key值前面的空格需要删除,或者用 ······ 进行填充
否则报错:
“reason”: "Unexpected character (’ ’ (code 160)): was expecting double-quote to start field name\n
2.删除文档
DELETE http://localhost:9200/books/_doc/1
3.修改文档
- 修改文档(全量修改)
PUT http://localhost:9200/books/_doc/1
body部分
{
"name":"springboot",
"type":"springboot",
"description":"springboot"
}
全部修改是指将原有的所有属性直接覆盖
- 修改文档(部分修改)
POST http://localhost:9200/books/_update/1
body部分
{
"doc":{
"name":"springboot"
}
}
部分修改是指将原有的指定属性进行覆盖
4.查询文档
- 常规查询
GET http://localhost:9200/books/_doc/1 #查询单个文档
GET http://localhost:9200/books/_search #查询全部文档
- 条件查询
GET http://localhost:9200/books/_search?q=name:springboot
12.2.3.3SpringBoot整合ElasticSearch(ES)
12.2.3.3.1方式一
1.导入驱动坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
2.配置
spring:
elasticsearch:
rest:
uris: http://localhost:9200
3.客户端整合第三方技术
@SpringBootTest
class Springboot18EsApplicationTests {
@Autowired
private ElasticsearchRestTemplate template;
}
12.2.3.3.2方式二(现已不可用)
1.导入驱动坐标
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
2.配置(无)
补充:
SpringBoot平台并没有跟随ES的更新速度进行同步更新,ES提供了High Level Client操作ES
3.客户端
@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);
}
@BeforeEach注解。它用于表示应在当前类中的每个@Test方法之前执行注解方法。
@AfterEach注解。它用于表示应在当前类中的每个@Test方法之后执行注解方法。
12.2.3.3.3添加文档
添加文档
//添加文档
@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);
}
参考资料https://www.bilibili.com/video/BV15b4y1a7yG/?p=105&spm_id_from=pageDriver
批量添加文档
//批量添加文档
@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);
}
12.2.3.3.4查询文档
按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);
}
}
参考资料https://www.bilibili.com/video/BV15b4y1a7yG/?p=106&spm_id_from=pageDriver
13.整合第三方技术
13.1缓存
笔记小结
1.spring-cache
- simple
- ehcache
- redis
- memcached
2.jetcache
3.j2cache
缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质
作用:
- 使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能
- 缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间
减少了数据库的压力
示例
@Service
public class MsgServiceImpl implements IMsgService {
//定义代码缓存
private HashMap<String, String> cache = new HashMap<>();
@Override
public String get(String tel) {
//截取后6位作为验证码
String code = tel.substring(tel.length() - 6);
cache.put(tel, code);
return code;
}
@Override
public Boolean check(String tel, String code) {
String queryCode = cache.get(tel);
return queryCode.equals(code);
}
}
当获取验证码时,将电话后6位返回,并保存到内存中。当校验验证码时,再取出内存中的6位验证码即可比对成功。优点:省去了存入数据库的频繁读写;缺点:会导致内存占用过大,而崩溃
SpringBoot提供了缓存技术,方便缓存使用
- 启用缓存
- 设置进入缓存的数据
- 设置读取缓存的数据
1.导入缓存技术对应的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.启用缓存
@SpringBootApplication
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
在启类的位置设置@EnableCaching注解
3.设置当前操作的结果数据进入缓存
@Cacheable(value="cacheSpace",key="#id")
public Book getById(Integer id) {
return bookDao.selectById(id);
}
在实现类的相应的方法上添加@Cacheable注解
示例:
SpringBoot19CacheApplication
SpringBootApplication
//启动缓存
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
BookServiceImpl
@Service
public class BookServiceImpl implements IBookService {
@Autowired
private BookDao bookDao;
//如果当前缓存中没有本次要查询的数据,则进行查询,否则直接从缓存中获取数据返回
// private HashMap<Integer, Book> cache = new HashMap<>();
// @Override
// public Book getById(Integer id) {
// Book book = cache.get(id);
// if (book == null) {
// Book queryBook = bookDao.selectById(id);
// cache.put(id, queryBook);
// return queryBook;
// }
// return book;
// }
@Override
@Cacheable(value = "cacheSpace", key = "#id")
public Book getById(Integer id) {
return bookDao.selectById(id);
}
}
SpringBoot提供的缓存技术除了提供默认的缓存方案,还可以对其他缓存技术进行整合,统一接口,方便缓存技术的开发与管理
- Generic
- JCache
- Ehcache
- Hazelcast
- Infinispan
- Couchbase
- Redis
- Caffeine
- Simple(默认)
- memcached
13.1.1案例展示——手机验证码
13.1.1.1手机验证码获取
需求
- 输入手机号获取验证码,组织文档以短信形式发送给用户(页面模拟)
- 输入手机号和验证码验证结果
需求分析
- 提供controller,传入手机号,业务层通过手机号计算出独有的6位验证码数据,存入缓存后返回此数据
- 提供controller,传入手机号与验证码,业务层通过手机号从缓存中读取验证码与输入验证码进行比对,返回比对结果
1.导入缓存技术对应的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
在pom.xml文件中添加依赖
2.启用缓存
@SpringBootApplication
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
在启类的位置设置**@EnableCaching**注解
3.domain类
package com.example.domain;
import lombok.Data;
@Data
public class SMSCode {
private String tel;
private String code;
}
因为手机验证码需要得到手机与响应的验证码,此时创建SMSCode对象,对值进行封装
4.工具类
CodeUtil验证码校验类
@Component
public class CodeUtil {
/**
* 获取随机验证码
* @param tel 手机号
* @return 验证码
*/
public String generator(String tel) {
//hashCode() 方法用于返回字符串的哈希码,哈希码通过相应计算得来。
int hashCode = tel.hashCode();
//定义加密数字
int encryption = 20040213;
//一层加密
long result = hashCode ^ encryption;
//获取当前时间
long nowTime = System.currentTimeMillis();
//二层加密
result = result ^ nowTime;
String temp = String.valueOf(result);
return temp.substring(temp.length() - 6);
}
}
添加@Component注解是为了让spring boot对CodeUtil进行管理
4.业务层接口
public interface ISMSCodeService {
/**
* 传入手机号获取验证码,存入缓存
*
* @param tel
* @return
*/
String sendCodeToSMS(String tel);
/**
* 传入手机号与验证码,校验匹配是否成功
*
* @param smsCode
* @return
*/
boolean checkCode(SMSCode smsCode);
}
5.业务层实现
@Service
public class SMSCodeServiceImpl implements ISMSCodeService {
@Autowired
CodeUtil codeUtil;
@Override
//@Cacheable(value = "smsCache", key = "#tel")
@CachePut(value = "smsCache", key = "#tel")
public String sendCodeToSMS(String tel) {
return codeUtil.generator(tel);
}
@Override
public boolean checkCode(SMSCode smsCode) {
return false;
}
}
注意使用 @CachePut注解,而并不是@Cacheable
@CachePut:将tel作为key,将返回值作为value
checkCode暂时没有编写
6.控制层
@RestController
@RequestMapping("/sms")
public class SMSCodeController {
@Autowired
SMSCodeServiceImpl service;
@GetMapping("/{tel}")
public String getCode(@PathVariable String tel) {
return service.sendCodeToSMS(tel);
}
@PostMapping
public boolean checkCode(SMSCode smsCode) {
return service.checkCode(smsCode);
}
}
checkCode暂时没有编写
13.1.1.2手机验证码校验
1.工具类
/**
* 取出缓存中的验证码
*
* @param tel 电话号码
* @return 如果电话号码相匹配则返回value
* 当使用了@Cacheable注解,在返回时填入null即可。若缓存中key有对应的值则返回,无则返回null
* 当使用了@Cacheable注解,需要被springboot的容器管理才能生效
*/
@Cacheable(value = "smsCache", key = "#tel")
public String get(String tel) {
return null;
}
CodeUtil的补充
2.业务层实现补充
@Override
public boolean checkCode(SMSCode smsCode) {
String code = smsCode.getCode();
String checkCode = codeUtil.get(smsCode.getTel());
return code.equals(checkCode);
}
13.1.2缓存供应商变更:Ehcache
笔记小结
1.导入maven坐标
2.进行yml文件配置
3.提供ehcache配置文件
13.1.2.1实现步骤:
1.加入Ehcache坐标(缓存供应商实现)
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
因为springboot整合了ehcache,因此可以不用指定坐标的版本
2.缓存设定为使用Ehcache
spring:
cache:
type: ehcache
主要配置cache的type为ehcache
若需要修改默认的配置文件名,需要添加ehcache的config来进行修改
ehcache:
config: ehcache.xml
3.提供ehcache配置文件ehcache.xml
在resource下添加此文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--默认缓存策略 -->
<!-- external:是否永久存在,设置为true则不会被清除,此时与timeout冲突,通常设置为false-->
<!-- diskPersistent:是否启用磁盘持久化-->
<!-- maxElementsInMemory:最大缓存数量-->
<!-- overflowToDisk:超过最大缓存数量是否持久化到磁盘-->
<!-- timeToIdleSeconds:最大不活动间隔,设置过长缓存容易溢出,设置过短无效果-->
<!-- timeToLiveSeconds:最大存活时间-->
<!-- memoryStoreEvictionPolicy:缓存清除策略-->
<defaultCache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LRU" />
<!--smsCode配置的ehcache -->
<cache
name="smsCode"
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
当需要使用多组配置时,只需要添加多组即可,它们之间用name进行区分
注意文件名需要指定为ehcache.xml,若需要变更,则要修改yml配置
13.1.3缓存供应商变更:Redis(重点)
笔记小结
1.导入maven坐标
2.进行yml文件配置
13.1.3.1实现步骤:
1.加入Redis坐标(缓存供应商实现)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置Redis服务器,缓存设定为使用Redis
spring:
redis:
host: localhost
port: 6379
cache:
type: redis
3.设置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 # 是否允许存储空值
当use-key-prefix的值设置为False时,key-prefix自动失效。time-to-live设置Redis中key和Value的存活时间
注意,需要配置host主机与port端口
13.1.3.2示例:
1.配置坐标
在pom.xml文件中
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置连接
spring:
cache:
type: redis
redis:
time-to-live: 10s
redis:
host: localhost
port: 6379
3.业务层接口类
在service/impl/的SMSCodeServiceImpl中
@Override
@CachePut(value = "smsCache", key = "#tel")
public String sendCodeToSMS(String tel) {
return codeUtil.generator(tel);
}
@Override
public boolean checkCode(SMSCode smsCode) {
String code = smsCode.getCode();
String checkCode = codeUtil.get(smsCode.getTel());
return code.equals(checkCode);
}
/*ehcache、redis、simple用法 */的调用方法是一样的
@Component public class CodeUtil { /** * 获取随机验证码 * * @param tel 手机号 * @return 验证码 */ public String generator(String tel) { //hashCode() 方法用于返回字符串的哈希码,哈希码通过相应计算得来。 int hashCode = tel.hashCode(); //定义加密数字 int encryption = 20040213; //一层加密 long result = hashCode ^ encryption; //获取当前时间 long nowTime = System.currentTimeMillis(); //二层加密 result = result ^ nowTime; String temp = String.valueOf(result); return temp.substring(temp.length() - 6); } /** * 取出缓存中的验证码 * * @param tel 电话号码 * @return 如果电话号码相匹配则返回value * 当使用了@Cacheable注解,在返回时填入null即可。若缓存中key有对应的值则返回,无则返回null * 当使用了@Cacheable注解,需要被springboot的容器管理才能生效 */ @Cacheable(value = "smsCache", key = "#tel") public String get(String tel) { return null; } }
注意:此处 @CachePut(value = “smsCache”, key = “#tel”)和@Cacheable(value = “smsCache”, key = “#tel”)的value前缀需要保持一致,否则,在redis中将不能找出相应的value值
13.1.4缓存供应商变更:Memcached
笔记小结
- memcached下载与安装
- memcached启动与退出
- xmemcached客户端加载方式(bean初始化)
- xmemcached客户端使用方式(set & get)
下载memcached
- 地址:https://www.runoob.com/memcached/window-install-memcached.html
安装memcached
-
使用管理员身份运行cmd指令
-
安装
memcached.exe -d install
运行memcached
-
启动服务
memcached.exe -d start
-
停止服务
memcached.exe -d stop
可在windows资源管理器界面进行查看
memcached客户端选择
- Memcached Client for Java:最早期客户端,稳定可靠,用户群广
- SpyMemcached:效率更高
- Xmemcached:并发处理更好
SpringBoot未提供对memcached的整合,需要使用硬编码方式实现客户端初始化管理
13.1.4.1实现步骤:
1.加入Xmemcache坐标(缓存供应商实现)
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.7</version>
</dependency>
2.配置memcached服务器必要属性
memcached:
# memcached服务器地址
servers: localhost:11211
# 连接池的数量
poolSize: 10
# 设置默认操作超时
opTimeout: 3000
3.创建读取属性配置信息类,加载配置
@Component
@ConfigurationProperties(prefix = "memcached")
@Data
public class XMemcachedProperties {
private String servers;
private Integer poolSize;
private Long opTimeout;
}
自定义配置属性信息类,在基础配置中有详细记载
@Component为了定义XMemcachedProperties变为springboot管控的bean
@ConfigurationProperties(prefix = “memcached”)是为了读取application.yml文件的配置
4.创建客户端配置类
@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;
}
}
5.配置memcached属性
@Service
public class SMSCodeServiceMemcacheImpl implements SMSCodeService {
@Autowired
CodeUtil codeUtil;
//使用自定义配置的XMemcached的配置技术
@Autowired
MemcachedClient memcachedClient;
@Override
public String sendCodeToSMS(String tel) {
String code = codeUtil.generator(tel);
try {
//设置memcachedClient存入的值
memcachedClient.set(tel, 10, code);
} catch (Exception e) {
e.printStackTrace();
}
return code;
}
}
使用 memcachedClient.set(tele,0,code);进行设置,参数分别为对应的key关键字,timeout超时时间,value值
6.配置memcached属性
@Service
public class SMSCodeServiceMemcacheImpl implements SMSCodeService {
@Autowired
CodeUtil codeUtil;
//使用自定义配置的XMemcached的配置技术
@Autowired
MemcachedClient memcachedClient;
@Override
public boolean checkCode(CodeMsg codeMsg) {
String code = null;
try {
//获取memcachedClient存入的值
code = memcachedClient.get(codeMsg.getTele()).toString();
} catch (Exception e) {
e.printStackTrace();
}
return codeMsg.getCode().equals(value);
}
}
注意:此时需要用.tostring()方法,因为memcachedClient.get(codeMsg.getTele())返回的是一个对象
13.1.4.2示例:
1.加入Xmemcache坐标(缓存供应商实现)
修改pom.xml
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.7</version>
</dependency>
2.配置memcached服务器必要属性
配置application.yml文件
memcached:
# memcached服务器地址
servers: localhost:11211
# 连接池的数量
poolSize: 10
# 设置默认操作超时
opTimeout: 3000
3.创建读取属性配置信息类,加载配置
创建config/XMemcachedProperties类
@Component
@ConfigurationProperties(prefix = "memcached")
@Data
public class XMemcachedProperties {
private String servers;
private Integer poolSize;
private Long opTimeout;
}
4.创建客户端配置类
创建config/xMemcachedProperties类
@Configuration
public class XMemcachedConfig {
@Autowired
XMemcachedProperties xMemcachedProperties;
@Bean
public MemcachedClient getMemcachedClient() throws IOException {
//创建创建memcachedClient的builder对象
MemcachedClientBuilder builder = new XMemcachedClientBuilder(xMemcachedProperties.getServers());
//设置最大连接池个数
builder.setConnectionPoolSize(xMemcachedProperties.getPoolSize());
//设置超时时间
builder.setOpTimeout(xMemcachedProperties.getOpTimeout());
//创建memcachedClient对象
MemcachedClient memcachedClient = builder.build();
return memcachedClient;
}
}
5.在代码中整合
修改service/impl包下的SMSCodeServiceImpl类
@Service
public class SMSCodeServiceImpl implements ISMSCodeService {
@Autowired
CodeUtil codeUtil;
//使用自定义配置的XMemcached的配置技术
@Autowired
MemcachedClient memcachedClient;
@Override
public String sendCodeToSMS(String tel) {
String code = codeUtil.generator(tel);
try {
//设置memcachedClient存入的值
memcachedClient.set(tel, 10, code);
} catch (TimeoutException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (MemcachedException e) {
throw new RuntimeException(e);
}
return code;
}
@Override
public boolean checkCode(SMSCode smsCode) {
String code;
try {
//获取memcachedClient存入的值
code = memcachedClient.get(smsCode.getTel()).toString();
} catch (TimeoutException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (MemcachedException e) {
throw new RuntimeException(e);
}
return smsCode.getCode().equals(code);
}
}
13.1.5缓存供应商变更:jetcache(重点)
13.1.5.1启用配置注解
笔记小结
1.jetcache简介
2.jetcache远程缓存使用方式
3.jetcache本地缓存使用方式
-
jetCache对SpringCache进行了封装,在原有功能基础上实现了多级缓存、缓存统计、自动刷新、异步调用、数据报表等功能
-
jetCache设定了本地缓存与远程缓存的多级缓存解决方案
-
本地缓存(local)
-
LinkedHashMap
-
Caffeine
-
-
远程缓存(remote)
-
Redis
-
Tair
-
-
13.1.5.1.1实现步骤:
1.加入jetcache坐标
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.7</version>
</dependency>
注意:经测试,当版本控制在2.6.7时,不会发生springboot2.7.x与jetcache的版本冲突导致的循环报错
2.配置远程缓存必要属性
jetcache:
remote:
default:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
sms:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
注意: poolConfig是必须要配置,否则会报错
3.配置本地缓存必要属性
jetcache:
local:
default:
type: linkedhashmap
keyConvertor: fastjson
配置本地缓存有两个必要的属性,分别是type与keyConvertor
本地缓存的类型有两种,其中一种是linkedhashmap
本地缓存需要配置keyConvertor(fastjson可以将对象转换为json格式的字符串),为了让存入本地的key转换为字符串,当key存为object时,会加大本地索引key的难度
完整示例:
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.statIntervalMinutes | 0 | 统计间隔,0表示不统计 |
jetcache.hiddenPackages | 无 | 自动生成name时,隐藏指定的包名前缀 |
jetcache.[local|remote].${area}.type | 无 | 缓存类型,本地支持linkedhashmap、caffeine,远程支持redis、tair |
jetcache.[local|remote].${area}.keyConvertor | 无 | key转换器,当前仅支持fastjson |
jetcache.[local|remote].${area}.valueEncoder | java | 仅remote类型的缓存需要指定,可选java和kryo |
jetcache.[local|remote].${area}.valueDecoder | java | 仅remote类型的缓存需要指定,可选java和kryo |
jetcache.[local|remote].${area}.limit | 100 | 仅local类型的缓存需要指定,缓存实例最大元素数 |
jetcache.[local|remote].${area}.expireAfterWriteInMillis | 无穷大 | 默认过期时间,毫秒单位 |
jetcache.local.${area}.expireAfterAccessInMillis | 0 | 仅local类型的缓存有效,毫秒单位,最大不活动间隔 |
3.开启jetcache注解支持
@SpringBootApplication
@EnableCreateCacheAnnotation
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
使用**@EnableCreateCacheAnnotation**,表示使用注解开启创建缓存
4.声明缓存对象
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@CreateCache(area = "default", name = "smsCache_", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.REMOTE)
private Cache<String, String> jetSMSCache;
}
@CreateCache:area表示哪一组配置,name表示key的前缀。expire表示超时时间设置。timeUnit通常与expire连用,表示超时设置的时间单位,默认值为秒数。cacheType表示缓存的读取类型为本地,远程都能读取到
注意Cache<String,String>导包,import com.alicp.jetcache.Cache;
5.操作缓存
@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);
}
}
jetSMSCache.put(tele,code);
jetSMSCache.get(codeMsg.getTele());
即可获取或者key对应的值
13.1.5.1.2示例:
1.加入jetcache坐标
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.7</version>
</dependency>
2.添加application.yml配置
#jetcache
jetcache:
#远程方案
remote:
default:
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
# #本地方案
# local:
# default:
# type: linkedhashmap
# keyConvertor: fastjson
3.Springboot20JetcacheApplication
@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
public class Springboot20JetcacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot20JetcacheApplication.class, args);
}
}
4.业务成的实现类
@Service
public class SMSCodeServiceImpl implements ISMSCodeService {
@Autowired
CodeUtil codeUtil;
@CreateCache(area = "default", name = "smsCache_", expire = 3600, timeUnit = TimeUnit.SECONDS)
private Cache<String, String> jetSMSCache;
@Override
public String sendCodeToSMS(String tel) {
String code = codeUtil.generator(tel);
jetSMSCache.put(tel, code);
return code;
}
@Override
public boolean checkCode(SMSCode smsCode) {
String code = jetSMSCache.get(smsCode.getTel());
return smsCode.getCode().equals(code);
}
}
补充:
CodeUtil.class
@Component public class CodeUtil { /** * 获取随机验证码 * * @param tel 手机号 * @return 验证码 */ public String generator(String tel) { //hashCode() 方法用于返回字符串的哈希码,哈希码通过相应计算得来。 int hashCode = tel.hashCode(); //定义加密数字 int encryption = 20040213; //一层加密 long result = hashCode ^ encryption; //获取当前时间 long nowTime = System.currentTimeMillis(); //二层加密 result = result ^ nowTime; String temp = String.valueOf(result); return temp.substring(temp.length() - 6); } /** * 取出缓存中的验证码 * * @param tel 电话号码 * @return 如果电话号码相匹配则返回value * 当使用了@Cacheable注解,在返回时填入null即可。若缓存中key有对应的值则返回,无则返回null * 当使用了@Cacheable注解,需要被springboot的容器管理才能生效 */ @Cacheable(value = "smsCache", key = "#tel") public String get(String tel) { return null; } }
13.1.5.2启用方法注解
笔记小结
jetcache方法注解使用方式
13.1.5.2.1实现步骤:
1.在启动类上添加注解
Springboot20JetcacheApplication
@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
@EnableMethodCache(basePackages = "com.example")
public class Springboot20JetcacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot20JetcacheApplication.class, args);
}
}
注意
@EnableCreateCacheAnnotation
@EnableMethodCache(basePackages = “com.example”)(注意,方法注解,需要添加此注解)
两个注解需要联合使用,basePackages为启动方法缓存的包名,支持数组写法
2.使用方法注解操作缓存
@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);
}
@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;
}
}
当主系统与子系统让数据发生变化时,建议使用 @CacheRefresh(refresh = 10,timeUnit = TimeUnit.SECONDS)注解,让内存中的数据保持同步(系统会根据预定的时间,自动向数据库进行查询)
使用 **@CacheUpdate(**name = “smsCache_”, key = “#book.id”, value = “#book”)与, @CacheInvalidate(name = “smsCache_”, key = “#id”)是为了让缓存中的数据保持同步的更新或者删除
补充:若查询缓存中不存在的数据,系统会自动向数据库进行检索查询
3.缓存对象必须保障可序列化
@Data
public class Book implements Serializable {
}
jetcache:
remote:
default:
type: redis
keyConvertor: fastjson
valueEncoder: java
valueDecoder: java
keyConvertor: fastjson,将存入redis的key值,转换成字符串
valueEncoder: java,将进去的值转换为java对象
valueDecoder: java,将出来的值也转换为java对象否则报错
4.查看缓存统计报告
jetcache:
statIntervalMinutes: 15
在yml配置上添加即可
13.1.6缓存供应商变更:j2cache
- j2cache是一个缓存整合框架,可以提供缓存的整合方案,使各种缓存搭配使用,自身不提供缓存功能
- 基于 ehcache + redis 进行整合
13.1.6.1实现步骤:
1.加入j2cache坐标,加入整合缓存的坐标
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-spring-boot2-starter</artifactId>
<version>2.8.0-release</version>
</dependency>
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-core</artifactId>
<version>2.8.4-release</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
j2cache-spring-boot2-starter与 j2cache-core为j2cache框架的核心坐标,ehcache是缓存
2.配置使用j2cache(application.yml)
j2cache:
config-location: j2cache.properties
3.配置一级缓存与二级缓存以及一级缓存数据到二级缓存的发送方式(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
j2cache.broadcast = net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy,是将缓存数据的推送方式定为 发布与订阅
j2cache.L2.provider_class = net.oschina.j2cache.cache.support.redis.SpringRedisProvider,是用的内置的redis技术进行缓存
补充:若想要得到更多的缓存配置,则可以查看核心库的jar包文件做参考
4.配置ehcache.xml文件(ehcache.xml)
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--默认缓存策略 -->
<!-- external:是否永久存在,设置为true则不会被清除,此时与timeout冲突,通常设置为false-->
<!-- diskPersistent:是否启用磁盘持久化-->
<!-- maxElementsInMemory:最大缓存数量-->
<!-- overflowToDisk:超过最大缓存数量是否持久化到磁盘-->
<!-- timeToIdleSeconds:最大不活动间隔,设置过长缓存容易溢出,设置过短无效果-->
<!-- timeToLiveSeconds:最大存活时间-->
<!-- memoryStoreEvictionPolicy:缓存清除策略-->
<defaultCache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
配置文件可从maven包里面进行查找
5.设置使用缓存
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private CacheChannel 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);
}
}
通过频道进行set和get即可使用
as 运算符类似于强制转换,不同的是,当转换失败时,运算符将产生null值,而不是引发异常。
因此asString是强制转换,异常为空,与toString进行区别开来
13.1.6.2示例:
1.加入坐标
<!--j2cache-->
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-spring-boot2-starter</artifactId>
<version>2.8.0-release</version>
</dependency>
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-core</artifactId>
<version>2.8.4-release</version>
</dependency>
<!--ehcache-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
2.配置使用j2cache文件(application.yml)
j2cache:
config-location: j2cache.properties
3.配置使用j2cache缓存(j2cache.properties)
# 配置1级缓存
j2cache.L1.provider_class=ehcache
ehcache.configXml=ehcache.xml
# 配置2级缓存
j2cache.L2.provider_class=net.oschina.j2cache.cache.support.redis.SpringRedisProvider
j2cache.L2.config_section=redis
redis.hosts=localhost:6379
# 配置1级缓存数据到2级缓存的广播方式:可以使用redis提供的消息订阅模式,也可以使用jgroups多播实现(1级缓存如何达到2级缓存)
j2cache.broadcast=net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
4.配置使用j2cache的ehcache方案(ehcache.xml)
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--默认缓存策略 -->
<!-- external:是否永久存在,设置为true则不会被清除,此时与timeout冲突,通常设置为false-->
<!-- diskPersistent:是否启用磁盘持久化-->
<!-- maxElementsInMemory:最大缓存数量-->
<!-- overflowToDisk:超过最大缓存数量是否持久化到磁盘-->
<!-- timeToIdleSeconds:最大不活动间隔,设置过长缓存容易溢出,设置过短无效果-->
<!-- timeToLiveSeconds:最大存活时间-->
<!-- memoryStoreEvictionPolicy:缓存清除策略-->
<defaultCache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
5.业务成实现类
@Service
public class SMSCodeServiceImpl implements ISMSCodeService {
@Autowired
CodeUtil codeUtil;
@Autowired
CacheChannel cacheChannel;
@Override
public String sendCodeToSMS(String tel) {
String code = codeUtil.generator(tel);
cacheChannel.set("sms", tel, code);
return code;
}
@Override
public boolean checkCode(SMSCode smsCode) {
String code = cacheChannel.get("sms",smsCode.getTel()).asString();
return smsCode.getCode().equals(code);
}
}
13.2任务
普通定时
public class TimerTaskApp {
public static void main(String[] args) {
Timer timer = new Timer();
//task是个抽象类,因此可以直接new
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("Timer is running……");
}
};
//每间隔两秒就执行一下timer对象
timer.schedule(task, 0, 2000);
}
}
不规范,因此就有人做了定时任务技术
定时任务是企业级应用中的常见操作
- 年度报表
- 缓存统计报告
- … …
市面上流行的定时任务技术
- Quartz
- Spring Task
相关概念
- 工作(Job):用于定义具体执行的工作
- 工作明细(JobDetail):用于描述定时工作相关的信息
- 触发器(Trigger):用于描述触发工作的规则,通常使用cron表达式定义调度规则
- 调度器(Scheduler):描述了工作明细与触发器的对应关系
13.2.1Quartz
笔记小结
SpringBoot整合Quartz
- 工作(Job)
- 工作明细(JobDetail)
- 触发器(Trigger)
- 调度器(Scheduler)
13.2.1.1实现步骤:
1.导入SpringBoot整合quartz的坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.定义具体要执行的任务,继承****QuartzJobBean
public class QuartzTaskBean extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("quartz job run... ");
}
}
注意,此处并不定义类为springboot管控的类,而是跟普通java对象一样继承QuartzJobBean
定义具体要执行的任务,继承QuartzJobBean
当工作明细指定了此工作时,就会自动执行**executeInternal()**方法
3.定义工作明细与触发器,并绑定对应关系
@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();
}
}
newJob指定需要让那个job进行工作
storeDurably让具体的工作进行实例化存储
withSchedule添加定时计划
forJob指定工作明细
13.2.2Spring Task✳
笔记小结
Spring Task
- @EnableScheduling
- @Scheduled
13.2.2.1实现步骤:
1.开启定时任务功能
@SpringBootApplication
//开启定时任务
@EnableScheduling
public class Springboot22TaskApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot22TaskApplication.class, args);
}
}
在启动类上添加**@EnableScheduling注解**,表示启动定时任务
2.设置定时执行的任务,并设定执行周期
@Component
public class ScheduledBean {
@Scheduled(cron = "0/5 * * * * ?")
public void printLog(){
System.out.println(Thread.currentThread().getName()+":run...");
}
}
定义为springboot管控的对象,与Quartz做区别
添加 @Scheduled(cron = “0/5 * * * * ?”),填入相应的cron表达式即可
“0/5 * * * * ?”,表示每5秒执行一次
3.定时任务相关配置(可省略)
spring:
task:
scheduling:
# 任务调度线程池大小 默认 1
pool:
size: 1
# 调度线程名称前缀 默认 scheduling-
thread-name-prefix: ssm_
shutdown:
# 线程池关闭时等待所有任务完成
await-termination: false
# 调度线程关闭前最大等待时间,确保最后一定关闭
await-termination-period: 10s
thread-name-prefix配置线程名称前缀,可通过Thread.currentThread().getName()进行获取
13.2.2.2示例:
1.启动类
@SpringBootApplication
//开启定时任务
@EnableScheduling
public class Springboot22TaskApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot22TaskApplication.class, args);
}
}
2.配置类
创建config/ScheduledBean类
@Component
public class ScheduledBean {
@Scheduled(cron = "0/5 * * * * ?")
public void printLog() {
System.out.println("Schedule is running……");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pi7LMQoG-1678687150053)(SpringBoot2.assets/image-20230209162804808.png)]
3.额外配置(application.yml)
spring:
task:
scheduling:
# 任务调度线程池大小 默认 1
pool:
size: 1
# 调度线程名称前缀 默认 scheduling-
thread-name-prefix: ssm_
shutdown:
# 线程池关闭时等待所有任务完成
await-termination: false
# 调度线程关闭前最大等待时间,确保最后一定关闭
await-termination-period: 10s
13.3邮件
笔记小结
SpringBoot整合JavaMail发送简单邮件
13.3.1SpringBoot整合JavaMail
- SMTP(Simple Mail Transfer Protocol):简单邮件传输协议,用于发送电子邮件的传输协议
- POP3(Post Office Protocol - Version 3):用于接收电子邮件的标准协议
- IMAP(Internet Mail Access Protocol):互联网消息协议,是POP3的替代协议
13.3.1.1实现步骤:
1.导入SpringBoot整合JavaMail的坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.配置JavaMail
spring:
mail:
# 邮件供应商,smtp代表发送,qq.com表示使用qq邮件
host: smtp.qq.com
username: *********@qq.com
password: *********
配置授权信息
记得在邮件中开启相应服务
此时的passsword不是你账户的密码,是开启服务后相应的值
3.开启发送任务功能—简单邮件
@Service
public class SendMailServiceImpl implements SendMailService {
//定义一个邮件寄送者
@Autowired
private JavaMailSender javaMailSender;
private String from = "********@qq.com"; // 发送人
private String to = "********@126.com"; // 接收人
private String subject = "测试邮件"; // 邮件主题
private String text = "测试邮件正文"; // 邮件内容
@Override
public void sendMail() {
SimpleMailMessage mailMessage = new SimpleMailMessage();
//设置发送人
//mailMessage.setFrom(from+"(小甜甜)");
mailMessage.setFrom(from);
//设置接收人
mailMessage.setTo(to);
//设置邮件主题
mailMessage.setSubject(subject);
//设置邮件内容
mailMessage.setText(text);
//发送邮件
javaMailSender.send(mailMessage);
}
}
注意:配置在application.yml文件中的配置信息的username需要与发送人一直,否则会报错:
补充:若在设置 //**mailMessage.setFrom(from+“(小甜甜)”);**添加名称,那么发送邮件时,会替代相关发送人信息
3.开启发送任务功能—复杂邮件
@Service
public class SendMailServiceImpl2 implements ISendMailService {
@Autowired
JavaMailSender javaMailSender;
// 发送人
private String from = "[email protected]";
// 接收人
private String to = "[email protected]";
// 邮件主题
private String subject = "测试邮件";
// 邮件内容
private String text = "测试邮件正文";
//html标签
private String htmlTest = "<a href='https://www.baidu.com/'>百度一下</a>";
/**
* 发送邮件
*/
@Override
public void sendMail() {
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
//创建helper对象---若需要多个附件,则需要设置第二个multipart参数为true
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
//设置发送人
helper.setFrom(from);
//设置接收人
helper.setTo(to);
//设置邮件主题
helper.setSubject(subject);
//设置邮件内容---若需要让页面解析html,则需要设置第二个html参数为true
helper.setText(text + htmlTest, true);
//设置附件---添加附件,需要使用FIle进行读取本地的相关资源
File file = new File("E:\\Documents\\Learn\\SpringBoot\\springboot_23_mail\\src\\main\\resources\\static\\微信图片_20230209221711.jpg");
//将attachmentFilename的名字加上后缀,可在浏览器中直接预览
helper.addAttachment("大美女.png", file);
File file1 = new File("E:\\Documents\\Learn\\SpringBoot\\springboot_23_mail\\target\\springboot_23_mail-0.0.1-SNAPSHOT.jar");
helper.addAttachment(file1.getName(), file1);
//发送邮件
javaMailSender.send(mimeMessage);
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}
4.调用功能
@SpringBootTest
class Springboot23MailApplicationTests {
@Autowired
ISendMailService service;
@Test
void contextLoads() {
service.sendMail();
}
}
在test中直接进行调用
13.3.1.2示例:
1.导入坐标
在pom.xml文件中
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.配置JavaMail
在application.yml文件中
spring:
mail:
# 邮件供应商,smtp代表发送,qq.com表示使用qq邮件
host: smtp.qq.com
username: *********@qq.com
password: *********
3.开启发送任务功能
@Service
public class SendMailServiceImpl implements SendMailService {
//定义一个邮件寄送者
@Autowired
private JavaMailSender javaMailSender;
private String from = "********@qq.com"; // 发送人
private String to = "********@126.com"; // 接收人
private String subject = "测试邮件"; // 邮件主题
private String text = "测试邮件正文"; // 邮件内容
@Override
public void sendMail() {
SimpleMailMessage mailMessage = new SimpleMailMessage();
//设置发送人
//mailMessage.setFrom(from+"(小甜甜)");
mailMessage.setFrom(from);
//设置接收人
mailMessage.setTo(to);
//设置邮件主题
mailMessage.setSubject(subject);
//设置邮件内容
mailMessage.setText(text);
//发送邮件
javaMailSender.send(mailMessage);
}
}
4.测试
在Springboot23MailApplicationTests.java文件中
@SpringBootTest
class Springboot23MailApplicationTests {
@Autowired
ISendMailService service;
@Test
void contextLoads() {
service.sendMail();
}
测试结果
简单邮件
复杂邮件
13.4消息
笔记小结
1.消息概念与作用
2.JMS
3.AMQP
4.MQTT
- 消息发送方(生产者)
- 消息接收方(消费者)
同步消息:简单的说,用微信聊天来假设,就是对方说完一句话,你才能说一句话
异步消息:简单的说,用微信聊天来假设,就是对方说很多句话,你回复与否都无所谓
编程的应用场景:
若业务需求都累计在一个服务器就会造成卡顿,因此希望通过MQ这个中间件来进行业务需求的分发。
企业级应用中广泛使用的三种异步消息传递技术✳
- JMS
- AMQP
- MQTT
1.JMS(Java Message Service):一个规范,等同于JDBC规范,提供了与消息服务相关的API接口
-
JMS消息模型
-
peer-2-peer:点对点模型,消息发送到一个队列中,队列保存消息。队列的消息只能被一个消费者消费,或超时
-
publish-subscribe:发布订阅模型,消息可以被多个消费者消费,生产者和消费者完全独立,不需要感知对方的存在
-
-
JMS消息种类
- TextMessage
- MapMessage
- BytesMessage
- StreamMessage
- ObjectMessage
- Message (只有消息头和属性)
-
JMS实现:ActiveMQ、Redis、HornetMQ、RabbitMQ、RocketMQ(没有完全遵守JMS规范)
2.AMQP(advanced message queuing protocol):一种协议(高级消息队列协议,也是消息代理规范),规范了网络交换的数据格式,兼容JMS
- 优点:具有跨平台性,服务器供应商,生产者,消费者可以使用不同的语言来实现
- AMQP消息模型
- direct exchange
- fanout exchange
- topic exchange
- headers exchange
- system exchange
- AMQP消息种类:byte[]
- AMQP实现:RabbitMQ、StormMQ、RocketMQ
3.MQTT(Message Queueing Telemetry Transport)消息队列遥测传输,专为小设备设计,是物联网(IOT)生态系统中主要成分之一
4.Kafka
- Kafka,一种高吞吐量的分布式发布订阅消息系统,提供实时消息功能。
13.4.1案例展示—订单短信通知
购物订单业务
- 登录状态检测
- 生成主单
- 生成子单
- 库存检测与变更
- 积分变更
- 支付
- 短信通知(异步)
- 购物车维护
- 运单信息初始化
- 商品库存维护
- 会员维护
- …
1.业务层
接口
OrderService
public interface OrderService {
//订单的处理
public void order(String id);
}
MessageService
public interface MessageService {
//发送短信
public void sendMessage(String id);
//处理短信
public String doMessage();
}
实现类
OrderServiceImpl
/*提供订单业务处理的服务*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
MessageService messageService;
/*此处业务中只提供订单的id*/
@Override
public void order(String id) {
//一系列操作,包括各种服务调用,处理各种业务
System.out.println("订单处理开始");
System.out.println("…………");
//短信消息处理
messageService.sendMessage(id);
System.out.println("…………");
System.out.println("订单处理结束");
System.out.println();
}
}
MessageServiceImpl
/*订单业务中的需要处理的其中一项————发送短信*/
@Service
public class MessageServiceImpl implements MessageService {
//模拟队列
private ArrayList<String> msgList = new ArrayList<>();
@Override
public void sendMessage(String id) {
System.out.println("待发送的订单已纳入处理队列,id:" + id);
msgList.add(id);
}
@Override
public String doMessage() {
String id = msgList.remove(0);
System.out.println("已完成短信发送业务,id:" + id);
return id;
}
}
2.表现层
OrderController
/*提供一个订单开始进行处理*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
OrderService orderService;
@PostMapping("/{id}")
public void order(@PathVariable String id) {
orderService.order(id);
}
}
MessageController
@RestController
@RequestMapping("/msgs")
public class MessageController {
@Autowired
MessageService messageService;
@GetMapping
public String doMessage() {
String id = messageService.doMessage();
return id;
}
}
13.4.2ActiveMQ
-
下载地址:https://activemq.apache.org/components/classic/download/
注意:5.17及以上java1.8不支持
-
安装:解压缩
-
启动服务
activemq.bat
-
访问服务器
http://127.0.0.1:8161/
-
服务端口:61616,管理后台端口:8161
用户名&密码:admin
13.4.2.1实现步骤:
1.导入SpringBoot整合ActiveMQ坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
2.配置ActiveMQ(采用默认配置)
spring:
activemq:
#配置activemq的服务地址
broker-url: tcp://localhost:61616
#配置JSM默认的保存位置
jms:
#修改为发布与订阅模式
pub-sub-domain: true
template:
default-destination: mysoul
3.生产与消费消息(使用默认消息存储队列)
@Service
public class MessageServiceActivemqImpl implements MessageService {
//遵守JMS的规范,因此用这个API
@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);
}
}
注意,默认消息存储队列,需要在yml文件中进行默认的位置配置
补充:使用convertAndSend方法,不管什么数据,先进行类型转换再进行发送
使用receiveAndConvert方法,思安接收数据再进行数据的转换
4.生产与消费消息(指定消息存储队列)
@Service
public class MessageServiceActivemqImpl implements MessageService {
//遵守JMS的规范,因此用这个API
@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);
}
}
补充:使用convertAndSend方法
4.使用消息监听器对消息队列监听
@Component
public class MessageListener {
@JmsListener(destination = "order.sm.queue.id")
public void receive(String id){
System.out.println("已完成短信发送业务,id:"+id);
}
}
使用**@JmsListener**(destination = “order.sm.queue.id”)注解,并填好队列名称
5.流程性业务消息消费完转入下一个消息队列
@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;
}
}
使用 @SendTo(“order.other.queue.id”)注解,并填好需要转发到的队列名称
补充:若使用@SendTo注解需要配置返回值,因为此注解是根据返回值进行转发
13.4.2.2示例:
1.业务层
创建service/impl/activemq下的MessageServiceActivemqImpl实现类
@Service
public class MessageServiceActivemqImpl implements MessageService {
//遵守JMS的规范,因此用这个API
@Autowired
JmsMessagingTemplate jmsMessagingTemplate;
// //使用JSM默认的保存位置
// @Override
// public void sendMessage(String id) {
// System.out.println("待发送的订单已纳入处理队列,id:" + id);
// jmsMessagingTemplate.convertAndSend(id);
// }
//
// @Override
// public String doMessage() {
// String id = jmsMessagingTemplate.receiveAndConvert(String.class);
// System.out.println("已完成短信发送业务,id:" + id);
// return id;
// }
//使用JMS自定义认的保存位置
@Override
public void sendMessage(String id) {
System.out.println("待发送的订单已纳入处理队列,id:" + id);
jmsMessagingTemplate.convertAndSend("order.sm.queue.id", id);
}
/*若配置了listener监听,此方法不再执行*/
@Override
public String doMessage() {
String id = jmsMessagingTemplate.receiveAndConvert("order.sm.queue.id", String.class);
System.out.println("已完成短信发送业务,id:" + id);
return id;
}
创建service/impl/activemq/listener下的MessageListener作为监听器的类
@Component
public class MessageListener {
@JmsListener(destination = "order.sm.queue.id")
public void receive(String id) {
System.out.println("已完成短信发送业务,id:" + id);
}
}
13.4.3RabbitMQ✳
RabbitMQ基于Erlang语言编写,需要安装Erlang
-
Erlang
- 下载地址:https://www.erlang.org/downloads
- 安装:一键傻瓜式安装,安装完毕需要重启,需要依赖Windows组件
-
环境变量配置
-
ERLANG_HOME
-
PATH
有的电脑在安装时若提示没有安装VIsual C++的库,需要安装完毕之后方可使用
下载地址:https://rabbitmq.com/install-windows.html
安装:一键傻瓜式安装若Erlang配置成功则可以打开RabbitMQ的安装程序
否则报错
-
-
启动服务
rabbitmq-service.bat start
- 关闭服务
rabbitmq-service.bat stop
- 查看服务状态
rabbitmqctl status
服务管理可视化(插件形式)
- 查看已安装的插件列表
rabbitmq-plugins.bat list
- 开启服务管理插件
rabbitmq-plugins.bat enable rabbitmq_management
- 访问服务器
http://localhost:15672
- 服务端口:5672,管理后台端口:15672
- 用户名&密码:guest
13.4.3.1Springboot整合RabbitMQ的直连交换机模式
13.4.3.1.1实现步骤:
1.导入SpringBoot整合RabbitMQ坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.配置RabbitMQ (采用默认配置)
spring:
rabbitmq:
host: localhost
port: 5672
3.定义消息队列(direct)
//让容器能够找到此配置
@Configuration
public class RabbitDirectConfig {
// @Bean
// public Queue queue(){
// // durable:是否持久化,默认false
// // exclusive:是否当前连接专用,默认false,连接关闭后队列即被删除
// // autoDelete:是否自动删除,当生产者或消费者不再使用此队列,自动删除
// return new Queue("simple_queue",true,false,false);
// }
@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");
}
}
Queue 创建两个队列(定义存储消息的对象)
DirectExchange 创建一个交换机
Binding 将队列与交换机进行绑定关系
补充:一个交换机可以多个队列进行复用
4.生产与消费消息(direct)
@Service
public class MessageServiceRabbitmqDirectImpl implements MessageService {
//amqp,因为springboot整合了rabbitmq,因此使用amqpTemplate
@Autowired
private AmqpTemplate amqpTemplate;
@Override
public void sendMessage(String id) {
System.out.println("使用Rabbitmq将待发送短信的订单纳入处理队列,id:"+id);
amqpTemplate.convertAndSend("directExchange","direct",id);
}
}
5.使用消息监听器对消息队列监听(direct)
@Component
public class RabbitMessageListener {
@RabbitListener(queues = "direct_queue")
public void receive(String id){
System.out.println("已完成短信发送业务,id:"+id);
}
}
实用@RabbitListener注解
5.可使用多消息监听器对消息队列监听进行消息轮循处理(direct)
@Component
public class RabbitMessageListener2 {
@RabbitListener(queues = "direct_queue")
public void receive(String id){
System.out.println("已完成短信发送业务(two),id:"+id);
}
}
13.4.3.2示例:
业务层:
1.创建service/impl/rabbitmq/direct下的MessageServiceRabbitmqDirectImpl实现类
/*生产消息*/
@Service
public class MessageServiceRabbitmqDirectImpl implements MessageService {
//amqp,因为springboot整合了rabbitmq,因此使用amqpTemplate
@Autowired
private AmqpTemplate amqpTemplate;
@Override
public void sendMessage(String id) {
System.out.println("使用Rabbitmq将待发送短信的订单纳入处理队列(direct),id:" + id);
//rabbitmq的交换机直连模式需要配置交换机以及队列与交换机之间的关系
amqpTemplate.convertAndSend("directExchange", "direct", id);
}
@Override
public String doMessage() {
return null;
}
}
注意rabbitmq的交换机直连模式
2.创建service/impl/rabbitmq/direct/config下的RabbitDirectConfig类
@Configuration
public class RabbitDirectConfig {
// @Bean
// public Queue queue(){
// // durable:是否持久化,默认false
// // exclusive:是否当前连接专用,默认false,连接关闭后队列即被删除
// // autoDelete:是否自动删除,当生产者或消费者不再使用此队列,自动删除
// return new Queue("simple_queue",true,false,false);
// }
//定义队列对象
@Bean
public Queue directQueue() {
return new Queue("direct_queue");
}
//定义队列对象
@Bean
public Queue directQueue2() {
return new Queue("direct_queue2");
}
//定义队列的direct模式交换机
@Bean
public DirectExchange directExchange() {
return new DirectExchange("directExchange");
}
//定义队列对象与交换机的联系
@Bean
public Binding directBinding() {
return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct");
}
//定义队列对象与交换机的联系
@Bean
public Binding directBinding2() {
return BindingBuilder.bind(directQueue2()).to(directExchange()).with("direct2");
}
}
需要将配置定义为springboot所管控的bean,因此别忘了加上@Configuration注解
3.创建service/impl/rabbitmq/direct/listener下的RabbitMessageListener监听器
@Component
public class RabbitMessageListener {
@RabbitListener(queues = "direct_queue")
public void receive(String id) {
System.out.println("已完成短信发送业务,id:" + id);
}
}
13.4.3.2Springboot整合RabbitMQ的topic交换机模式
13.4.3.2.1实现步骤:
1.步骤跟整合RabbitMQ直连交换机模式类似
2.定义消息队列(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模式的交换机不一样 TopicExchange
topic模式的联系tipic的binding可以绑定键匹配规则
- (星号): 用来表示一个单词 ,且该单词是必须出现的
- # (井号): 用来表示任意数量
匹配键 topic.*.* topic.# topic.order.id true true order.topic.id false false topic.sm.order.id false true topic.sm.id false true topic.id.order true true topic.id false true topic.order false true
3.生产与消费消息(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);
}
}
4.使用消息监听器对消息队列监听(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);
}
}
13.4.3.2.2示例:
业务层:
1.创建service/impl/rabbitmq/topic下的MessageServiceRabbitmqTopicImpl实现类
/*生产消息*/
@Service
public class MessageServiceRabbitmqTopicImpl implements MessageService {
//amqp,因为springboot整合了rabbitmq,因此使用amqpTemplate
@Autowired
private AmqpTemplate amqpTemplate;
@Override
public void sendMessage(String id) {
System.out.println("使用Rabbitmq将待发送短信的订单纳入处理队列(topic),id:" + id);
//rabbitmq的交换机topic模式需要配置交换机以及队列与交换机之间的关系
amqpTemplate.convertAndSend("topicExchange", "topic.orders.id", id);
}
@Override
public String doMessage() {
return null;
}
}
注意rabbitmq的交换机topic模式
2.创建service/impl/rabbitmq/direct/topic下的RabbitTopicConfig类
@Configuration
public class RabbitTopicConfig {
// @Bean
// public Queue queue(){
// // durable:是否持久化,默认false
// // exclusive:是否当前连接专用,默认false,连接关闭后队列即被删除
// // autoDelete:是否自动删除,当生产者或消费者不再使用此队列,自动删除
// return new Queue("simple_queue",true,false,false);
// }
//定义队列对象
@Bean
public Queue topicQueue() {
return new Queue("topic_queue");
}
//定义队列对象
@Bean
public Queue topicQueue2() {
return new Queue("topic_queue2");
}
//定义队列的topic模式交换机
@Bean
public TopicExchange topicExchange() {
return new TopicExchange("topicExchange");
}
//定义队列对象与交换机的联系
@Bean
public Binding topicBinding() {
return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("topic.*.*");
}
//定义队列对象与交换机的联系
@Bean
public Binding topicBinding2() {
return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("topic2");
}
}
需要将配置定义为springboot所管控的bean,因此别忘了加上@Configuration注解
3.创建service/impl/rabbitmq/topic/listener下的RabbitMessageListener监听器
@Component
public class RabbitMessageListener {
@RabbitListener(queues = "topic_queue")
public void receive(String id) {
System.out.println("已完成短信发送业务,id:" + id);
}
}
13.4.4RocketMQ
笔记小结
1.RocketMQ下载与安装(环境变量配置)
2.命名服务器启动(控制台)
3.broker服务启动(控制台)
4.消息生产消费测试
下载地址:https://rocketmq.apache.org/
安装:解压缩
- 默认服务端口:9876
环境变量配置
- ROCKETMQ_HOME
- PATH
- NAMESRV_ADDR (建议): 127.0.0.1:9876
命名服务器与broker
启动命名服务器
mqnamesrv
双击mqnamesrv.cmd即可
启动broker
mqbroker
双击mqbroker.cmd即可
服务器功能测试:生产者
tools org.apache.rocketmq.example.quickstart.Producer
服务器功能测试:消费者
tools org.apache.rocketmq.example.quickstart.Consumer
13.4.4.1实现步骤:
1.导入SpringBoot整合RocketMQ坐标
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.1</version>
</dependency>
2.配置RocketMQ (采用默认配置)
rocketmq:
#配置rocketmq的服务地址
name-server: localhost:9876
#rocketmq需要配置生产者分组组名
producer:
group: group_rocketmq
3.生产消息
@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);
}
}
4.生产异步消息
@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);
}
}
5.使用消息监听器对消息队列监听
@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);
}
}
13.4.4.2示例:
业务层:
1.MessageServiceRocketmqImpl
创建service/impl/rocketmq下的MessageServiceRocketmqImpl实现类
/*生产消息*/
@Service
public class MessageServiceRocketmqImpl implements MessageService {
@Autowired
private RocketMQTemplate rocketMQTemplate;
@Override
public void sendMessage(String id) {
System.out.println("使用Rabbitmq将待发送短信的订单纳入处理队列,id:" + id);
// rocketMQTemplate.convertAndSend("order_sm_id", id);
SendCallback callback = new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("消息发送成功");
}
@Override
public void onException(Throwable throwable) {
System.out.println("消息发送失败!!!!!!!!!!!");
}
};
//rocketMQ异步发送的方式
//destination为消息发送的队列位置
rocketMQTemplate.asyncSend("order_sm_id", id, callback);
}
@Override
public String doMessage() {
return null;
}
}
2.RocketmqMessageListener
3.创建service/impl/rocketmq/listener下的RocketmqMessageListener监听器
/*此listener与activemq,rabbitmq不同,此处listener需要添加注解*/
@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);
}
}
13.4.5Kafka
笔记小结
1.Kafka下载与安装(环境变量配置)
2.zookeeper启动(控制台)
3.kafka服务启动(控制台)
4.topic维护
5.消息生产消费测试
下载地址:https://kafka.apache.org/downloads
- windows 系统下3.0.0版本存在BUG,建议使用2.X版本
若下载错误再执行时会报错:
Classpath is empty. Please build the project first e.g. by running ‘gradlew jarAll’
安装:解压缩
启动zookeeper
zookeeper-server-start.bat ..\..\config\zookeeper.properties
- 默认端口:2181
启动kafka
kafka-server-start.bat ..\..\config\server.properties
- 默认端口:9092
创建topic
kafka-topics.bat --create --bootstrap-server localhost:2181 --replication-factor 1 --partitions 1 --topic itheima
查看topic
kafka-topics.bat --bootstrap-server 127.0.0.1:2181 --list
删除topic
kafka-topics.bat --delete --bootstrap-server 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
13.4.5.1实现步骤:
1.导入SpringBoot整合Kafka坐标
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
2.配置Kafka (采用默认配置)
spring:
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: order
需要配置消费者的组
3.生产消息
@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);
}
}
4.使用消息监听器对消息队列监听
@Component
public class KafkaMessageListener{
@KafkaListener(topics = {"kafka_topic"})
public void onMessage(ConsumerRecord<?, ?> record) {
System.out.println("已完成短信发送业务,id:"+record.value());
}
}
14.监控
14.1监控的意义
- 监控服务状态是否宕机
- 监控服务运行指标(内存、虚拟机、线程、请求等)
- 监控日志
- 管理服务(服务下线)
显示监控信息的服务器:用于获取服务信息,并显示对应的信息
运行的服务:启动时主动上报,告知监控服务器自己需要受到监控
显示监控信息的服务器 可以主动去拉取 运行的服务的消息
运行的服务 会自动上报给 显示监控的信息,并把配置参数进行上传
14.2可视化监控平台
Spring Boot Admin,开源社区项目,用于管理和监控SpringBoot应用程序。 客户端注册到服务端后,通过HTTP请求方式,服务端定期从客户端获取对应的信息,并通过UI界面展示对应信息。
14.2.1Admin服务端
1.创建服务端的程序时,可以勾选Ops内的如下内容
2.在pom.xml中导入坐标
<properties>
<spring-boot-admin.version>2.5.4</spring-boot-admin.version>
</properties>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
注意是spring-boot-admin-starter-server
以上全部信息可简写如下代码
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> <version>2.5.4</version> </dependency>
注意:版本需要与springboot的版本配置为一样的,例如当前版本为2.5.4,那么springboot的版本也应为2.5.4
详细查看GitHub - codecentric/spring-boot-admin: Admin UI for administration of spring boot applications查看版本信息
需要配置web依赖
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置web依赖是为了让监控平台在浏览器的网页中进行打开
3.application.yml
server:
port: 8080
4.设置启用Spring-Admin
@SpringBootApplication
//开启admin服务
@EnableAdminServer
public class Springboot25ActuatorServerApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot25ActuatorServerApplication.class, args);
}
}
14.2.2Admin客户端
1.创建客户端的程序时,可以勾选Ops内的如下内容
2.在pom.xml中导入坐标
<properties>
<spring-boot-admin.version>2.5.4</spring-boot-admin.version>
</properties>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
注意是spring-boot-admin-starter-client
以上全部信息可简写如下代码
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>2.5.4</version> </dependency>
注意:版本需要与springboot的版本配置为一样的,例如当前版本为2.5.4,那么springboot的版本也应为2.5.4
详细查看GitHub - codecentric/spring-boot-admin: Admin UI for administration of spring boot applications查看版本信息
需要配置web依赖
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置web依赖是为了让程序能够持续的运行,便于让监控平台监控
application.yml
server:
port: 8081
spring:
boot:
admin:
client:
url: http://localhost:8080
management:
# 启用健康信息详细展示端点
endpoint:
health:
show-details: always
# 设置web展示所有的资源项
endpoints:
web:
exposure:
include: "*"
在客户端中,需要将想给服务端看的配置进行yml文件的设置
此处需要配置server.port以便于程序运行,注意不要与服务端配置端口产生冲突
14.2.3可视化监控平台
14.3监控原理
笔记小结
1.Actuator
2.端点功能开启与关闭
3.端点功能暴露
- Actuator提供了SpringBoot生产就绪功能,通过端点的配置与访问,获取端点信息
- 端点描述了一组监控信息,SpringBoot提供了多个内置端点,也可以根据需要自定义端点信息
- 访问当前应用所有端点信息:/actuator
- 访问端点详细信息:/actuator/端点名称
例如:
以Admin客户端的application.yml为例
server:
port: 8081
spring:
boot:
admin:
client:
url: http://localhost:8080
management:
# 启用健康信息详细展示端点
endpoint:
health:
show-details: always
# 设置web展示所有的资源项
endpoints:
web:
exposure:
include: "*"
Web程序专用端点
启用指定端点
management:
endpoint:
health: # 端点名称
enabled: true
show-details: always
beans: # 端点名称
enabled: true
启用所有端点
management:
endpoints:
enabled-by-default: true
暴露端点功能
端点中包含的信息存在敏感信息,需要对外暴露端点功能时手动设定指定端点信息
常用示例:
management:
# 配置健康信息详细展示
endpoint:
health:
show-details: always
info:
enabled: false
# 展示所有的资源项
endpoints:
web:
exposure:
include: "*"
展示,一般设置为展示所有的信息,若想单独禁用某项,指定端点时,进行禁用
14.4自定义监控指标
14.4.1info端点指标控制
为info端点添加自定义指标
application.yml
info:
appName: @project.artifactId@
version: @project.version@
author: itheima
动态获取info
@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);
}
}
实现InfoContributor,使用builder.withDetail或者builder.withDetails
14.4.2helath端点指标控制
为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);
}
}
}
需要注意的是类名会成为heal配置所显示的方法名
14.4.3metrics端点指标控制
@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;
}
}
再需要添加的指标监控中,添加counter.increment();,这样系统会再程序启动时自动注
14.4.4自定义端点
@Component
@Endpoint(id="pay", enableByDefault = true)
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;
}
}
需要让springboot直到这是一个端点,因此需要添加@Endpoint(id=“pay”),并写入名称。
当程序加载时就需要读取此端点,因此需要添加 @ReadOperation注解
若想拥有返回值并想返回json字符串,需要返回map集合