SpringBoot整合(一)SpringBoot整合Redis、ES7.6、Quartz

SpringBoot 整合Redis、ES7.6、Quartz

一、SpringBoot整合Redis

不熟悉docker的可以参考:
容器虚拟化技术Docker(一)简介、安装、常见命令、数据卷、安装常规软件

1.1 docker安装redis6.0.8

# 1、从docker hub上(阿里云加速器)拉取redis镜像到本地标签为6.0.8
docker pull redis:6.0.8


# 2、将一个redis.conf文件模板拷贝进/app/redis目录下
mkdir -p /app/redis

# 3 /app/redis目录下修改redis.conf文件
# 3.1 开启redis验证    可选
requirepass 123
 
# 3.2 允许redis外地连接  必须
注释掉 # bind 127.0.0.1
# 3.3   daemonize no
将daemonize yes注释起来或者 daemonize no设置,因为该配置和docker run中-d参数冲突,会导致容器一直启动失败   
# 开启redis数据持久化  appendonly yes  可选
     
     
     
# 4、使用redis6.0.8镜像创建容器(也叫运行镜像)
docker run  -p 6379:6379 
--name myr3 --privileged=true 
-v /app/redis/redis.conf:/etc/redis/redis.conf 
-v /app/redis/data:/data 
-d redis:6.0.8 redis-server /etc/redis/redis.conf

1.2 整合redis

使用springboot技术去管理其他技术,几个问题是躲不掉的。

​ 第一,需要先导入对应技术的坐标,而整合之后,这些坐标都有了一些变化

​ 第二,任何技术通常都会有一些相关的设置信息,整合之后,这些信息如何写,写在哪是一个问题

​ 第三,没有整合之前操作如果是模式A的话,整合之后如果没有给开发者带来一些便捷操作,那整合将毫无意义,所以整合后操作肯定要简化一些,那对应的操作方式自然也有所不同

步骤①:导入springboot整合redis的starter坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

步骤②:进行基础配置

spring:
  redis:
    host: 192.168.42.104
    port: 6379

​ 操作redis,最基本的信息就是操作哪一台redis服务器,所以服务器地址属于基础配置信息,不可缺少。但是即便你不配置,目前也是可以用的。因为以上两组信息都有默认配置,刚好就是上述配置值。

步骤③:使用springboot整合redis的专用客户端接口操作,此处使用的是RedisTemplate

package com.yyds;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

@SpringBootTest
public class RedisTests {


    @Autowired
    private RedisTemplate redisTemplate;


    @Test
    void set() {
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("age",41);
    }

    @Test
    void get() {
        ValueOperations ops = redisTemplate.opsForValue();
        Object age = ops.get("age");
        System.out.println(age);
    }

    @Test
    void hset() {
        HashOperations ops = redisTemplate.opsForHash();
        ops.put("info","b","bb");
    }
    @Test
    void hget() {
        HashOperations ops = redisTemplate.opsForHash();
        Object val = ops.get("info", "b");
        System.out.println(val);
    }

}

​ 在操作redis时,需要先确认操作何种数据,根据数据种类得到操作接口。例如使用opsForValue()获取string类型的数据操作接口,使用opsForHash()获取hash类型的数据操作接口,剩下的就是调用对应api操作了。各种类型的数据操作接口如下:

在这里插入图片描述

StringRedisTemplate

​ 由于redis内部不提供java对象的存储格式,因此当操作的数据以对象的形式存在时,会进行转码,转换成字符串格式后进行操作。为了方便开发者使用基于字符串为数据的操作,springboot整合redis时提供了专用的API接口StringRedisTemplate,你可以理解为这是RedisTemplate的一种指定数据泛型的操作API。

package com.yyds;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

@SpringBootTest(classes = BootHotDeployApplication.class)
public class StringRedisTemplateTest {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void set(){
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        ops.set("name","66");
    }

    @Test
    void get(){
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        String name = ops.get("name");
        System.out.println(name);
    }
}

redis客户端选择

springboot整合redis技术提供了多种客户端兼容模式,默认提供的是lettucs客户端技术,也可以根据需要切换成指定客户端技术,例如jedis客户端技术,切换成jedis客户端技术操作步骤如下:

步骤①:导入jedis坐标

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

​ jedis坐标受springboot管理,无需提供版本号

步骤②:配置客户端技术类型,设置为jedis

spring:
  redis:
    host: 192.168.42.104
    port: 6379
    client-type: jedis

步骤③:根据需要设置对应的配置

spring:
  redis:
    host: 192.168.42.104
    port: 6379
    client-type: jedis
    lettuce:
      pool:
        max-active: 16
    jedis:
      pool:
        max-active: 16

lettcus与jedis区别

  • jedis连接Redis服务器是直连模式,当多线程模式下使用jedis会存在线程安全问题,解决方案可以通过配置连接池使每个连接专用,这样整体性能就大受影响
  • lettcus基于Netty框架进行与Redis服务器连接,底层设计中采用StatefulRedisConnection。 StatefulRedisConnection自身是线程安全的,可以保障并发访问安全问题,所以一个连接可以被多线程复用。当然lettcus也支持多连接实例一起工作

二、SpringBoot整合ES7.6

2.1 docker安装es7.6以及ik分词器

IK分词器下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases

分词器下载后解压到ES安装目录的plugins目录中即可(注意ik分词器的版本需要与es版本一致),安装分词器后需要重新启动ES服务器

# 拉取elasticsearch:7.6.2
[root@centos04 ~]# docker pull elasticsearch:7.6.2


[root@centos04 ~]# mkdir -p /app/elasticsearch
[root@centos04 ~]# cd /app/elasticsearch
[root@centos04 elasticsearch]# mkdir data
[root@centos04 elasticsearch]# mkdir logs
# 将ik分词器解压放到此目录下
[root@centos04 elasticsearch]# mkdir plugins
[root@centos04 plugins]# ll
total 0
drwxrwxrwx. 3 root root 243 Feb  6 17:25 elasticsearch-analysis-ik-7.6.2


# 给予权限
[root@centos04 app]# chmod -R 777 ../elasticsearch

# 节点探测类型(discovery.type)设置为 single-node 表示不开启集群模式。
[root@centos04 app]# docker run -d --name=es7.6 --restart=always -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-v /app/elasticsearch/data:/usr/share/elasticsearch/data \
-v /app/elasticsearch/logs:/usr/share/elasticsearch/logs \
-v /app/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
elasticsearch:7.6.2


[root@centos04 plugins]# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED      STATUS          PORTS                                                                                  NAMES
33f34d6842b0   elasticsearch:7.6.2   "/usr/local/bin/dock…"   2 days ago   Up 54 seconds   0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 0.0.0.0:9300->9300/tcp, :::9300->9300/tcp   es7.6

浏览器访问:  http://centos04:9200/
# 得到下面信息说明安装成功
{
  "name" : "cfce85122643",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "gcm8jugjRz2VKX-sY9voLg",
  "version" : {
    "number" : "7.6.2",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
    "build_date" : "2020-03-26T06:34:37.794943Z",
    "build_snapshot" : false,
    "lucene_version" : "8.4.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

2.2 基本操作

  • 创建索引,books是索引名称,下同

    PUT请求		http://centos04:9200/books
    

    发送请求后,看到如下信息即索引创建成功

    {
        "acknowledged": true,
        "shards_acknowledged": true,
        "index": "books"
    }
    

    重复创建已经存在的索引会出现错误信息,reason属性中描述错误原因

    {
        "error": {
            "root_cause": [
                {
                    "type": "resource_already_exists_exception",
                    "reason": "index [books/VgC_XMVAQmedaiBNSgO2-w] already exists",
                    "index_uuid": "VgC_XMVAQmedaiBNSgO2-w",
                    "index": "books"
                }
            ],
            "type": "resource_already_exists_exception",
            "reason": "index [books/VgC_XMVAQmedaiBNSgO2-w] already exists",	# books索引已经存在
            "index_uuid": "VgC_XMVAQmedaiBNSgO2-w",
            "index": "book"
        },
        "status": 400
    }
    
  • 查询索引

    GET请求		http://centos04:9200/books
    

    查询索引得到索引相关信息,如下

    {
        "book": {
            "aliases": {},
            "mappings": {},
            "settings": {
                "index": {
                    "routing": {
                        "allocation": {
                            "include": {
                                "_tier_preference": "data_content"
                            }
                        }
                    },
                    "number_of_shards": "1",
                    "provided_name": "books",
                    "creation_date": "1645768584849",
                    "number_of_replicas": "1",
                    "uuid": "VgC_XMVAQmedaiBNSgO2-w",
                    "version": {
                        "created": "7160299"
                    }
                }
            }
        }
    }
    

    如果查询了不存在的索引,会返回错误信息,例如查询名称为book的索引后信息如下

    {
        "error": {
            "root_cause": [
                {
                    "type": "index_not_found_exception",
                    "reason": "no such index [book]",
                    "resource.type": "index_or_alias",
                    "resource.id": "book",
                    "index_uuid": "_na_",
                    "index": "book"
                }
            ],
            "type": "index_not_found_exception",
            "reason": "no such index [book]",		# 没有book索引
            "resource.type": "index_or_alias",
            "resource.id": "book",
            "index_uuid": "_na_",
            "index": "book"
        },
        "status": 404
    }
    
  • 删除索引

    DELETE请求	http://centos04:9200/books
    

    删除所有后,给出删除结果

    {
        "acknowledged": true
    }
    

    如果重复删除,会给出错误信息,同样在reason属性中描述具体的错误原因

    {
        "error": {
            "root_cause": [
                {
                    "type": "index_not_found_exception",
                    "reason": "no such index [books]",
                    "resource.type": "index_or_alias",
                    "resource.id": "book",
                    "index_uuid": "_na_",
                    "index": "book"
                }
            ],
            "type": "index_not_found_exception",
            "reason": "no such index [books]",		# 没有books索引
            "resource.type": "index_or_alias",
            "resource.id": "book",
            "index_uuid": "_na_",
            "index": "book"
        },
        "status": 404
    }
    
  • 创建索引并指定分词器

    PUT请求		http://centos04:9200/books
    
    
    {
        "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"
                }
            }
        }
    }
    

目前我们已经有了索引了,但是索引中还没有数据,所以要先添加数据,ES中称数据为文档,下面进行文档操作。

  • 添加文档,有三种方式

    POST请求	http://centos04:9200/books/_doc		#使用系统生成id
    POST请求	http://centos04:9200/books/_create/1	#使用指定id
    POST请求	http://centos04:9200/books/_doc/1		#使用指定id,不存在创建,存在更新(版本递增)
    
    文档通过请求参数传递,数据格式json
    {
        "name":"springboot",
        "type":"springboot",
        "description":"springboot"
    }  
    
  • 查询文档

    GET请求	http://centos04:9200/books/_doc/1		 #查询单个文档 		
    GET请求	http://centos04:9200/books/_search		 #查询全部文档
    
  • 条件查询

    GET请求	http://centos04:9200/books/_search?q=name:springboot	# q=查询属性名:查询属性值
    
  • 删除文档

    DELETE请求	http://centos04:9200/books/_doc/1
    
  • 修改文档(全量更新)

    PUT请求	http://centos04:9200/books/_doc/1
    
    文档通过请求参数传递,数据格式json
    {
        "name":"springboot",
        "type":"springboot",
        "description":"springboot"
    }
    
  • 修改文档(部分更新)

    POST请求	http://centos04:9200/books/_update/1
    
    文档通过请求参数传递,数据格式json
    {			
        "doc":{						#部分更新并不是对原始文档进行更新,而是对原始文档对象中的doc属性中的指定属性更新
            "name":"springboot"		#仅更新提供的属性值,未提供的属性值不参与更新操作
        }
    }
    

2.3 整合ES7.6

步骤①:导入springboot整合ES的starter坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

步骤②:进行基础配置

spring:
  elasticsearch:
    rest:
      uris: http://centos04:9200

​ 配置ES服务器地址,端口9200

步骤③:使用springboot整合ES的专用客户端接口ElasticsearchRestTemplate来进行操作

@SpringBootTest
class ElasticsearchTests {
    @Autowired
    private ElasticsearchRestTemplate template;
}

​ 上述操作形式是ES早期的操作方式,使用的客户端被称为Low Level Client,这种客户端操作方式性能方面略显不足,于是ES开发了全新的客户端操作方式,称为High Level Client。高级别客户端与ES版本同步更新,但是springboot最初整合ES的时候使用的是低级别客户端,所以企业开发需要更换成高级别的客户端模式。

​ 下面使用高级别客户端方式进行springboot整合ES,操作步骤如下:

步骤①:导入springboot整合ES高级别客户端的坐标,此种形式目前没有对应的starter

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

步骤②:使用编程的形式设置连接的ES服务器,并获取客户端对象

@SpringBootTest
class ElasticsearchTests {
    private RestHighLevelClient client;
      @Test
      void testCreateClient() throws IOException {
          HttpHost host = HttpHost.create("http://192.168.42.104:9200");
          RestClientBuilder builder = RestClient.builder(host);
          client = new RestHighLevelClient(builder);
  
          client.close();
      }
}

​ 配置ES服务器地址与端口9200,记得客户端使用完毕需要手工关闭。由于当前客户端是手工维护的,因此不能通过自动装配的形式加载对象。

步骤③:使用客户端对象操作ES,例如创建索引

@SpringBootTest
class ElasticsearchTests {
    private RestHighLevelClient client;
      
    
    @Test
    void testCreateIndex() throws IOException {
        HttpHost host = HttpHost.create("http://192.168.42.104:9200");
        RestClientBuilder builder = RestClient.builder(host);
        client = new RestHighLevelClient(builder);

        CreateIndexRequest request = new CreateIndexRequest("it-books");
        client.indices().create(request, RequestOptions.DEFAULT);

        client.close();
    }
}

​ 高级别客户端操作是通过发送请求的方式完成所有操作的,ES针对各种不同的操作,设定了各式各样的请求对象,上例中创建索引的对象是CreateIndexRequest,其他操作也会有自己专用的Request对象。

​ 当前操作我们发现,无论进行ES何种操作,第一步永远是获取RestHighLevelClient对象,最后一步永远是关闭该对象的连接。在测试中可以使用测试类的特性去帮助开发者一次性的完成上述操作,但是在业务书写时,还需要自行管理。将上述代码格式转换成使用测试类的初始化方法和销毁方法进行客户端对象的维护。

@SpringBootTest
class ElasticsearchTests {
    //在测试类中每个操作运行前运行的方法
    @BeforeEach
    void setUp() {
        HttpHost host = HttpHost.create("http://192.168.42.104:9200");
        RestClientBuilder builder = RestClient.builder(host);
        client = new RestHighLevelClient(builder);
    }

    //在测试类中每个操作运行后运行的方法
    @AfterEach
    void tearDown() throws IOException {
        client.close();
    }

    private RestHighLevelClient client;

    @Test
    void testCreateIndex() throws IOException {
        CreateIndexRequest request = new CreateIndexRequest("it-books");
        client.indices().create(request, RequestOptions.DEFAULT);
    }
}

现在的书写简化了很多,也更合理,下面测试一些基本操作。

package com.yyds;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yyds.dao.BookDao;
import com.yyds.domain.Book;
import org.apache.http.HttpHost;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.util.List;

@SpringBootTest(classes = BootHotDeployApplication.class)
public class ElasticsearchTests {


//    @Autowired
//    private ElasticsearchRestTemplate template;


    private RestHighLevelClient client;


    //在测试类中每个操作运行前运行的方法
    @BeforeEach
    void setUp() {
        HttpHost host = HttpHost.create("http://192.168.42.104:9200");
        RestClientBuilder builder = RestClient.builder(host);
        client = new RestHighLevelClient(builder);
    }

    //在测试类中每个操作运行后运行的方法
    @AfterEach
    void tearDown() throws IOException {
        client.close();
    }


    @Test
    void testCreateIndex() throws IOException {
        CreateIndexRequest request = new CreateIndexRequest("it-books");
        client.indices().create(request, RequestOptions.DEFAULT);
    }


    @Test
    void testCreateIndexWithMapping() throws IOException {
        CreateIndexRequest request = new CreateIndexRequest("it-books");


        String json = "{\n" +
                "    \"mappings\":{\n" +
                "        \"properties\":{\n" +
                "            \"id\":{\n" +
                "                \"type\":\"keyword\"\n" +
                "            },\n" +
                "            \"name\":{\n" +
                "                \"type\":\"text\",\n" +
                "                \"analyzer\":\"ik_max_word\",\n" +
                "                \"copy_to\":\"all\"\n" +
                "            },\n" +
                "            \"type\":{\n" +
                "                \"type\":\"keyword\"\n" +
                "            },\n" +
                "            \"description\":{\n" +
                "                \"type\":\"text\",\n" +
                "                \"analyzer\":\"ik_max_word\",\n" +
                "                \"copy_to\":\"all\"\n" +
                "            },\n" +
                "            \"all\":{\n" +
                "                \"type\":\"text\",\n" +
                "                \"analyzer\":\"ik_max_word\"\n" +
                "            }\n" +
                "        }\n" +
                "    }\n" +
                "}";


        //设置请求中的参数
        request.source(json, XContentType.JSON);
        client.indices().create(request, RequestOptions.DEFAULT);
    }


    @Autowired
    private BookDao bookDao;

    //添加文档
    @Test
    void testCreateDoc() throws IOException {
        Book book = bookDao.selectById(1);
        IndexRequest request = new IndexRequest("it-books").id(book.getId().toString());
        String json = JSON.toJSONString(book);
        request.source(json,XContentType.JSON);
        client.index(request,RequestOptions.DEFAULT);
    }


    @Test
    //批量添加文档
    void testCreateDocAll() throws IOException {

        QueryWrapper<Book> wrapper = new QueryWrapper<>();
        wrapper.gt("id",1);
        List<Book> bookList = bookDao.selectList(wrapper);
        BulkRequest bulk = new BulkRequest();
        for (Book book : bookList) {
            IndexRequest request = new IndexRequest("it-books").id(book.getId().toString());
            String json = JSON.toJSONString(book);
            request.source(json,XContentType.JSON);
            bulk.add(request);
        }
        client.bulk(bulk,RequestOptions.DEFAULT);
    }



    //按id查询
    @Test
    void testGet() throws IOException {
        GetRequest request = new GetRequest("it-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("it-books");

        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.query(QueryBuilders.termQuery("all","spring"));
        request.source(builder);

        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        for (SearchHit hit : hits) {
            String source = hit.getSourceAsString();
            //System.out.println(source);
            Book book = JSON.parseObject(source, Book.class);
            System.out.println(book);
        }
    }
}

三、SpringBoot整合Quartz

3.1 简单整合

Quartz技术是一个比较成熟的定时任务框架,springboot对其进行整合后,简化了一系列的配置,将很多配置采用默认设置,这样开发阶段就简化了很多。

  • 工作(Job):用于定义具体执行的工作
  • 工作明细(JobDetail):用于描述定时工作相关的信息
  • 触发器(Trigger):描述了工作明细与调度器的对应关系
  • 调度器(Scheduler):用于描述触发工作的执行规则,通常使用cron表达式定义规则

​ 简单说就是你定时干什么事情,这就是工作,工作不可能就是一个简单的方法,还要设置一些明细信息。工作啥时候执行,设置一个调度器,可以简单理解成设置一个工作执行的时间。工作和调度都是独立定义的,它们两个怎么配合到一起呢?用触发器。

步骤①:导入springboot整合Quartz的starter

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

步骤②:定义任务Bean,按照Quartz的开发规范制作,继承QuartzJobBean

public class MyQuartz extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println("quartz task run...");
    }
}

步骤③:创建Quartz配置类,定义工作明细(JobDetail)与触发器的(Trigger)bean

@Configuration
public class QuartzConfig {
    @Bean
    public JobDetail printJobDetail(){
        //绑定具体的工作
        return JobBuilder.newJob(MyQuartz.class).storeDurably().build();
    }
    @Bean
    public Trigger printJobTrigger(){
        ScheduleBuilder schedBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?");
        //绑定对应的工作明细
        return TriggerBuilder.newTrigger().forJob(printJobDetail()).withSchedule(schedBuilder).build();
    }
}

​ 工作明细中要设置对应的具体工作,使用newJob()操作传入对应的工作任务类型即可。触发器需要绑定任务,使用forJob()操作传入绑定的工作明细对象。此处可以为工作明细设置名称然后使用名称绑定,也可以直接调用对应方法绑定。触发器中最核心的规则是执行时间,此处使用调度器定义执行时间,执行时间描述方式使用的是cron表达式。

这种方式虽然简单,但是其实调度程序是写死在程序中,修改也不方便。

3.2 在接口中使用Quartz

3.2.1 创建一个定时任务相关实体类
package com.yyds.domain;

import lombok.Data;

@Data
public class QuartzBean {
    /** 任务id */
    private String  id;

    /** 任务名称 */
    private String jobName;

    /** 任务执行类 */
    private String jobClass;

    /** 任务状态 启动还是暂停*/
    private Integer status;

    /** 任务运行时间表达式 */
    private String cronExpression;
}
3.2.2 创建定时任务暂停,修改,启动,单次启动工具类
package com.yyds.utils;

import com.yyds.domain.QuartzBean;
import org.quartz.*;

public class QuartzUtils {

    /**
     * 创建定时任务 定时任务创建之后默认启动状态
     *
     * @param scheduler  调度器
     * @param quartzBean 定时任务信息类
     * @throws Exception
     */
    public static void createScheduleJob(Scheduler scheduler, QuartzBean quartzBean) {
        try {
            //获取到定时任务的执行类  必须是类的绝对路径名称
            //定时任务类需要是job类的具体实现 QuartzJobBean是job的抽象类。
            Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(quartzBean.getJobClass());
            // 构建定时任务信息
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(quartzBean.getJobName()).build();
            // 设置定时任务执行方式
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
            // 构建触发器trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzBean.getJobName()).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (ClassNotFoundException e) {
            System.out.println("定时任务类路径出错:请输入类的绝对路径");
        } catch (SchedulerException e) {
            System.out.println("创建定时任务出错:" + e.getMessage());
        }
    }

    /**
     * 根据任务名称暂停定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void pauseScheduleJob(Scheduler scheduler, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);

        scheduler.pauseJob(jobKey);

    }

    /**
     * 根据任务名称恢复定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void resumeScheduleJob(Scheduler scheduler, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);

        scheduler.resumeJob(jobKey);

    }

    /**
     * 根据任务名称立即运行一次定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void runOnce(Scheduler scheduler, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);

        scheduler.triggerJob(jobKey);

    }

    /**
     * 更新定时任务
     *
     * @param scheduler  调度器
     * @param quartzBean 定时任务信息类
     * @throws SchedulerException
     */
    public static void updateScheduleJob(Scheduler scheduler, QuartzBean quartzBean) throws SchedulerException {

        //获取到对应任务的触发器
        TriggerKey triggerKey = TriggerKey.triggerKey(quartzBean.getJobName());
        //设置定时任务执行方式
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzBean.getCronExpression());
        //重新构建任务的触发器trigger
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
        //重置对应的job
        scheduler.rescheduleJob(triggerKey, trigger);

    }

    /**
     * 根据定时任务名称从调度器当中删除定时任务
     *
     * @param scheduler 调度器
     * @param jobName   定时任务名称
     * @throws SchedulerException
     */
    public static void deleteScheduleJob(Scheduler scheduler, String jobName) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName);

        scheduler.deleteJob(jobKey);

    }
}
3.2.3 创建一个定时任务以及controller
package com.yyds.task;

import com.yyds.service.BookService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.HashMap;
import java.util.List;

/**
 * 统计
 */
public class FirstTask extends QuartzJobBean {


    @Autowired
    private BookService bookService;


    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        List<HashMap<String,Long>> res = bookService.getTypeCount();

        System.out.println(res);
    }
}
package com.yyds.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yyds.domain.Book;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.HashMap;
import java.util.List;


@Mapper
public interface BookDao extends BaseMapper<Book> {


    @Select("select type,count(1) as cnt from tbl_book group by type ")
    List<HashMap<String,Long>>  getTypeCount();

}

QuartzController类

package com.yyds.controller;

import com.yyds.domain.QuartzBean;
import com.yyds.utils.QuartzUtils;
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/quartz")
public class QuartzController {

    //注入任务调度
    @Autowired
    private Scheduler scheduler;

    @PostMapping("/create")
    public String  createJob(@RequestBody QuartzBean quartzBean)  {
        try {
            QuartzUtils.createScheduleJob(scheduler,quartzBean);
        } catch (Exception e) {
            return "创建失败";
        }
        return "创建成功";
    }

    @GetMapping("/pause")
    public String  pauseJob(@RequestParam("jobName") String jobName)  {
        try {
            QuartzUtils.pauseScheduleJob (scheduler,jobName);
        } catch (Exception e) {
            return "暂停失败";
        }
        return "暂停成功";
    }

    @RequestMapping("/runOnce")
    public String  runOnce(@RequestParam("jobName") String jobName)  {
        try {
            QuartzUtils.runOnce (scheduler,jobName);
        } catch (Exception e) {
            return "运行一次失败";
        }
        return "运行一次成功";
    }

    @GetMapping("/resume")
    public String  resume(@RequestParam("jobName") String jobName)  {
        try {
            QuartzUtils.resumeScheduleJob(scheduler,jobName);
        } catch (Exception e) {
            return "启动失败";
        }
        return "启动成功";
    }

    @PostMapping("/update")
    public String  update(@RequestBody  QuartzBean quartzBean)  {
        try {
            QuartzUtils.updateScheduleJob(scheduler,quartzBean);
        } catch (Exception e) {
            return "更新失败";
        }
        return "更新成功";
    }
}

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值