springboot專案整合quartz動態建立,停止,恢復,刪除定時任務,整合swagger3.0

血煞風雨城2018發表於2020-10-15

                  目錄                   

                         springboot專案整合quartz動態建立,停止,刪除定時任務

一、新增quartz依賴

二、編寫自定義job1 和 job2類,實現job介面重寫execute方法

三、編寫定時任務測試controller

                         springboot整合最新swagger3.0 api doc框架

一、新增swagger3.0依賴

二、新增配置項

三、編寫SwaggerProperties類用來讀取配置檔案中swagger的配置項

四、編寫SwaggerConfiguration 類


                                     

                              springboot專案整合quartz動態建立,停止,刪除定時任務

一、新增quartz依賴

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

二、編寫自定義job1 和 job2類,實現job介面重寫execute方法

package com.sf.gis.boot.rcboot.task;

import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;

/**
 * @author 80004819
 * @ClassName:
 * @Description:
 * @date 2020年10月15日 10:59:17
 */

/**
 * 新增@DisallowConcurrentExecution
 * //Job中的任務有可能併發執行,例如任務的執行時間過長,而每次觸發的時間間隔太短,則會導致任務會被併發執行。如果是併發執行,就需要一個資料庫鎖去避免一個資料被多次處理。
 */
@DisallowConcurrentExecution
@Slf4j
public class TestJob1 implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("JOB1定時任務開始執行,當前時間:{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        log.info("name is :{}", jobExecutionContext.getMergedJobDataMap().getString("name"));
        log.info("age is :{}", jobExecutionContext.getMergedJobDataMap().getInt("age"));
        log.info("phoneNumber is :{}", jobExecutionContext.getMergedJobDataMap().getString("phoneNumber"));
    }
}
package com.sf.gis.boot.rcboot.task;

import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @author 80004819
 * @ClassName:
 * @Description:
 * @date 2020年10月15日 11:49:33
 */
@DisallowConcurrentExecution
@Slf4j
public class TestJob2 implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        log.info("JOB2定時任務開始執行,當前時間:{}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        log.info("name is :{}", jobExecutionContext.getMergedJobDataMap().getString("name"));
        log.info("age is :{}", jobExecutionContext.getMergedJobDataMap().getInt("age"));
        log.info("phoneNumber is :{}", jobExecutionContext.getMergedJobDataMap().getString("phoneNumber"));
    }
}

三、編寫定時任務測試controller

package com.sf.gis.boot.rcboot.controller;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.sf.gis.boot.rcboot.shiro.ShiroConfig;
import com.sf.gis.boot.rcboot.task.TestJob1;
import com.sf.gis.boot.rcboot.util.JsonResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.C;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.spring.web.json.Json;

import java.io.IOException;
import java.util.Date;

/**
 * @author 80004819
 * @ClassName:
 * @Description:
 * @date 2020年10月15日 11:07:45
 */
@RestController
@RequestMapping("/scheduler")
@Api(tags = "定時任務controller")
@Slf4j
public class SchedulerJobController {


    @Autowired
    private Scheduler scheduler;

    private static final String JOB1 = "JOB1";

    private static final String JOB2 = "JOB2";


    @GetMapping("/createJob")
    @ApiOperation("建立定時任務")
    public JsonResponse createJob(@RequestParam("job_key") String job_key) {
        try {
            //定時任務在在當前時間10秒後執行
            Date start = new Date(System.currentTimeMillis() + 10000);
            Date end = new Date(System.currentTimeMillis() + 30000);
            //寫死的方式
//            JobDetail jobDetail = JobBuilder.newJob(TestJob1.class)
//                    .usingJobData("name", "dzx")
//                    .usingJobData("age", "26")
//                    .withIdentity(job_key)
//                    .build();

            //利用反射動態建立job的方式
            String clazzStr = StrUtil.equalsIgnoreCase(JOB1, job_key) ? "com.sf.gis.boot.rcboot.task.TestJob1" : "com.sf.gis.boot.rcboot.task.TestJob2";
            JobDetail jobDetail = JobBuilder.newJob(((Job) Class.forName(clazzStr).newInstance()).getClass())
                    .usingJobData("name", "dzx")
                    .usingJobData("age", "26")
                    .usingJobData("phoneNumber", "17823234521")
                    .withIdentity(job_key)
                    .build();

            //簡單觸發器
            Trigger trigger = TriggerBuilder.newTrigger().usingJobData("phoneNumber", "13865412236")
                    .withIdentity(job_key).startAt(start)
                    .endAt(end)
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            //3秒執行一次
                            .withIntervalInSeconds(3)
                            //一直重複執行
                            .repeatForever())
                    .build();

            //cron表示式觸發器
            CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                    /**給當前JobDetail新增引數,K V形式,鏈式呼叫,可以傳入多個引數,在Job實現類中,可以通過jobExecutionContext.getTrigger().getJobDataMap().get("orderNo")獲取值*/
                    .usingJobData("phoneNumer", "13865412236")
                    /**新增認證資訊,有3種重寫的方法,我這裡是其中一種,可以檢視原始碼看其餘2種*/
                    .withIdentity(job_key)
                    /**立即生效*/
                    //      .startNow()
                    /**開始執行時間*/
                    .startAt(start)
                    /**結束執行時間,不寫永久執行*/
                    .endAt(end)
                    /**新增執行規則,SimpleTrigger、CronTrigger的區別主要就在這裡,我這裡是demo,寫了個每2分鐘執行一次*/
                    .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?"))
                    .build();
            //將任務和觸發器新增到排程器中
            scheduler.scheduleJob(jobDetail, cronTrigger);
            if (!scheduler.isShutdown()) {
                //啟動定時任務
                scheduler.start();
            }
            return JsonResponse.ok(JsonResponse.STATUS_SUCCESS, true);
        } catch (Exception e) {
            log.error("建立定時任務失敗", e);
            return JsonResponse.ok(JsonResponse.STATUS_FAILED, false);
        }
    }


    @GetMapping("/shutDown")
    @ApiOperation("停止定時任務")
    public JsonResponse shutdown(@RequestParam("job_key") String job_key) {
        try {
            scheduler.pauseJob(JobKey.jobKey(job_key));
            scheduler.pauseTrigger(TriggerKey.triggerKey(job_key));
            return JsonResponse.ok(JsonResponse.STATUS_SUCCESS, true);
        } catch (SchedulerException e) {
            log.error("停止定時任務失敗", e);
            return JsonResponse.ok(JsonResponse.STATUS_FAILED, false);
        }
    }

    @PostMapping("/resume")
    @ApiOperation(value = "恢復定時任務", notes = "恢復")
    public Object resume(@RequestParam("job_key") String job_key) {
        try {
            //恢復Trigger
            scheduler.resumeTrigger(TriggerKey.triggerKey(job_key));
            return JsonResponse.ok(JsonResponse.STATUS_SUCCESS, true);
        } catch (Exception e) {
            log.error("恢復定時任務失敗", e);
            return JsonResponse.ok(JsonResponse.STATUS_FAILED, false);
        }
    }

    @PostMapping("/del")
    @ApiOperation(value = "刪除定時任務", notes = "刪除")
    public Object del(@RequestParam("job_key") String job_key) {
        try {
            //恢復Trigger
            scheduler.pauseTrigger(TriggerKey.triggerKey(job_key));
            //移除觸發器
            scheduler.unscheduleJob(TriggerKey.triggerKey(job_key));
            //刪除Job
            scheduler.deleteJob(JobKey.jobKey(job_key));
            return JsonResponse.ok(JsonResponse.STATUS_SUCCESS, true);
        } catch (Exception e) {
            log.error("刪除定時任務失敗", e);
            return JsonResponse.ok(JsonResponse.STATUS_FAILED, false);
        }
    }


}

呼叫建立定時任務介面,可看到控制檯如下輸出,說明定時任務建立併成功執行:

                 

                         springboot整合最新swagger3.0 api doc框架

一、新增swagger3.0依賴

  <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-boot-starter</artifactId>
            <version>3.0.0</version>
        </dependency>


     <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.10</version>
        </dependency>

二、新增配置項

# ===== 自定義swagger配置 ===== #
swagger:
  enable: true
  application-name: ${spring.application.name}
  application-version: 1.0
  application-description: springfox swagger 3.0整合Demo
  try-host: http://localhost:${server.port}

三、編寫SwaggerProperties類用來讀取配置檔案中swagger的配置項

package com.sf.gis.boot.rcboot.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author 80004819
 * @ClassName:
 * @Description:
 * @date 2020年10月15日 13:54:07
 */
@Component
@ConfigurationProperties("swagger")
@Data
public class SwaggerProperties {
    /**
     * 是否開啟swagger,生產環境一般關閉,所以這裡定義一個變數
     */
    private Boolean enable;

    /**
     * 專案應用名
     */
    private String applicationName;

    /**
     * 專案版本資訊
     */
    private String applicationVersion;

    /**
     * 專案描述資訊
     */
    private String applicationDescription;

    /**
     * 介面除錯地址
     */
    private String tryHost;
}

四、編寫SwaggerConfiguration 類

package com.sf.gis.boot.rcboot.config;

import io.swagger.models.auth.In;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.boot.SpringBootVersion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author 80004819
 * @ClassName:
 * @Description:
 * @date 2020年10月15日 13:55:03
 */
@EnableOpenApi
@Configuration
public class SwaggerConfiguration implements WebMvcConfigurer {

    private final SwaggerProperties swaggerProperties;

    public SwaggerConfiguration(SwaggerProperties swaggerProperties) {
        this.swaggerProperties = swaggerProperties;
    }

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30).pathMapping("/")

                // 定義是否開啟swagger,false為關閉,可以通過變數控制
                .enable(swaggerProperties.getEnable())

                // 將api的元資訊設定為包含在json ResourceListing響應中。
                .apiInfo(apiInfo())

                // 介面除錯地址
                .host(swaggerProperties.getTryHost())

                // 選擇哪些介面作為swagger的doc釋出
                .select()
                //所有的controller介面
                .apis(RequestHandlerSelectors.any())
                // 選擇指定的包
                // .apis(RequestHandlerSelectors.basePackage("com.sf.gis.boot.rcboot.controller"))
                .paths(PathSelectors.any())
                .build()

                // 支援的通訊協議集合
                .protocols(newHashSet("https", "http"))

                // 授權資訊設定,必要的header token等認證資訊
                .securitySchemes(securitySchemes())

                // 授權資訊全域性應用
                .securityContexts(securityContexts());
    }

    /**
     * API 頁面上半部分展示資訊
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title(swaggerProperties.getApplicationName() + " Api Doc")
                .description(swaggerProperties.getApplicationDescription())
                .contact(new Contact("lighter", null, "123456@gmail.com"))
                .version("Application Version: " + swaggerProperties.getApplicationVersion() + ", Spring Boot Version: " + SpringBootVersion.getVersion())
                .build();
    }


    /**
     * 設定授權資訊
     */
    private List<SecurityScheme> securitySchemes() {
        ApiKey apiKey = new ApiKey("BASE_TOKEN", "token", In.HEADER.toValue());
        return Collections.singletonList(apiKey);
    }

    /**
     * 授權資訊全域性應用
     */
    private List<SecurityContext> securityContexts() {
        return Collections.singletonList(
                SecurityContext.builder()
                        .securityReferences(Collections.singletonList(new SecurityReference("BASE_TOKEN", new AuthorizationScope[]{new AuthorizationScope("global", "")})))
                        .build()
        );
    }

    @SafeVarargs
    private final <T> Set<T> newHashSet(T... ts) {
        if (ts.length > 0) {
            return new LinkedHashSet<>(Arrays.asList(ts));
        }
        return null;
    }

    /**
     * 通用攔截器排除swagger設定,所有攔截器都會自動加swagger相關的資源排除資訊
     */
    @SuppressWarnings("unchecked")
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        try {
            Field registrationsField = FieldUtils.getField(InterceptorRegistry.class, "registrations", true);
            List<InterceptorRegistration> registrations = (List<InterceptorRegistration>) ReflectionUtils.getField(registrationsField, registry);
            if (registrations != null) {
                for (InterceptorRegistration interceptorRegistration : registrations) {
                    interceptorRegistration
                            .excludePathPatterns("/swagger**/**")
                            .excludePathPatterns("/webjars/**")
                            .excludePathPatterns("/v3/**")
                            .excludePathPatterns("/doc.html");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

最後訪問http://localhost:8085/swagger-ui/index.html  ,注意這裡已經不是swagger2的http://localhost:8085/swagger-ui.html

訪問路徑了。

看到如下介面:

相關文章