1. SpringBoot 整合 定时任务
1.1 常见cron表达式查询网站
(1) cron.qqe2.com/
注意:这里要注意,这个工具 '年' 是不支持的。cron位数严格要求为6位。
(2) crontab.guru
1.2 常见cron表达式
1.3 整合 定时任务task
SpringBoot整合定时任务task非常的简单,共分为以下三步:
-
在启动类加上@EnableScheduling注解
-
在controller的类上加上@Component注解
-
在controller的方法上加上@Scheduled注解即可
之后启动程序,就会自动开始执行任务了。
(1) 首先启动类上添加注解 @EnableScheduling 来 开启定时任务
/*开启定时任务*/
@EnableScheduling
@SpringBootApplication
@MapperScan(basePackages = "com.learn.tm.mapper")
public class TianmengApplication {
public static void main(String[] args) {
SpringApplication.run(TianmengApplication.class, args);
}
}
(2) 然后要定时执行的方法上添加 @Scheduled 注解并指定定时规则
/*每隔3s执行一次*/
@Scheduled(fixedRate = 3000)
public void cronJobs(){
System.out.println(new Date());
}
1.4 整合 XXL-JOB
源码地址 :
1.4.1 xxl-job单独服务
1.4.1.1 下载
首先打开官网地址,开始下载源码,即上面的源码地址,哪个都可以。下载之后使用idea工具打开,项目结构如下:
xxl-job-admin:调度中心
xxl-job-core:公共依赖
xxl-job-executor-samples:执行器Sample示例(选择合适的版本执行器,可直接使用,也可以参考其并将现有项目改造成执行器)
:xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器,推荐这种方式;
:xxl-job-executor-sample-frameless:无框架版本;
1.4.1.2 执行sql文件
文件路径 : doc\db\tables_xxl_job.sql
执行完成后会生成如下数据库和表 :
XXL-JOB调度模块基于自研调度组件并支持集群部署,调度数据库表说明如下:
xxl_job_lock:任务调度锁表;xxl_job_group:执行器信息表,维护任务执行器信息;xxl_job_info:调度扩展信息表: 用于保存XXL-JOB调度任务的扩展信息,如任务分组、任务名、机器地址、执行器、执行入参和报警邮件等等;xxl_job_log:调度日志表: 用于保存XXL-JOB任务调度的历史信息,如调度结果、执行结果、调度入参、调度机器和执行器等等;xxl_job_log_report:调度日志报表:用户存储XXL-JOB任务调度日志的报表,调度中心报表功能页面会用到;xxl_job_logglue:任务GLUE日志:用于保存GLUE更新历史,用于支持GLUE的版本回溯功能;xxl_job_registry:执行器注册表,维护在线的执行器和调度中心机器地址信息;xxl_job_user:系统用户表;
1.4.1.3 修改配置文件
# 首先修改xxl-job-admin模块下的application.properties配置
修改端口号:
server.port=8090
修改数据库连接账号密码:
spring.datasource.url=jdbc:mysql://192.168.198.100:3306/xxl_job
spring.datasource.username=root
spring.datasource.password=123456
# 然后修改xxl-job-executor-samples下的xxl-job-executor-sample-springboot模块下的application.properties配置
修改端口号:
server.port=8091
修改xxl.job.admin.addresses配置:
xxl.job.admin.addresses=http://127.0.0.1:8090/xxl-job-admin
修改xxl.job.executor.port端口号:(不冲突可不修改)
xxl.job.executor.port=9999
1.4.1.4 添加测试方法
xxl-job-executor-samples下的xxl-job-executor-sample-springboot模块下的SampleXxlJob类添加测试方法
@XxlJob("testJobHandler")
public void testJobHandler(){
System.out.println("测试定时任务");
}
1.4.1.5 测试
启动2个服务xxl-job-admin和xxl-job-executor-sample-springboot
浏览器访问 : http://localhost:8090/xxl-job-admin/ 进入到登录页面 默认账号密码为admin 123456
登录进来效果 :
新建定时任务 :
JobHandler配置的内容就是@XxlJob("testJobHandler")注解里的testJobHandler
配置完成后,点击启动,等到整分钟的时候,就可以看到控制台打印日志了,并且也可以通过系统查看日志:
1.4.2 xxl-job整合到已有springboot服务
1.4.2.1 添加配置信息
在已有服务的yml配置文件中添加如下内容 :
这里注意几个点
(1) accessToken 默认值为 default_token ,不填写可能会有问题
(2) appname 这里我自定义了一个,后面需要在页面上配置一下,否则会导致定时任务不生效,名称暂时定为 xxl-job-SpringBoot-learn
(3) addresses 后面配置的信息需要跟一会启动的xxl-job-admin服务保持一致。
xxl:
job:
admin:
# 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。
addresses: http://127.0.0.1:8090/xxl-job-admin
# 执行器通讯TOKEN [选填]:非空时启用
accessToken: default_token
executor:
# 执行器的应用名称
appname: xxl-job-SpringBoot-learn
# 执行器注册 [选填]:优先使用该配置作为注册地址
address: ""
# 执行器IP [选填]:默认为空表示自动获取IP
ip: ""
# 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999
port: 9999
# 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
logpath: /data/applogs/xxl-job/jobhandler
# 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
logretentiondays: -1
1.4.2.2 新增依赖
在已有服务中添加如下依赖 :
<!-- xxl-job-core -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.1</version>
</dependency>
1.4.2.3 新增配置文件
在已有服务中添加如下配置 :
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")
private String adminAddresses;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.address}")
private String address;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logPath;
@Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}
}
1.4.2.4 添加测试方法
在已有服务中添加如下测试方法
@XxlJob("testJobHandler")
public void testJobHandler() throws InterruptedException {
System.out.println("进入测试方法");
}
1.4.2.5 配置执行器
浏览器访问 : http://localhost:8090/xxl-job-admin 默认账号密码为 admin 123456
首先在执行器管理中新增一个执行器 :
然后任务管理中新增一个任务,注意执行器一定要选择正确,否则会一直报执行器为空,导致定时任务不能正常执行。
一切都完成后,启动两个服务 : 已有服务 和 xxl-job-admin 服务,可以看到控制台打印的日志内容。
具体xxl-job-admin的源码下载,参考上面即可。
2. SpringBoot 整合 redis
在 springboot 1.5.x版本的默认的Redis客户端是 Jedis实现的,springboot 2.x版本中默认客户端是用 lettuce实现的
lettcus与jedis的区别:
(1)jedis:采用的直连,多个线程操作的话,是不安全的。如果要避免不安全,使用jedis pool连接池。更像BIO模式。但是这样整体性能就大受影响。
(2)lettuce基于Netty框架进行与Redis服务器连接,实例可以在多个线程中共享,不存在线程不安全的情况。可以减少线程数据了,更像NIO模式
2.1 redisTemplate
2.1.1 添加依赖
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.1.2 创建配置文件
package com.learn.tm.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
2.1.3 添加配置信息
spring:
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址(根据业务对应修改)
host: 192.168.198.100
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
# password: 123456
# 连接超时时间(毫秒)
timeout: 1000
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池最大连接数(使用负值表示没有限制)
max-active: 200
# 连接池中的最大空闲连接
max-idle: 10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
2.1.4 测试
@Autowired
private RedisTemplate<String,String> redisTemplate;//这里要注意泛型的类型
@GetMapping("getMsg")
public Result<String> getMsg(){
redisTemplate.opsForValue().set("k","v");
String k = redisTemplate.opsForValue().get("k");
System.out.println(k);//输出v
//"code":0,"msg":"操作成功","data":"v"}
return new Result<String>().ok(k);
}
2.2 jedis
因为 springboot2.0中默认是使用 Lettuce来集成Redis服务,spring-boot-starter-data-redis默认只引入了 Lettuce包,并没有引入 jedis包支持。所以在我们需要手动引入 jedis的包,并排除掉 lettuce的包
2.2.1 添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
2.2.2 添加配置信息
spring:
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址(根据业务对应修改)
host: 192.168.198.100
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
# password: 123456
# 连接超时时间(毫秒)
timeout: 1000
jedis:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池最大连接数(使用负值表示没有限制)
max-active: 8
# 连接池中的最大空闲连接
max-idle: 10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
2.2.3 创建配置文件
因为在 springoot 2.x版本中,默认采用的是 Lettuce实现的,所以无法初始化出 Jedis的连接对象 JedisConnectionFactory,所以我们需要手动配置并注入
这里要提醒的一点就是,在springboot 2.x版本中 JedisConnectionFactory设置连接的方法已过时
在 springboot 2.x版本中推荐使用 RedisStandaloneConfiguration类来设置连接的端口,地址等属性
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
import java.io.Serializable;
import java.time.Duration;
@Configuration
public class JedisRedisConfig {
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-active}")
private int maxActive;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWaitMillis;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
/**
* 连接池配置信息
*/
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大连接数
jedisPoolConfig.setMaxTotal(maxActive);
// 当池内没有可用连接时,最大等待时间
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
// 最大空闲连接数
jedisPoolConfig.setMinIdle(maxIdle);
// 最小空闲连接数
jedisPoolConfig.setMinIdle(minIdle);
// 其他属性可以自行添加
return jedisPoolConfig;
}
/**
* Jedis 连接
*
* @param jedisPoolConfig
* @return
*/
@Bean
public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder().usePooling()
.poolConfig(jedisPoolConfig).and().readTimeout(Duration.ofMillis(timeout)).build();
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(RedisPassword.of(password));
return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
}
/**
* 缓存管理器
*
* @param connectionFactory
* @return
*/
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManager.create(connectionFactory);
}
@Bean
public RedisTemplate<String, Serializable> redisTemplate(JedisConnectionFactory connectionFactory) {
RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(jedisConnectionFactory(jedisPoolConfig()));
return redisTemplate;
}
}
2.2.4 测试
@Autowired
private RedisTemplate<String,String> redisTemplate;//这里要注意泛型的类型
@GetMapping("getMsg")
public Result<String> getMsg(){
redisTemplate.opsForValue().set("k2","v2");
String k = redisTemplate.opsForValue().get("k2");
System.out.println(k);//输出v2
//{"code":0,"msg":"操作成功","data":"v2"}
return new Result<String>().ok(k);
}
2.3 Redission
2.3.1 添加依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.6</version>
</dependency>
2.3.2 添加配置类
package com.learn.tm.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
// 配置信息
Config config = new Config();
//地址、密码
// config.useSingleServer().setAddress("redis://192.168.198.100:6379").setPassword("");
config.useSingleServer().setAddress("redis://192.168.198.100:6379");
return Redisson.create(config);
}
}
2.3.3 测试类
首先调用getMsg方法,获取锁成功,然后休眠5s,在此期间,调用getMsg1方法,发现卡住了,等到getMsg方法执行完毕后,getMsg1方法获取锁继续执行。
@Resource
private RedissonClient redissonClient;
@GetMapping("getMsg")
public Result<String> getMsg() throws InterruptedException {
//使用 Redission 获取锁对象
RLock lock = redissonClient.getLock("k");
//尝试获取锁
boolean isLock = lock.tryLock(10, TimeUnit.MINUTES);
if(!isLock){
return new Result<String>().error("获取锁失败");
}
try{
//获取锁成功->执行业务代码
System.out.println("获取锁成功->执行业务代码");
Thread.sleep(5000);
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
return new Result<String>().ok();
}
@GetMapping("getMsg1")
public Result<String> getMsg1() throws InterruptedException {
//使用 Redission 获取锁对象
RLock lock = redissonClient.getLock("k");
//尝试获取锁
boolean isLock = lock.tryLock(5, TimeUnit.MINUTES);
if(!isLock){
return new Result<String>().error("获取锁1失败");
}
try{
//获取锁成功->执行业务代码
System.out.println("获取锁1成功->执行业务代码");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
return new Result<String>().ok();
}
结果日志如下:
获取锁成功->执行业务代码
获取锁1成功->执行业务代码
3. SpringBoot 整合 Spring Cache + Redis
@Cacheable
用的最多的注解,功能也很强大,一般用在查询方法上。
可以根据方法对其返回结果进行缓存,下次请求时,如果缓存存在,则直接读取缓存数据返回;如果缓存不存在,则执行方法,并把返回的结果存入缓存中。
说白了,比如我第一次查询缓存操作,如果不存在就去查询数据库,查到数据后返回,并且放到缓存里去,当第二次查询的时候就可以查询缓存里的数据。
查看源码,属性值如下:
value:缓存名,必填,它指定了你的缓存存放在哪块命名空间cacheNames:与 value 差不多,二选一即可key:可选属性,可以使用 SpEL 标签自定义缓存的key
@CachePut
一般用在新增方法上。
使用该注解标志的方法,每次都会执行,并将结果存入指定的缓存中。其他方法可以直接从响应的缓存中读取缓存数据,而不需要再去查询数据库。
查看源码,属性值如下:
value:缓存名,必填,它指定了你的缓存存放在哪块命名空间cacheNames:与 value 差不多,二选一即可key:可选属性,可以使用 SpEL 标签自定义缓存的key
@CacheEvict
一般用在更新或者删除方法上,使用该注解标志的方法,会清空指定的缓存。
查看源码,属性值如下:
value:缓存名,必填,它指定了你的缓存存放在哪块命名空间cacheNames:与 value 差不多,二选一即可key:可选属性,可以使用 SpEL 标签自定义缓存的keyallEntries:是否清空所有缓存,默认为 false。如果指定为 true,则方法调用后将立即清空所有的缓存beforeInvocation:是否在方法执行前就清空,默认为 false。如果指定为 true,则在方法执行前就会清空缓存
(1) @CacheConfig:是类级别的注解,统一类的所有缓存key前缀;@CacheConfig(cacheNames = { "user" })代表了该类的所有缓存key值都是"user::"为前缀。第二种方式@CacheConfig(cacheNames = "c1")则意味着从配置文件里取 spring.cache.cache-names: c1对应的c1值
(2) @Cacheable(key="#id"):方法级别的注解,可以将运行结果缓存,以后查询相同的数据,直接从缓存中取,不需要调用方法;id的值作为key。
方法被调用时,先从缓存中读取数据,如果缓存没有找到数据,再执行方法体,最后把返回值添加到缓存中。 调用方法传入id=100,那redis对应的key=user::100 ,value通过采用GenericJackson2JsonRedisSerializer序列化为json
(3) @CachePut(key = "#obj.id"):方法级别的注解,用于更新缓存。
方法被调用时,先执行方法体,然后springcache通过返回值更新缓存,即key = "#obj.id",value=User
(4)@CacheEvict(key = "#id"):方法级别的注解,用于删除缓存。
方法被调用时,先执行方法体,在通过方法参数删除缓存。
注意 :
(1) 对于redis的缓存,SpringCache只支持String,其他的Hash 、List、Set、ZSet都不支持,所以对于Hash 、List、set、ZSet只能用RedisTemplate。
(2) 对于多表查询的数据缓存,SpringCache是不支持的,只支持单表的简单缓存。对于多表的整体缓存,只能用RedisTemplate。
3.1 添加配置文件
spring:
redis:
# Redis数据库索引(默认为0)
database: 0
# Redis服务器地址(根据业务对应修改)
host: 192.168.198.100
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
# password: 123456
# 连接超时时间(毫秒)
timeout: 1000
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池最大连接数(使用负值表示没有限制)
max-active: 200
# 连接池中的最大空闲连接
max-idle: 10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1
cache:
cache-names: c1
3.2 添加依赖
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--spring cache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--spring cache连接池依赖包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.2</version>
</dependency>
3.3 添加配置类
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
3.4 测试类
这里要注意 : 实体类需要实现Serializable,否则可能会有报错。
import com.learn.tm.entity.User;
import com.learn.tm.exception.Result;
import com.learn.tm.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
@Slf4j
@RestController
@RequestMapping("/tm")
@Api(tags = "用户测试类")
@CacheConfig(cacheNames = {"user"})
//@CacheConfig(cacheNames = "c1")
public class TestController {
@Autowired
private UserService userService;
@GetMapping("findUserById")
@Cacheable(key="#id")
@ApiOperation(value = "Cacheable注解测试",notes = "")
public Result<User> findUserById(Integer id){
return new Result<User>().ok(userService.getById(id));
}
@PutMapping("updateUser")
@CachePut(key="#user.id")
@ApiOperation(value = "CachePut注解测试",notes = "")
public Result<User> updateUser(@RequestBody User user){
userService.updateById(user);
return new Result<User>().ok(userService.getById(user.getId()));
}
@DeleteMapping("deleteUser")
@CacheEvict(key="#id")
@ApiOperation(value = "CacheEvict注解测试",notes = "")
public Result<User> deleteUser(Integer id){
userService.removeById(id);
return new Result<User>().ok(userService.getById(id));
}
}
@Data
@TableName("user")
@ApiModel
public class User implements Serializable {
@ApiModelProperty(position = 0,value = "用户ID")
@TableId(value = "id",type = IdType.AUTO)
private Long id;
@ApiModelProperty(position = 1,value = "用户姓名")
@TableField("name")
private String name;
@ApiModelProperty(position = 2,value = "用户年龄")
private Long age;
@ApiModelProperty(position = 3,value = "用户电话号")
private String tel;
@ApiModelProperty(position = 4,value = "用户性别")
private Long sex;
@ApiModelProperty(position = 5,value = "用户地址")
private String address;
@ApiModelProperty(position = 6,value = "用户邮箱")
private String email;
@ApiModelProperty(position = 7,value = "用户生日")
private Date birthday;
}
启动类需要加上@EnableCaching注解
3.5 效果
首先调用findUserById方法,第一次进入断点,第二次不会进入,使用工具查看redis,可以看到成功缓存,对应的key就是@CacheConfig(cacheNames = {"user"})里配置的user加上两个冒号::再加上@Cacheable(key="#id")对应的参数id值
更新这里就不强调了,对于删除来说,执行完方法后,可以看到redis中对应的缓存也消失了。
3.6 注意
如果通过ID查询出来的结果是null,默认是还会缓存的,这样其实就会出现问题了。处理的办法就是注解里加上unless="#result == null"配置。
但是这里还要注意一点,就是下面方法1其实还是会缓存的,方法二不会,这是因为方法二userService.getById(id)结果是null 而方法一结果是{"code": 0,"msg": "操作成功","data": null}。所以可以看出,缓存实际上是根据方法的返回值来判断的。所以我们在使用对应注解的方法上,尽量让返回值是我们要缓存的内容,而不是经过包装后的内容。
# 方法1:
@GetMapping("findUserById")
@Cacheable(key="#id",unless="#result == null")
@ApiOperation(value = "Cacheable注解测试",notes = "")
public Result<User> findUserById(Integer id){
return new Result<User>().ok(userService.getById(id));
}
# 方法2:
@GetMapping("findUserById")
@Cacheable(key="#id",unless="#result == null")
@ApiOperation(value = "Cacheable注解测试",notes = "")
public User findUserById(Integer id){
return userService.getById(id);
}
4. SpringBoot 整合 Spring Cache + Ehcache
4.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!--spring cache连接池依赖包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
</dependency>
4.2 新增ehcache.xml配置文件
路径为 : src/main/resources/ehcache.xml 默认 spring boot 就会在 resources 目录下扫描到 ehcache 所以也不需要在 application.yml 中单独配置。
默认情况下,这个文件名是固定的,必须叫 ehcache.xml ,如果一定要换一个名字,那么需要在 application.yml 中明确指定配置文件名,配置方式如下:spring.cache.ehcache.config=classpath:aaa.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://www.ehcache.org/ehcache.xsd"
updateCheck="false">
<!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir" />
<!-- 默认缓存 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<!-- 测试 -->
<cache name="user"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="true"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="600"/>
</ehcache>
参数配置含义:
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds 当缓存闲置n秒后销毁.仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds 当缓存存活n秒后销毁.最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskPersistent:是否缓存虚拟机重启期数据
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
4.3 启动类添加注解
启动类上添加注解 @EnableCaching
4.4 测试类
这里要注意 :
(1) @CacheConfig(cacheNames = {"user"})里的user要与ehcache.xml里配置的保持一致
(2) 如果没有在类上配置@CacheConfig(cacheNames = {"user"}),那么需要在每个方法上的缓存注解里添加value属性指定缓存名称,比如 @Cacheable(value="user",key="#id",unless="#result == null")。其他用法与cache + redis组合一样
@Slf4j
@RestController
@RequestMapping("/tm")
@Api(tags = "用户测试类")
@CacheConfig(cacheNames = {"user"})
public class TestController {
@Autowired
private UserService userService;
@GetMapping("findUserById")
@Cacheable(key="#id",unless="#result == null")
@ApiOperation(value = "Cacheable注解测试",notes = "")
public Result<User> findUserById(Integer id){
return new Result<User>().ok(userService.getById(id));
}
@PutMapping("updateUser")
@CachePut(key="#user.id")
@ApiOperation(value = "CachePut注解测试",notes = "")
public Result<User> updateUser(@RequestBody User user){
userService.updateById(user);
return new Result<User>().ok(userService.getById(user.getId()));
}
@DeleteMapping("deleteUser")
@CacheEvict(key="#id")
@ApiOperation(value = "CacheEvict注解测试",notes = "")
public Result<User> deleteUser(Integer id){
userService.removeById(id);
return new Result<User>().ok(userService.getById(id));
}
5. SpringBoot 整合 swagger2
5.1 添加依赖
这里要注意:一定要保证springboot版本不能太高,版本太高,缺失相应的依赖,启动会报错。
Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
目前确定的是2.5.3可以,2.6.x,2.7.x都不可以
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
5.2 添加配置文件
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.pathMapping("/")
.select()
.apis(RequestHandlerSelectors.basePackage("com.learn.tm.controller"))
.paths(PathSelectors.any())
.build().apiInfo(new ApiInfoBuilder()
.title("SpringBoot整合Swagger2测试")
.description("SpringBoot整合Swagger,详细信息......")
.version("1.0")
.contact(new Contact("xxx系统","www.xxx.com","tm@gmail.com"))
.license("")
.licenseUrl("")
.build());
}
}
5.3 测试类
浏览器访问:http://localhost:8080/swagger-ui.html
@Data
@TableName("user")
@ApiModel
public class User {
@ApiModelProperty(position = 0,value = "用户ID")
@TableId(value = "id",type = IdType.AUTO)
private Long id;
@ApiModelProperty(position = 1,value = "用户姓名")
@TableField("name")
private String name;
@ApiModelProperty(position = 2,value = "用户年龄")
private Long age;
@ApiModelProperty(position = 3,value = "用户电话号")
private String tel;
@ApiModelProperty(position = 4,value = "用户性别")
private Long sex;
@ApiModelProperty(position = 5,value = "用户地址")
private String address;
@ApiModelProperty(position = 6,value = "用户邮箱")
private String email;
@ApiModelProperty(position = 7,value = "用户生日")
private Date birthday;
}
@Slf4j
@RestController
@RequestMapping("/tm")
@Api(tags = "用户测试类")
public class TestController {
@ApiOperation(value = "GET无参方法测试",notes = "")
@GetMapping("getMsg")
public Result<String> getMsg(){
return new Result<String>().ok();
}
@GetMapping("getParamMsg")
@ApiOperation(value = "GET有参方法测试",notes = "根据姓名和电话号确认用户信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "name",value = "用户名",required = true,dataType = "String"),
@ApiImplicitParam(name = "tel",value = "电话号",required = true,dataType = "Long")
})
public Result<String> getParamMsg(String name,Long tel){
return new Result<String>().ok(name+tel);
}
@PostMapping("postMsg")
@ApiOperation(value = "POST方法测试",notes = "处理用户信息")
@ApiImplicitParam(name = "user",value = "用户实体类user",required = true,dataType = "User")
public Result<String> postMsg(@RequestBody User user){
return new Result<String>().ok();
}
}
5.4 效果
接口1:
接口2:
接口3:
6. SpringBoot 整合 Knife4j
6.1 添加依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
6.2 添加配置类
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2
@EnableKnife4j
public class Knife4jConfiguration {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.learn.tm.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.description("Kinfe4j 集成测试文档")
.contact(new Contact("TM", "https://juejin.cn/user/3167065128057870", "xxx@qq.com"))
.version("v1.1.0")
.title("API测试文档")
.build();
}
}
6.3 测试类
浏览器访问:http://localhost:8080/doc.html#/
@Data
@TableName("user")
@ApiModel
public class User {
@ApiModelProperty(position = 0,value = "用户ID")
@TableId(value = "id",type = IdType.AUTO)
private Long id;
@ApiModelProperty(position = 1,value = "用户姓名")
@TableField("name")
private String name;
@ApiModelProperty(position = 2,value = "用户年龄")
private Long age;
@ApiModelProperty(position = 3,value = "用户电话号")
private String tel;
@ApiModelProperty(position = 4,value = "用户性别")
private Long sex;
@ApiModelProperty(position = 5,value = "用户地址")
private String address;
@ApiModelProperty(position = 6,value = "用户邮箱")
private String email;
@ApiModelProperty(position = 7,value = "用户生日")
private Date birthday;
}
@Slf4j
@RestController
@RequestMapping("/tm")
@Api(tags = "用户测试类")
public class TestController {
@ApiOperation(value = "GET无参方法测试",notes = "")
@GetMapping("getMsg")
public Result<String> getMsg(){
return new Result<String>().ok();
}
@GetMapping("getParamMsg")
@ApiOperation(value = "GET有参方法测试",notes = "根据姓名和电话号确认用户信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "name",value = "用户名",required = true,dataType = "String"),
@ApiImplicitParam(name = "tel",value = "电话号",required = true,dataType = "Long")
})
public Result<String> getParamMsg(@RequestParam(value = "name") String name,@RequestParam(value = "tel") Long tel){
return new Result<String>().ok();
}
@PostMapping("postMsg")
@ApiOperation(value = "POST方法测试",notes = "处理用户信息")
@ApiImplicitParam(name = "user",value = "用户实体类user",required = true,dataType = "User")
public Result<String> postMsg(@RequestBody User user){
return new Result<String>().ok();
}
}
6.4 效果
实体类:
接口:
7. SpringBoot 启动后执行方法
7.1 注解@PostConstruct
使用注解@PostConstruct是最常见的一种方式,存在的问题是如果执行的方法耗时过长,会导致项目在方法执行期间无法提供服务。
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class ApplicationStartExecute {
@PostConstruct
public void init(){
//这里如果方法执行过长会导致项目一直无法提供服务
System.out.println("=======PostConstruct=======");
}
}
7.2 实现CommandLineRunner接口
实现CommandLineRunner接口 然后在run方法里面调用需要调用的方法即可,好处是方法执行时,项目已经初始化完毕,是可以正常提供服务的。
同时该方法也可以接受参数,可以根据项目启动时: java -jar demo.jar arg1 arg2 arg3 传入的参数进行一些处理。
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class CommandLineRunnerImpl implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("=======CommandLineRunner=======");
}
}
7.3 实现ApplicationRunner接口
实现ApplicationRunner接口和实现CommandLineRunner接口基本是一样的。
唯一的不同是启动时传参的格式,CommandLineRunner对于参数格式没有任何限制,ApplicationRunner接口参数格式必须是:–key=value
import java.util.Set;
import java.util.List;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class ApplicationRunnerImpl implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
Set<String> optionNames = args.getOptionNames();
for (String optionName : optionNames) {
List<String> values = args.getOptionValues(optionName);
System.out.println(values.toString());
}
System.out.println("=======ApplicationRunner=======");
}
}
7.4 实现ApplicationListener
实现接口ApplicationListener方式和实现ApplicationRunner,CommandLineRunner接口都不影响服务,都可以正常提供服务,注意监听的事件,通常是ApplicationStartedEvent 或者ApplicationReadyEvent,其他的事件可能无法注入bean。
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class ApplicationListenerStartedImpl implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("=======ApplicationListenerStartedImpl=======");
}
}
-----------------------------------------------------------------------------------------------
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class ApplicationListenerReadyImpl implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("=======ApplicationListenerReadyImpl=======");
}
}
7.5 四种方式的执行顺序
通过下面打印的日志可以看出
(1) 注解方式@PostConstruct 始终最先执行
(2) 如果监听的是ApplicationStartedEvent 事件,则一定会在CommandLineRunner和ApplicationRunner 之前执行。如果监听的是ApplicationReadyEvent 事件,则一定会在CommandLineRunner和ApplicationRunner 之后执行。
(3) CommandLineRunner和ApplicationRunner 默认是ApplicationRunner先执行,如果双方指定了@Order 则按照@Order的大小顺序执行,大的先执行。
......
2023-01-04 14:12:05.867 [main] INFO org.apache.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.50]
2023-01-04 14:12:06.149 [main] INFO o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
2023-01-04 14:12:06.150 [main] INFO o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1498 ms
=======PostConstruct=======
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
2023-01-04 14:12:06.381 [main] INFO c.a.d.s.b.a.DruidDataSourceAutoConfigure - Init DruidDataSource
2023-01-04 14:12:06.494 [main] INFO com.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
Property 'mapperLocations' was not specified.
_ _ |_ _ _|_. ___ _ | _
| | |\/|_)(_| | |_\ |_)||_|_\
/ |
3.4.0
2023-01-04 14:12:07.662 [main] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
2023-01-04 14:12:07.677 [main] INFO o.s.boot.web.embedded.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
2023-01-04 14:12:07.797 [main] WARN s.d.s.r.operation.OperationImplicitParameterReader - Unable to interpret the implicit parameter configuration with dataType: String, dataTypeClass: class java.lang.Void
2023-01-04 14:12:07.801 [main] WARN s.d.s.r.operation.OperationImplicitParameterReader - Unable to interpret the implicit parameter configuration with dataType: Long, dataTypeClass: class java.lang.Void
2023-01-04 14:12:07.819 [main] WARN s.d.s.r.operation.OperationImplicitParameterReader - Unable to interpret the implicit parameter configuration with dataType: User, dataTypeClass: class java.lang.Void
2023-01-04 14:12:07.842 [main] INFO com.learn.tm.TianmengApplication - Started TianmengApplication in 3.818 seconds (JVM running for 4.842)
=======ApplicationListenerStartedImpl=======
=======ApplicationRunner=======
=======CommandLineRunner=======
=======ApplicationListenerReadyImpl=======
正常是ApplicationRunner优先于CommandLineRunner执行,但是如果在这两个类上添加order注解,如下:
@Order(1)
@Component
public class CommandLineRunnerImpl implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("=======CommandLineRunner=======");
}
}
@Order(2)
@Component
public class ApplicationRunnerImpl implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("=======ApplicationRunner=======");
}
}
则最后结果为 : CommandLineRunner优先于ApplicationRunner ,即@Order注解后的参数值越小,优先级越高
=======PostConstruct=======
=======ApplicationListenerStartedImpl=======
=======CommandLineRunner=======
=======ApplicationRunner=======
=======ApplicationListenerReadyImpl=======
8. SpringBoot 整合 ElasticSearch
8.1 添加依赖
<!-- https://mvnrepository.com/artifact/org.elasticsearch/elasticsearch -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.8.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.8.0</version>
</dependency>
8.2 添加配置类
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticSearchConfig {
@Bean
public RestHighLevelClient restHighLevelClient (){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("192.168.198.100",9200,"http")));
return client;
}
}
8.3 添加配置信息
spring:
elasticsearch:
rest:
#username: user
#password: 123456
uris: https://192.168.198.100:9200
# http连接超时时间
connection-timeout: 1000
# socket连接超时时间
socketTimeout: 30000
# 获取连接的超时时间
connectionRequestTimeout: 500
# 最大连接数
maxConnTotal: 100
# 最大路由连接数
maxConnPerRoute: 100
# 任务最长可执行时间 (单位:小时)
executeTimeout: 8
8.4 测试类
@GetMapping("/getAllIndex")
@ApiOperation(value = "elasticsearch测试",notes = "")
public void getAllIndex() throws IOException {
//查看所有索引信息
GetIndexRequest request = new GetIndexRequest("*");
GetIndexResponse response = client.indices().get(request, RequestOptions.DEFAULT);
String[] indices = response.getIndices();
System.out.println(indices.toString());
}