springboot整合quartz通过数据库配置任务调度简单办法

news2024/11/26 21:44:13

简介

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。在java企业级应用中,Quartz是使用最广泛的定时调度框架。

在Quartz中的主要概念:

  • Scheduler:调度任务的主要API
  • ScheduleBuilder:用于构建Scheduler,例如其简单实现类SimpleScheduleBuilder
  • Job:调度任务执行的接口,也即定时任务执行的方法
  • JobDetail:定时任务作业的实例
  • JobBuilder:关联具体的Job,用于构建JobDetail
  • Trigger:定义调度执行计划的组件,即定时执行
  • TriggerBuilder:构建Trigger

教程

1.首先在maven项目的pom文件引入spring-boot-starter-quartz依赖

	<!--定时任务-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-quartz</artifactId>
		</dependency>

2. 数据库创建一个sys_job表存储定时任务的调度信息,表结构如下:

创建表sql语句

CREATE TABLE "public"."sys_job" (
  "id" int8 NOT NULL,
  "job_name" varchar(50) COLLATE "pg_catalog"."default",
  "job_group" varchar(50) COLLATE "pg_catalog"."default",
  "invoke_target" varchar(255) COLLATE "pg_catalog"."default",
  "cron_expression" varchar(50) COLLATE "pg_catalog"."default",
  "misfire_policy" varchar(2) COLLATE "pg_catalog"."default",
  "concurrent" varchar(1) COLLATE "pg_catalog"."default",
  "create_user" int8,
  "create_time" timestamp(6),
  "update_user" int8,
  "update_time" timestamp(6),
  "status" int4,
  "is_deleted" int4,
  CONSTRAINT "blade_datasource_copy1_pkey" PRIMARY KEY ("id")
)
;

ALTER TABLE "public"."sys_job" 
  OWNER TO "postgres";

COMMENT ON COLUMN "public"."sys_job"."id" IS '主键';

COMMENT ON COLUMN "public"."sys_job"."job_name" IS '任务名称';

COMMENT ON COLUMN "public"."sys_job"."job_group" IS '任务组名';

COMMENT ON COLUMN "public"."sys_job"."invoke_target" IS '调用目标字符串';

COMMENT ON COLUMN "public"."sys_job"."cron_expression" IS 'cron执行表达式';

COMMENT ON COLUMN "public"."sys_job"."misfire_policy" IS 'cron计划策略 ';

COMMENT ON COLUMN "public"."sys_job"."concurrent" IS '是否并发执行(0允许 1禁止)';

COMMENT ON COLUMN "public"."sys_job"."create_user" IS '创建人';

COMMENT ON COLUMN "public"."sys_job"."create_time" IS '创建时间';

COMMENT ON COLUMN "public"."sys_job"."update_user" IS '修改人';

COMMENT ON COLUMN "public"."sys_job"."update_time" IS '修改时间';

COMMENT ON COLUMN "public"."sys_job"."status" IS '状态';

COMMENT ON COLUMN "public"."sys_job"."is_deleted" IS '是否已删除';

COMMENT ON TABLE "public"."sys_job" IS '数据源配置表';

 3.创建数据库映射orm实体类和mapper类,我是用mybatis plus工具实现的

实体类SysJob代码:


import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springblade.coalface.modules.quartz.constant.ScheduleConstants;
import org.springblade.core.mp.base.BaseEntity;

/**
 * 定时任务调度表 sys_job
 * 
 * @author tarzan
 */
@Data
@TableName("sys_job")
public class SysJob extends BaseEntity {
    private static final long serialVersionUID = 1L;

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

    /** 任务组名 */
    private String jobGroup;

    /** 调用目标字符串 */
    private String invokeTarget;

    /** cron执行表达式 */
    private String cronExpression;

    /** cron计划策略 */
    private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;

    /** 是否并发执行(0允许 1禁止) */
    private String concurrent;


}
SysJobMapper类代码如下:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springblade.coalface.modules.quartz.domain.SysJob;


/**
 * 调度任务信息 数据层
 * 
 * @author tarzan
 */
public interface SysJobMapper extends BaseMapper<SysJob> {
}

4.创建定时任务表的增删改查接口

创建ISysJobService接口代码如下:


import com.baomidou.mybatisplus.core.metadata.IPage;
import org.quartz.SchedulerException;
import org.springblade.coalface.modules.quartz.domain.SysJob;
import org.springblade.coalface.modules.quartz.util.TaskException;
import org.springblade.core.mp.base.BaseService;
import org.springblade.core.mp.support.Query;
import org.springframework.transaction.annotation.Transactional;


/**
 * 定时任务调度信息信息 服务层
 * 
 * @author tarzan
 */
public interface ISysJobService extends BaseService<SysJob> {

    IPage<SysJob> selectJobList(SysJob job, Query query);

    @Transactional(rollbackFor = Exception.class)
    boolean pauseJob(SysJob job) throws SchedulerException;

    @Transactional(rollbackFor = Exception.class)
    boolean resumeJob(SysJob job) throws SchedulerException;

    @Transactional(rollbackFor = Exception.class)
    boolean deleteJob(SysJob job) throws SchedulerException;

    @Transactional(rollbackFor = Exception.class)
    boolean changeStatus(SysJob job) throws SchedulerException;

    @Transactional(rollbackFor = Exception.class)
    boolean run(SysJob job) throws SchedulerException;

    @Transactional(rollbackFor = Exception.class)
    boolean insertJob(SysJob job) throws SchedulerException, TaskException;

    @Transactional(rollbackFor = Exception.class)
    boolean updateJob(SysJob job) throws SchedulerException, TaskException;

}

创建SysJobServiceImpl实现类代码如下:


import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.AllArgsConstructor;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springblade.coalface.modules.quartz.constant.ScheduleConstants;
import org.springblade.coalface.modules.quartz.domain.SysJob;
import org.springblade.coalface.modules.quartz.mapper.SysJobMapper;
import org.springblade.coalface.modules.quartz.service.ISysJobService;
import org.springblade.coalface.modules.quartz.util.ScheduleUtils;
import org.springblade.coalface.modules.quartz.util.TaskException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.tool.utils.StringUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Objects;

/**
 * 定时任务调度信息 服务层
 *
 * @author tarzan
 */
@Service
@AllArgsConstructor
public class SysJobServiceImpl extends BaseServiceImpl<SysJobMapper, SysJob> implements ISysJobService {

    private final Scheduler scheduler;

    /**
     * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
     */
    @PostConstruct
    public void init() throws SchedulerException, TaskException {
        scheduler.clear();
        List<SysJob> jobList = super.list();
        for (SysJob job : jobList) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
    }

    /**
     * 获取quartz调度器的计划任务列表
     *
     * @param job 调度信息
     * @return 调度任务对象集合
     */
    @Override
    public IPage<SysJob> selectJobList(SysJob job, Query query) {
        return super.lambdaQuery().like(StringUtil.isNotBlank(job.getJobName()),SysJob::getJobName,job.getJobName())
                .eq(StringUtil.isNotBlank(job.getJobGroup()),SysJob::getJobGroup,job.getJobGroup())
                .eq(Objects.nonNull(job.getStatus()),SysJob::getStatus,job.getStatus()).page(Condition.getPage(query));
    }


    /**
     * 暂停任务
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean pauseJob(SysJob job) throws SchedulerException {
        Long jobId = job.getId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        boolean flag = super.updateById(job);
        if (flag) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return flag;
    }

    /**
     * 恢复任务
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean resumeJob(SysJob job) throws SchedulerException {
        Long jobId = job.getId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        boolean flag = super.updateById(job);
        if (flag) {
            scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return flag;
    }

    /**
     * 删除任务后,所对应的trigger也将被删除
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteJob(SysJob job) throws SchedulerException {
        Long jobId = job.getId();
        String jobGroup = job.getJobGroup();
        boolean flag = super.removeById(jobId);
        if (flag) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return flag;
    }


    /**
     * 任务调度状态修改
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changeStatus(SysJob job) throws SchedulerException {
        boolean flag = false;
        Integer status = job.getStatus();
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
            flag = resumeJob(job);
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
            flag = pauseJob(job);
        }
        return flag;
    }

    /**
     * 立即运行任务
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean run(SysJob job) throws SchedulerException {
        boolean result = false;
        Long jobId = job.getId();
        String jobGroup = job.getJobGroup();
        SysJob properties = super.getById(job.getId());
        // 参数
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            result = true;
            scheduler.triggerJob(jobKey, dataMap);
        }
        return result;
    }

    /**
     * 新增任务
     *
     * @param job 调度信息 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertJob(SysJob job) throws SchedulerException, TaskException {
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        boolean flag = super.save(job);
        if (flag) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
        return flag;
    }

    /**
     * 更新任务的时间表达式
     *
     * @param job 调度信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateJob(SysJob job) throws SchedulerException, TaskException {
        SysJob properties = super.getById(job.getId());
        boolean flag = super.updateById(job);
        if (flag) {
            updateSchedulerJob(job, properties.getJobGroup());
        }
        return flag;
    }

    /**
     * 更新任务
     *
     * @param job      任务对象
     * @param jobGroup 任务组名
     */
    public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException {
        Long jobId = job.getId();
        // 判断是否存在
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除,然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }

}

创建controller层接口代码如下


import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.quartz.SchedulerException;
import org.springblade.coalface.modules.quartz.domain.SysJob;
import org.springblade.coalface.modules.quartz.service.ISysJobService;
import org.springblade.coalface.modules.quartz.util.CronUtils;
import org.springblade.coalface.modules.quartz.util.TaskException;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.web.bind.annotation.*;


/**
 * 调度任务信息操作处理
 *
 * @author tarzan
 */
@RestController
@Api(tags = "定时任务")
@RequestMapping("/schedule/job")
@AllArgsConstructor
public class SysJobController {

    private final ISysJobService jobService;

    /**
     * 查询定时任务列表
     */
    @GetMapping("/page")
    @ApiOperation(value = "分页")
    public R<IPage<SysJob>> list(SysJob sysJob, Query query) {
        return R.data(jobService.selectJobList(sysJob,query));
    }


    /**
     * 获取定时任务详细信息
     */
    @GetMapping(value = "/{jobId}")
    @ApiOperation(value = "详情")
    public R<SysJob> getInfo(@PathVariable("jobId") Long jobId) {
        return R.data(jobService.getById(jobId));
    }

    /**
     * 新增定时任务
     */
    @PostMapping
    @ApiOperation(value = "添加")
    public R<Boolean> add(@RequestBody SysJob job) throws SchedulerException, TaskException {
        R<Boolean> result=isValid(job);
        if(!result.isSuccess()){
            return result;
        }
        return R.status(jobService.insertJob(job));
    }

    /**
     * 修改定时任务
     */
    @PutMapping
    @ApiOperation(value = "修改")
    public R<Boolean> edit(@RequestBody SysJob job) throws SchedulerException, TaskException {
        R<Boolean> result=isValid(job);
        if(!result.isSuccess()){
            return result;
        }
        return R.status(jobService.updateJob(job));
    }

    /**
     * 定时任务状态修改
     */
    @PutMapping("/changeStatus")
    @ApiOperation(value = "状态修改")
    public R<Boolean> changeStatus(@RequestBody SysJob job) throws SchedulerException {
        SysJob newJob = jobService.getById(job.getId());
        newJob.setStatus(job.getStatus());
        return R.status(jobService.changeStatus(newJob));
    }

    /**
     * 定时任务立即执行一次
     */
    @PutMapping("/run")
    @ApiOperation(value = "立即执行一次")
    public R<Boolean> run(@RequestBody SysJob job) throws SchedulerException {
        return R.status(jobService.run(job));
    }

    /**
     * 删除定时任务
     */
    @DeleteMapping("/{jobId}")
    @ApiOperation(value = "删除")
    public R<Boolean> remove(@PathVariable Long jobId) throws SchedulerException, TaskException {
        SysJob sysJob=jobService.getById(jobId);
        return R.status(jobService.deleteJob(sysJob));
    }

    private R<Boolean> isValid(SysJob job){
        if (!CronUtils.isValid(job.getCronExpression())) {
            return R.fail("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
        } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), "rmi:")) {
            return R.fail("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
        } else if (StringUtil.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{"ldap:", "ldaps:"})) {
            return R.fail("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
        } else if (StringUtil.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{"http://", "https://"})) {
            return R.fail("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
        }
        return R.success();
    }
}

5.代码中的主要的相关工具类

AbstractQuartzJob 抽象quartz调用类,代码如下:

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.coalface.modules.quartz.constant.ScheduleConstants;
import org.springblade.coalface.modules.quartz.domain.SysJob;

import java.util.Date;

/**
 * 抽象quartz调用
 *
 * @author tarzan
 */
public abstract class AbstractQuartzJob implements Job {
    private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);

    /**
     * 线程本地变量
     */
    private static ThreadLocal<Date> threadLocal = new ThreadLocal<>();

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        SysJob sysJob = (SysJob) context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES);
        try {
            before(context, sysJob);
            if (sysJob != null) {
                doExecute(context, sysJob);
            }
            after(context, sysJob, null);
        } catch (Exception e) {
            log.error("任务执行异常  - :", e);
            after(context, sysJob, e);
        }
    }

    /**
     * 执行前
     *
     * @param context 工作执行上下文对象
     * @param sysJob 系统计划任务
     */
    protected void before(JobExecutionContext context, SysJob sysJob)
    {
        threadLocal.set(new Date());
    }

    /**
     * 执行后
     *
     * @param context 工作执行上下文对象
     * @param sysJob 系统计划任务
     */
    protected void after(JobExecutionContext context, SysJob sysJob, Exception e) {
        Date startTime = threadLocal.get();
        threadLocal.remove();
    }

    /**
     * 执行方法,由子类重载
     *
     * @param context 工作执行上下文对象
     * @param sysJob 系统计划任务
     * @throws Exception 执行过程中的异常
     */
    protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}
cron表达式工具类,代码如下:

import org.quartz.CronExpression;

import java.text.ParseException;
import java.util.Date;

/**
 * cron表达式工具类
 * 
 * @author tarzan
 *
 */
public class CronUtils {
    /**
     * 返回一个布尔值代表一个给定的Cron表达式的有效性
     *
     * @param cronExpression Cron表达式
     * @return boolean 表达式是否有效
     */
    public static boolean isValid(String cronExpression)
    {
        return CronExpression.isValidExpression(cronExpression);
    }

    /**
     * 返回一个字符串值,表示该消息无效Cron表达式给出有效性
     *
     * @param cronExpression Cron表达式
     * @return String 无效时返回表达式错误描述,如果有效返回null
     */
    public static String getInvalidMessage(String cronExpression) {
        try
        {
            new CronExpression(cronExpression);
            return null;
        }
        catch (ParseException pe)
        {
            return pe.getMessage();
        }
    }

    /**
     * 返回下一个执行时间根据给定的Cron表达式
     *
     * @param cronExpression Cron表达式
     * @return Date 下次Cron表达式执行时间
     */
    public static Date getNextExecution(String cronExpression) {
        try
        {
            CronExpression cron = new CronExpression(cronExpression);
            return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
        }
        catch (ParseException e)
        {
            throw new IllegalArgumentException(e.getMessage());
        }
    }
}
定时任务执行工具JobInvokeUtil类,代码如下:

import org.springblade.coalface.modules.quartz.domain.SysJob;
import org.springblade.core.tool.utils.CollectionUtil;
import org.springblade.core.tool.utils.SpringUtil;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;


/**
 * 任务执行工具
 *
 * @author tarzan
 */
public class JobInvokeUtil {
    /**
     * 执行方法
     *
     * @param sysJob 系统任务
     */
    public static void invokeMethod(SysJob sysJob) throws Exception {
        String invokeTarget = sysJob.getInvokeTarget();
        String beanName = getBeanName(invokeTarget);
        String methodName = getMethodName(invokeTarget);
        List<Object[]> methodParams = getMethodParams(invokeTarget);

        if (!isValidClassName(beanName)) {
            Object bean = SpringUtil.getBean(beanName);
            invokeMethod(bean, methodName, methodParams);
        } else {
            Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance();
            invokeMethod(bean, methodName, methodParams);
        }
    }

    /**
     * 调用任务方法
     *
     * @param bean 目标对象
     * @param methodName 方法名称
     * @param methodParams 方法参数
     */
    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        if(Objects.nonNull(bean)){
            if (CollectionUtil.isNotEmpty(methodParams)) {
                Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams));
                method.invoke(bean, getMethodParamsValue(methodParams));
            } else {
                Method method = bean.getClass().getMethod(methodName);
                method.invoke(bean);
            }
        }
    }

    /**
     * 校验是否为为class包名
     *
     * @param invokeTarget 名称
     * @return true是 false否
     */
    public static boolean isValidClassName(String invokeTarget)
    {
        return StringUtils.countMatches(invokeTarget, ".") > 1;
    }

    /**
     * 获取bean名称
     *
     * @param invokeTarget 目标字符串
     * @return bean名称
     */
    public static String getBeanName(String invokeTarget)
    {
        String beanName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringBeforeLast(beanName, ".");
    }

    /**
     * 获取bean方法
     *
     * @param invokeTarget 目标字符串
     * @return method方法
     */
    public static String getMethodName(String invokeTarget)
    {
        String methodName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringAfterLast(methodName, ".");
    }

    /**
     * 获取method方法参数相关列表
     *
     * @param invokeTarget 目标字符串
     * @return method方法相关参数列表
     */
    public static List<Object[]> getMethodParams(String invokeTarget)
    {
        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
        if (StringUtils.isEmpty(methodStr))
        {
            return null;
        }
        String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");
        List<Object[]> classs = new LinkedList<>();
        for (String methodParam : methodParams) {
            String str = StringUtils.trimToEmpty(methodParam);
            // String字符串类型,以'或"开头
            if (StringUtils.startsWithAny(str, "'", "\"")) {
                classs.add(new Object[]{StringUtils.substring(str, 1, str.length() - 1), String.class});
            }
            // boolean布尔类型,等于true或者false
            else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) {
                classs.add(new Object[]{Boolean.valueOf(str), Boolean.class});
            }
            // long长整形,以L结尾
            else if (StringUtils.endsWith(str, "L")) {
                classs.add(new Object[]{Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class});
            }
            // double浮点类型,以D结尾
            else if (StringUtils.endsWith(str, "D")) {
                classs.add(new Object[]{Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class});
            }
            // 其他类型归类为整形
            else {
                classs.add(new Object[]{Integer.valueOf(str), Integer.class});
            }
        }
        return classs;
    }

    /**
     * 获取参数类型
     *
     * @param methodParams 参数相关列表
     * @return 参数类型列表
     */
    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams)
    {
        Class<?>[] classs = new Class<?>[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = (Class<?>) os[1];
            index++;
        }
        return classs;
    }

    /**
     * 获取参数值
     *
     * @param methodParams 参数相关列表
     * @return 参数值列表
     */
    public static Object[] getMethodParamsValue(List<Object[]> methodParams)
    {
        Object[] classs = new Object[methodParams.size()];
        int index = 0;
        for (Object[] os : methodParams)
        {
            classs[index] = os[0];
            index++;
        }
        return classs;
    }
}
定时任务工具类 ScheduleUtils 工具类,代码如下:

import org.quartz.*;
import org.springblade.coalface.modules.quartz.constant.ScheduleConstants;
import org.springblade.coalface.modules.quartz.domain.SysJob;
import org.springblade.core.tool.utils.SpringUtil;

import java.util.Objects;

/**
 * 定时任务工具类
 *
 * @author tarzan
 */
public class ScheduleUtils {
    /**
     * 得到quartz任务类
     *
     * @param sysJob 执行计划
     * @return 具体执行任务类
     */
    private static Class<? extends Job> getQuartzJobClass(SysJob sysJob) {
        boolean isConcurrent = "0".equals(sysJob.getConcurrent());
        return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
    }

    /**
     * 构建任务触发对象
     */
    public static TriggerKey getTriggerKey(Long jobId, String jobGroup) {
        return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
    }

    /**
     * 构建任务键对象
     */
    public static JobKey getJobKey(Long jobId, String jobGroup) {
        return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
    }

    /**
     * 创建定时任务
     */
    public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException {
        Class<? extends Job> jobClass = getQuartzJobClass(job);
        // 构建job信息
        Long jobId = job.getId();
        String jobGroup = job.getJobGroup();
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();

        // 表达式调度构建器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
        cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);

        // 按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
                .withSchedule(cronScheduleBuilder).build();

        // 放入参数,运行时的方法可以获取
        jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);

        // 判断是否存在
        if (scheduler.checkExists(getJobKey(jobId, jobGroup))) {
            // 防止创建时存在数据问题 先移除,然后在执行创建操作
            scheduler.deleteJob(getJobKey(jobId, jobGroup));
        }

        // 判断任务是否过期
        if (Objects.nonNull(CronUtils.getNextExecution(job.getCronExpression()))) {
            // 执行调度任务
            scheduler.scheduleJob(jobDetail, trigger);
        }

        // 暂停任务
        if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
    }

    /**
     * 设置定时任务策略
     */
    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)
            throws TaskException {
        switch (job.getMisfirePolicy()) {
            case ScheduleConstants.MISFIRE_DEFAULT:
                return cb;
            case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
                return cb.withMisfireHandlingInstructionIgnoreMisfires();
            case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
                return cb.withMisfireHandlingInstructionFireAndProceed();
            case ScheduleConstants.MISFIRE_DO_NOTHING:
                return cb.withMisfireHandlingInstructionDoNothing();
            default:
                throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
                        + "' cannot be used in cron schedule tasks", TaskException.Code.CONFIG_ERROR);
        }
    }

    /**
     * 检查包名是否为白名单配置
     *
     * @param invokeTarget 目标字符串
     * @return 结果
     */
    public static boolean whiteList(String invokeTarget) {
        String packageName = StringUtils.substringBefore(invokeTarget, "(");
        int count = StringUtils.countMatches(packageName, ".");
        if (count > 1) {
            return StringUtils.containsAnyIgnoreCase(invokeTarget, JOB_WHITELIST_STR);
        }
        Object obj = SpringUtil.getBean(StringUtils.split(invokeTarget, ".")[0]);
        String beanPackageName = obj.getClass().getPackage().getName();
        return StringUtils.containsAnyIgnoreCase(beanPackageName, JOB_WHITELIST_STR)
                && !StringUtils.containsAnyIgnoreCase(beanPackageName, JOB_ERROR_STR);
    }

    /**
     * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
     */
    public static final String[] JOB_WHITELIST_STR = { "org.springblade" };

    /**
     * 定时任务违规的字符
     */
    public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", "org.springframework", "org.apache"};
}
定时任务处理(禁止并发执行)执行类QuartzDisallowConcurrentExecution,代码如下:

import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.springblade.coalface.modules.quartz.domain.SysJob;

/**
 * 定时任务处理(禁止并发执行)
 * 
 * @author tarzan
 *
 */
@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
    @Override
    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
        JobInvokeUtil.invokeMethod(sysJob);
    }
}
定时任务处理(允许并发执行)执行类QuartzJobExecution,代码如下:

import org.quartz.JobExecutionContext;
import org.springblade.coalface.modules.quartz.domain.SysJob;

/**
 * 定时任务处理(允许并发执行)
 * 
 * @author tarzan
 *
 */
public class QuartzJobExecution extends AbstractQuartzJob {
    @Override
    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
        JobInvokeUtil.invokeMethod(sysJob);
    }
}
字符串工具类StringUtils,代码如下:
import org.springframework.util.AntPathMatcher;

import java.util.*;

/**
 * 字符串工具类
 * 
 * @author tarzan
 */
public class StringUtils extends org.apache.commons.lang3.StringUtils {
    /** 空字符串 */
    private static final String NULLSTR = "";

    /** 下划线 */
    private static final char SEPARATOR = '_';

    /**
     * 获取参数不为空值
     * 
     * @param value defaultValue 要判断的value
     * @return value 返回值
     */
    public static <T> T nvl(T value, T defaultValue)
    {
        return value != null ? value : defaultValue;
    }

    /**
     * * 判断一个Collection是否为空, 包含List,Set,Queue
     * 
     * @param coll 要判断的Collection
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Collection<?> coll)
    {
        return isNull(coll) || coll.isEmpty();
    }

    /**
     * * 判断一个Collection是否非空,包含List,Set,Queue
     * 
     * @param coll 要判断的Collection
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Collection<?> coll)
    {
        return !isEmpty(coll);
    }

    /**
     * * 判断一个对象数组是否为空
     * 
     * @param objects 要判断的对象数组
     ** @return true:为空 false:非空
     */
    public static boolean isEmpty(Object[] objects)
    {
        return isNull(objects) || (objects.length == 0);
    }

    /**
     * * 判断一个对象数组是否非空
     * 
     * @param objects 要判断的对象数组
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Object[] objects)
    {
        return !isEmpty(objects);
    }

    /**
     * * 判断一个Map是否为空
     * 
     * @param map 要判断的Map
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(Map<?, ?> map)
    {
        return isNull(map) || map.isEmpty();
    }

    /**
     * * 判断一个Map是否为空
     * 
     * @param map 要判断的Map
     * @return true:非空 false:空
     */
    public static boolean isNotEmpty(Map<?, ?> map)
    {
        return !isEmpty(map);
    }

    /**
     * * 判断一个字符串是否为空串
     * 
     * @param str String
     * @return true:为空 false:非空
     */
    public static boolean isEmpty(String str)
    {
        return isNull(str) || NULLSTR.equals(str.trim());
    }

    /**
     * * 判断一个字符串是否为非空串
     * 
     * @param str String
     * @return true:非空串 false:空串
     */
    public static boolean isNotEmpty(String str)
    {
        return !isEmpty(str);
    }

    /**
     * * 判断一个对象是否为空
     * 
     * @param object Object
     * @return true:为空 false:非空
     */
    public static boolean isNull(Object object)
    {
        return object == null;
    }

    /**
     * * 判断一个对象是否非空
     * 
     * @param object Object
     * @return true:非空 false:空
     */
    public static boolean isNotNull(Object object)
    {
        return !isNull(object);
    }

    /**
     * * 判断一个对象是否是数组类型(Java基本型别的数组)
     * 
     * @param object 对象
     * @return true:是数组 false:不是数组
     */
    public static boolean isArray(Object object)
    {
        return isNotNull(object) && object.getClass().isArray();
    }

    /**
     * 去空格
     */
    public static String trim(String str)
    {
        return (str == null ? "" : str.trim());
    }

    /**
     * 截取字符串
     * 
     * @param str 字符串
     * @param start 开始
     * @return 结果
     */
    public static String substring(final String str, int start)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = str.length() + start;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (start > str.length())
        {
            return NULLSTR;
        }

        return str.substring(start);
    }

    /**
     * 截取字符串
     * 
     * @param str 字符串
     * @param start 开始
     * @param end 结束
     * @return 结果
     */
    public static String substring(final String str, int start, int end)
    {
        if (str == null)
        {
            return NULLSTR;
        }

        if (end < 0)
        {
            end = str.length() + end;
        }
        if (start < 0)
        {
            start = str.length() + start;
        }

        if (end > str.length())
        {
            end = str.length();
        }

        if (start > end)
        {
            return NULLSTR;
        }

        if (start < 0)
        {
            start = 0;
        }
        if (end < 0)
        {
            end = 0;
        }

        return str.substring(start, end);
    }


    /**
     * 是否为http(s)://开头
     * 
     * @param link 链接
     * @return 结果
     */
    public static boolean ishttp(String link) {
        return StringUtils.startsWithAny(link, "http://", "https://");
    }

    /**
     * 字符串转set
     * 
     * @param str 字符串
     * @param sep 分隔符
     * @return set集合
     */
    public static final Set<String> str2Set(String str, String sep)
    {
        return new HashSet<String>(str2List(str, sep, true, false));
    }

    /**
     * 字符串转list
     * 
     * @param str 字符串
     * @param sep 分隔符
     * @param filterBlank 过滤纯空白
     * @param trim 去掉首尾空白
     * @return list集合
     */
    public static final List<String> str2List(String str, String sep, boolean filterBlank, boolean trim)
    {
        List<String> list = new ArrayList<String>();
        if (StringUtils.isEmpty(str))
        {
            return list;
        }

        // 过滤空白字符串
        if (filterBlank && StringUtils.isBlank(str))
        {
            return list;
        }
        String[] split = str.split(sep);
        for (String string : split)
        {
            if (filterBlank && StringUtils.isBlank(string))
            {
                continue;
            }
            if (trim)
            {
                string = string.trim();
            }
            list.add(string);
        }

        return list;
    }

    /**
     * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
     *
     * @param collection 给定的集合
     * @param array 给定的数组
     * @return boolean 结果
     */
    public static boolean containsAny(Collection<String> collection, String... array)
    {
        if (isEmpty(collection) || isEmpty(array))
        {
            return false;
        }
        else
        {
            for (String str : array)
            {
                if (collection.contains(str))
                {
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写
     *
     * @param cs 指定字符串
     * @param searchCharSequences 需要检查的字符串数组
     * @return 是否包含任意一个字符串
     */
    public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences)
    {
        if (isEmpty(cs) || isEmpty(searchCharSequences))
        {
            return false;
        }
        for (CharSequence testStr : searchCharSequences)
        {
            if (containsIgnoreCase(cs, testStr))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 驼峰转下划线命名
     */
    public static String toUnderScoreCase(String str)
    {
        if (str == null)
        {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // 前置字符是否大写
        boolean preCharIsUpperCase = true;
        // 当前字符是否大写
        boolean curreCharIsUpperCase = true;
        // 下一字符是否大写
        boolean nexteCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++)
        {
            char c = str.charAt(i);
            if (i > 0)
            {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            }
            else
            {
                preCharIsUpperCase = false;
            }

            curreCharIsUpperCase = Character.isUpperCase(c);

            if (i < (str.length() - 1))
            {
                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }

            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
            {
                sb.append(SEPARATOR);
            }
            sb.append(Character.toLowerCase(c));
        }

        return sb.toString();
    }

    /**
     * 是否包含字符串
     * 
     * @param str 验证字符串
     * @param strs 字符串组
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs)
    {
        if (str != null && strs != null)
        {
            for (String s : strs)
            {
                if (str.equalsIgnoreCase(trim(s)))
                {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
     * 
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name)
    {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty())
        {
            // 没必要转换
            return "";
        }
        else if (!name.contains("_"))
        {
            // 不含下划线,仅将首字母大写
            return name.substring(0, 1).toUpperCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels)
        {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty())
            {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }

    /**
     * 驼峰式命名法
     * 例如:user_name->userName
     */
    public static String toCamelCase(String s)
    {
        if (s == null)
        {
            return null;
        }
        if (s.indexOf(SEPARATOR) == -1)
        {
            return s;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++)
        {
            char c = s.charAt(i);

            if (c == SEPARATOR)
            {
                upperCase = true;
            }
            else if (upperCase)
            {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            }
            else
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    /**
     * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
     * 
     * @param str 指定字符串
     * @param strs 需要检查的字符串数组
     * @return 是否匹配
     */
    public static boolean matches(String str, List<String> strs)
    {
        if (isEmpty(str) || isEmpty(strs))
        {
            return false;
        }
        for (String pattern : strs)
        {
            if (isMatch(pattern, str))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断url是否与规则配置: 
     * ? 表示单个字符; 
     * * 表示一层路径内的任意字符串,不可跨层级; 
     * ** 表示任意层路径;
     * 
     * @param pattern 匹配规则
     * @param url 需要匹配的url
     * @return
     */
    public static boolean isMatch(String pattern, String url)
    {
        AntPathMatcher matcher = new AntPathMatcher();
        return matcher.match(pattern, url);
    }

    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj)
    {
        return (T) obj;
    }

    /**
     * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
     * 
     * @param num 数字对象
     * @param size 字符串指定长度
     * @return 返回数字的字符串格式,该字符串为指定长度。
     */
    public static final String padl(final Number num, final int size)
    {
        return padl(num.toString(), size, '0');
    }

    /**
     * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
     * 
     * @param s 原始字符串
     * @param size 字符串指定长度
     * @param c 用于补齐的字符
     * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
     */
    public static final String padl(final String s, final int size, final char c)
    {
        final StringBuilder sb = new StringBuilder(size);
        if (s != null)
        {
            final int len = s.length();
            if (s.length() <= size)
            {
                for (int i = size - len; i > 0; i--)
                {
                    sb.append(c);
                }
                sb.append(s);
            }
            else
            {
                return s.substring(len - size, len);
            }
        }
        else
        {
            for (int i = size; i > 0; i--)
            {
                sb.append(c);
            }
        }
        return sb.toString();
    }
}

6.定时任务异常类

TaskException计划策略异常类,代码如下:

/**
 * 计划策略异常
 * 
 * @author tarzan
 */
public class TaskException extends Exception {
    private static final long serialVersionUID = 1L;

    private Code code;

    public TaskException(String msg, Code code)
    {
        this(msg, code, null);
    }

    public TaskException(String msg, Code code, Exception nestedEx) {
        super(msg, nestedEx);
        this.code = code;
    }

    public Code getCode()
    {
        return code;
    }

    public enum Code {
        TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
    }
}

7.代码中使用的常量类:

任务调度通用常量ScheduleConstants 代码如下:

/**
 * 任务调度通用常量
 * 
 * @author tarzan
 */
public class ScheduleConstants {
    public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";

    /** 执行目标key */
    public static final String TASK_PROPERTIES = "TASK_PROPERTIES";

    /** 默认 */
    public static final String MISFIRE_DEFAULT = "0";

    /** 立即触发执行 */
    public static final String MISFIRE_IGNORE_MISFIRES = "1";

    /** 触发一次执行 */
    public static final String MISFIRE_FIRE_AND_PROCEED = "2";

    /** 不触发立即执行 */
    public static final String MISFIRE_DO_NOTHING = "3";

    public enum Status {
        /**
         * 正常
         */
        NORMAL(0),
        /**
         * 暂停
         */
        PAUSE(1);

        private Integer value;

        Status(Integer value)
        {
            this.value = value;
        }

        public Integer getValue()
        {
            return value;
        }
    }
}

8.自定义定时任务类,示例代码


import org.springblade.core.tool.utils.StringUtil;
import org.springframework.stereotype.Component;

/**
 * 定时任务调度测试
 * 
 * @author tarzan
 */
@Component("ryTask")
public class AppTask {

    public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) {
        System.out.println(StringUtil.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
    }

    public void ryParams(String params)
    {
        System.out.println("执行有参方法:" + params);
    }

    public void ryNoParams()
    {
        System.out.println("执行无参方法");
    }
}

9.页面配置使用,如图所示

 调用字符目标,配置成你自定义的任务类和方法即可!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/778329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言中文件的读写

不争输赢&#xff0c;只问对错 文章目录 一、文件的概述 二、什么是读写文件 三、文件处理的函数 1.文件的打开与关闭 2.文件的顺序读写 文件的顺序读写相关函数 scanf 和 printf 家族的对比及其区分 3.文件的随机读写 文件的随机读写函数 四、文件缓冲区 五…

【el-tree查询并高亮】vue使用el-tree组件,搜索展开并选中对应节点,高亮搜索的关键字,过滤后高亮关键字,两种方法

第一种&#xff08;直接展开并高亮关键字&#xff09; 效果图这样的&#xff0c;会把所有的有这些关键字的节点都展开 代码&#xff1a; 这里的逻辑就是通过递归循环把所有和关键字匹配的节点筛选出来 然后通过setCheckedKeys方法把他展开选中 然后通过filterReal把关键字高亮…

Hadoop——DataGrip连接MySQL|Hive

1、下载 DataGrip下载&#xff1a;DataGrip: The Cross-Platform IDE for Databases & SQL by JetBrains 2、破解 破解链接&#xff1a;https://www.cnblogs.com/xiaohuhu/p/17218430.html 3、启动环境 启动Hadoop&#xff1a;到Hadoop的sbin目录下右键管理员身份运行…

数学建模学习(2):数学建模各类常用的算法全解析

一、评价类算法 常见的评价算法 1.层次分析法 基本思想 是定性与定量相结合的多准则决策、评价方法。将决策的有关元素分解成 目标层、准则层和方案层 &#xff0c;并通过人们的 判断对决策方案的 优劣进行排序 &#xff0c;在此基础上进行定性和定量分析。它把人的思维过程…

预处理详解

目录 一、预定义符号 二、#define 1.认识#define 2.使用#define 2.1#define定义常量 2.2#define定义宏 2.3#define的替换规则 三、宏定义的其他内容 1.#和## 1.1# 1.2## 2.宏的副作用 3.宏的命名规则 4.undef 5.条件编译 一、预定义符号 #include<stdio.h> int…

通过FPGA实现基于RS232串口的指令发送并控制显示器中目标位置

目录 1.算法理论概述 串口通信模块 指令解析模块 位置控制模块 显示器驱动模块 2.部分核心程序 3.算法运行软件版本 4.算法运行效果图预览 5.算法完整程序工程 1.算法理论概述 通过FPGA实现基于RS232串口的指令发送并控制显示器中目标位置是一种常见的应用场景&#x…

双向不循环链表的认识和基础操作(节点创建,头插头删,尾插尾删,输出和逆置)

头定义&#xff1a; typedef char datatype[20];//datatypechar[20] typedef struct Node {//数据域 数据元素datatype data;//指针域 下一个节点地址struct Node* next;//指针域 上一个节点地址struct Node* prev; }*DoubleLink; 创建节点&#xff1a; DoubleLink create_n…

校园电气安全风险分析及预防措施 安科瑞 许敏

摘要:校园属于人员密集场所&#xff0c;若安全风险排查、管控不到位&#xff0c;可能导致安全事故发生&#xff0c;造成严重事故后果。校园电气设备设施引起的电气火灾和触电等事故&#xff0c;是构成校园安全威胁之一&#xff0c;笔者通过对校园发生的电气安全事故案例原因分析…

一次线上OOM问题的个人复盘

我们一个java服务上线后&#xff0c;偶尔会发生内存OOM(Out Of Memory)问题&#xff0c;但由于OOM导致服务不响应请求&#xff0c;健康检查多次不通过&#xff0c;最后部署平台kill了java进程&#xff0c;这导致定位这次OOM问题也变得困难起来。 最终&#xff0c;在多次review代…

react目录结构

比较全面的react目录结构。 目录详解 assets&#xff1a;放置原始资源文件。 components&#xff1a;存放全局组件。 contants&#xff1a;常量文件夹&#xff0c;存放常量。 i18n&#xff1a;i18n国际化&#xff0c;各种语言的翻译。 pages&#xff1a;页面文件夹。 r…

es添加索引命令行和浏览器添加索引--图文详解

一、添加索引 创建索引 curl -X PUT "localhost:9200/my-index-00001?pretty" 获取索引 curl -X GET "localhost:9200/my-index-000001?pretty" 获取全部的索引 curl -X GET "http://localhost:9200/_cat/indices?v" 获取索引映射 cur…

2023 Pycharm 给项目配置解释器 基于已经创建的conda虚拟环境

我在2019年开始使用Pycharm作为python的IDE&#xff0c;最近配置解释器时&#xff0c;法线网上的方法大概过时了&#xff0c;自己尝试了好多次才发现新版本的Pycharm的解释配置方法&#xff0c;故记于此 背景描述&#xff1a; 我是用conda管理环境的&#xff0c;我已经创建好一…

C++ 单例模式(介绍+实现)

文章目录 一. 设计模式二. 单例模式三. 饿汉模式四. 懒汉模式结束语 一. 设计模式 单例模式是一种设计模式 设计模式(Design Pattern)是一套被反复使用&#xff0c;多数人知晓的&#xff0c;经过分类的&#xff0c;代码设计经验的总结。 为什么要有设计模式 就像人类历史发展会…

Docker容器网络和资源管理控制

Docker容器网络 一、Docker 网络实现原理二、Docker 的网络模式网络模式详解&#xff1a;①host模式②container模式③none模式④bridge模式⑤自定义网络 三、资源控制Ⅰ、CPU资源控制Ⅱ、对内存使用的限制Ⅲ、对磁盘IO配额控制&#xff08;blkio&#xff09;的限制 一、Docker…

前端vue uni-app仿美团下拉框下拉筛选组件

在前端Web开发中&#xff0c;下拉筛选功能是一种非常常见的交互方式&#xff0c;它可以帮助用户快速选择所需的选项。本文将介绍如何利用Vue.js和uni-app框架来实现一个高效的下拉筛选功能。通过使用这两个强大的前端框架&#xff0c;我们可以轻松地创建具有响应式用户操作的下…

Hbuildx下载内置浏览器失败

问题描述 刚开始接触Hbulidx&#xff0c;在下载内置浏览器时&#xff0c;报错 " Hbulidx内置浏览器下载失败 " 原因分析 从网上搜索相关的解决方法&#xff0c;一般都是说检查网络&#xff0c;或者关闭杀毒软件。这并没有解决问题&#xff0c;所以&#xff0c;我就…

【C++】STL之容器适配器——使用deque适配stack和queue

个人主页&#xff1a;&#x1f35d;在肯德基吃麻辣烫 分享一句喜欢的话&#xff1a;热烈的火焰&#xff0c;冰封在最沉默的火山深处。 文章目录 前言一、什么是容器适配器&#xff1f;二、stack的基本函数和模拟实现三、queue的基本函数和模拟实现 四、deque4.1deque的底层结构…

如何解决 Git 合并冲突

在遇到合并冲突时&#xff0c;请不要惊慌。通过一些娴熟的技巧协商&#xff0c;你可以解决任何冲突。 假设你和我正在共同编辑同一个名称为 index.html 的文件。我对文件进行了修改&#xff0c;进行了提交&#xff0c;并将更改推送到 Git 远程仓库。你也对同一个文件进行了修改…

K8S初级入门系列之六-控制器(RC/RS/Deployment)

一、前言 在第一章我们了解到&#xff0c;如果master节点是一个大脑&#xff0c;那么控制器组件就是大脑的总管&#xff0c;用来控制Pod的状态和行为。今天我们就来认识弹性扩缩容相关的控制器ReplicationController&#xff0c;ReplicaSet&#xff0c;Deployment。 二、Repli…

(汽车MCU)FS32K148UJT0VLLT、FS32K148HAT0MLLT、FS32K148UJT0VLUT基于32位Cortex-M4F内核 架构图

S32K 32位汽车通用微控制器 (MCU) 是一系列符合AEC-Q100标准、基于32位ARM Cortex-M4F内核的可扩展MCU&#xff0c;适用于通用汽车和高可靠性工业应用。这些系列提供具有可扩展性的软硬件兼容系列&#xff0c;有多种性能、存储器和特性可供选择。这些MCU提供ISO、CAN FD、CSEc硬…