springboot定时器的使用与源码分析

Posted Fire king

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot定时器的使用与源码分析相关的知识,希望对你有一定的参考价值。


小项目相关依赖

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
		<dependency>
			<groupId>org.quartz-scheduler</groupId>
			<artifactId>quartz</artifactId>
			<version>2.3.2</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-context-support -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>5.3.13</version>
		</dependency>

		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus</artifactId>
			<version>3.0.5</version>
		</dependency>
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.7.12</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.20</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
			<version>3.11</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/eu.bitwalker/UserAgentUtils -->
		<dependency>
			<groupId>eu.bitwalker</groupId>
			<artifactId>UserAgentUtils</artifactId>
			<version>1.21</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.hibernate.validator</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>6.1.6.Final</version>
		</dependency>

1.线程池高端配置:

1.1.yml

task:
  pool:
    # 核心线程池大小
    core-pool-size: 10
    # 最大线程数
    max-pool-size: 30
    # 活跃时间
    keep-alive-seconds: 60
    # 队列容量
    queue-capacity: 50

1.2.线程池配置属性类

/**
 * 线程池配置属性类
 * @author https://juejin.im/entry/5abb8f6951882555677e9da2
 * @date 2019年10月31日14:58:18
 */

@Data
@Component
@ConfigurationProperties(prefix = "task.pool")
public class AsyncTaskProperties 

    private int corePoolSize;

    private int maxPoolSize;

    private int keepAliveSeconds;

    private int queueCapacity;

1.3. 用于获取自定义线程池

/**
 * 用于获取自定义线程池
 * @author hupeng
 * @date 2019年10月31日18:16:47
 */

public class ThreadPoolExecutorUtil 

    public static ThreadPoolExecutor getPoll() 
        AsyncTaskProperties properties = SpringContextHolder.getBean(AsyncTaskProperties.class);
        return new ThreadPoolExecutor(
                properties.getCorePoolSize(),
                properties.getMaxPoolSize(),
                properties.getKeepAliveSeconds(),
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(properties.getQueueCapacity()),
                new TheadFactoryName()
        );
    

1.4. 自定义线程名称

/**
 * 自定义线程名称
 * @author hupeng
 * @date 2019年10月31日17:49:55
 */

@Component
public class TheadFactoryName implements ThreadFactory 

    private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;

    public TheadFactoryName() 
        this("el-pool");
    

    private TheadFactoryName(String name) 
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                Thread.currentThread().getThreadGroup();
        //此时namePrefix就是 name + 第几个用这个工厂创建线程池的
        this.namePrefix = name +
                POOL_NUMBER.getAndIncrement();
    

    @Override
    public Thread newThread(Runnable r) 
        //此时线程的名字 就是 namePrefix + -thread- + 这个线程池中第几个执行的线程
        Thread t = new Thread(group, r,
                namePrefix + "-thread-" + threadNumber.getAndIncrement(),
                0);
        if (t.isDaemon()) 
            t.setDaemon(false);
        
        if (t.getPriority() != Thread.NORM_PRIORITY) 
            t.setPriority(Thread.NORM_PRIORITY);
        
        return t;
    

2.定时器高端定制

2.1.定时任务实体类:

@Data
@TableName("quartz_job")
public class QuartzJob implements Serializable 

    public static final String JOB_KEY = "JOB_KEY";


/** 定时任务ID */

    @TableId
    private Long id;



/** Spring Bean名称 */

    private String beanName;



/** cron 表达式 */

    private String cronExpression;



/** 状态:1暂停、0启用 */

    private Boolean isPause;



/** 任务名称 */

    private String jobName;



/** 方法名称 */

    private String methodName;



/** 参数 */

    private String params;



/** 备注 */
    private String remark;


/** 创建时间  */

    @TableField(fill = FieldFill.INSERT)
    private Timestamp createTime;

    public void copy(QuartzJob source) 
        BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true));
    

2.2.为获取环境的类提供获取bean的上下文:

@Slf4j
public class SpringContextHolder implements ApplicationContextAware, DisposableBean 

    private static ApplicationContext applicationContext = null;

    /**
     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) 
        assertContextInjected();
        return (T) applicationContext.getBean(name);
    

    /**
     * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    public static <T> T getBean(Class<T> requiredType) 
        assertContextInjected();
        return applicationContext.getBean(requiredType);
    

    /**
     * 检查ApplicationContext不为空.
     */
    private static void assertContextInjected() 
        if (applicationContext == null) 
            throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
                    ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
        
    

    /**
     * 清除SpringContextHolder中的ApplicationContext为Null.
     */
    private static void clearHolder() 
        log.debug("清除SpringContextHolder中的ApplicationContext:"
                + applicationContext);
        applicationContext = null;
    

    @Override
    public void destroy() 
        SpringContextHolder.clearHolder();
    

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException 
        if (SpringContextHolder.applicationContext != null) 
            log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
        
        SpringContextHolder.applicationContext = applicationContext;
    


2.3.为执行任务的类获取环境:

/**
 * 执行定时任务
 * @author /
 */
@Slf4j
public class QuartzRunnable implements Callable 

    private Object target;
    private Method method;
    private String params;

    QuartzRunnable(String beanName, String methodName, String params)
            throws NoSuchMethodException, SecurityException 
        this.target = SpringContextHolder.getBean(beanName);
        this.params = params;

        if (StringUtils.isNotBlank(params)) 
            this.method = target.getClass().getDeclaredMethod(methodName, String.class);
         else 
            this.method = target.getClass().getDeclaredMethod(methodName);
        
    

    @Override
    public Object call() throws Exception 
        ReflectionUtils.makeAccessible(method);
        if (StringUtils.isNotBlank(params)) 
            method.invoke(target, params);
         else 
            method.invoke(target);
        
        return null;
    

2.4.真正执行任务的类:

@Async
public class ExecutionJob extends QuartzJobBean 

    private Logger logger = LoggerFactory.getLogger(this.getClass());


/** 该处仅供参考 */

    private final static ThreadPoolExecutor EXECUTOR = ThreadPoolExecutorUtil.getPoll();

    @Override
    @SuppressWarnings("unchecked")
    protected void executeInternal(JobExecutionContext context) 
        //继承了QuartzJob,也就拥有QuartzJob.JOB_KEY属性,通过context就能获取与QuartzManage中的QuartzJob
        QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY);
        // 获取spring bean
        TestQuartz quartzJobService = SpringContextHolder.getBean(TestQuartz.class);
        long startTime = System.currentTimeMillis();
        try 
            // 执行任务
            logger.info("任务准备执行,任务名称:", quartzJob.getJobName());
            QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(),
                    quartzJob.getParams());
            Future<?> future = EXECUTOR.submit(task);
            future.get();
            long times = System.currentTimeMillis() - startTime;

            // 任务状态

            logger.info("任务执行完毕,任务名称: 总共耗时: 毫秒", quartzJob.getJobName(), times);
         catch (Exception e) 
            logger.error("任务执行失败,任务名称:" + quartzJob.getJobName(), e);
            long times = (System.currentTimeMillis() - startTime);
            quartzJob.setIsPause(false);
            //更新状态
            //quartzJobService.updateIsPause(quartzJob);
         finally 
            //quartzLogService.save(log);
        
    

2.5.任务管理类:

专门暴露给客户端用的,主要用到addJob(QuartzJob quartzJob)这个方法

@Slf4j
@Component
public class QuartzManage 

    private static final String JOB_NAME = "TASK_";

    @Resource(name = "scheduler")
    private Scheduler scheduler;

    public void addJob(QuartzJob quartzJob) 
        try 
            // 构建job信息
            /*
            ExecutionJob.class必须继承或间接继承Job类
            * */
            JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class).
                    withIdentity(JOB_NAME + quartzJob.getId()).build();

            //通过触发器名和cron 表达式创建 Trigger
            Trigger cronTrigger = newTrigger()
                    .withIdentity(JOB_NAME + quartzJob.getId())
                    .startNow()
                    .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()))
                    .build();

            cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob);

            //重置启动时间
            ((5w字SpringBoot源码分析

5w字SpringBoot源码分析

UEFI.源码分析.DXE的异步事件服务.第三部分.定时器与时钟中断

UEFI.源码分析.DXE的异步事件服务.第三部分.定时器与时钟中断

TencentOS tiny深度源码分析——软件定时器

微服务架构 *2.5 Nacos 长轮询定时机制的源码分析