亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Boot支持Crontab任務(wù)改造的方法

 更新時(shí)間:2019年01月20日 14:18:12   作者:廣訓(xùn)  
這篇文章主要介紹了Spring Boot支持Crontab任務(wù)改造的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

在以往的 Tomcat 項(xiàng)目中,一直習(xí)慣用 Ant 打包,使用 build.xml 配置,通過(guò) ant -buildfile 的方式在機(jī)器上執(zhí)行定時(shí)任務(wù)。雖然 Spring 本身支持定時(shí)任務(wù),但都是服務(wù)一直運(yùn)行時(shí)支持。其實(shí)在項(xiàng)目中,大多數(shù)定時(shí)任務(wù),還是借助 Linux Crontab 來(lái)支持,需要時(shí)運(yùn)行即可,不需要一直占用機(jī)器資源。但 Spring Boot 項(xiàng)目或者普通的 jar 項(xiàng)目,就沒(méi)這么方便了。

Spring Boot 提供了類(lèi)似 CommandLineRunner 的方式,很好的執(zhí)行常駐任務(wù);也可以借助 ApplicationListener 和 ContextRefreshedEvent 等事件來(lái)做很多事情。借助該容器事件,一樣可以做到類(lèi)似 Ant 運(yùn)行的方式來(lái)運(yùn)行定時(shí)任務(wù),當(dāng)然需要做一些項(xiàng)目改動(dòng)。

1. 監(jiān)聽(tīng)目標(biāo)對(duì)象

借助容器刷新事件來(lái)監(jiān)聽(tīng)目標(biāo)對(duì)象即可,可以認(rèn)為,定時(shí)任務(wù)其實(shí)每次只是執(zhí)行一種操作而已。

比如這是一個(gè)寫(xiě)好的例子,注意不要直接用 @Service 將其放入容器中,除非容器本身沒(méi)有其它自動(dòng)運(yùn)行的事件。

package com.github.zhgxun.learn.common.task;

import com.github.zhgxun.learn.common.task.annotation.ScheduleTask;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 不自動(dòng)加入容器, 用于區(qū)分是否屬于任務(wù)啟動(dòng), 否則放入容器中, Spring 無(wú)法選擇性執(zhí)行
 * 需要根據(jù)特殊參數(shù)在啟動(dòng)時(shí)注入
 * 該監(jiān)聽(tīng)器本身不能訪(fǎng)問(wèn)容器變量, 如果需要訪(fǎng)問(wèn), 需要從上下文中獲取對(duì)象實(shí)例后方可繼續(xù)訪(fǎng)問(wèn)實(shí)例信息
 * 如果其它類(lèi)中啟動(dòng)了多線(xiàn)程, 是無(wú)法接管異常拋出的, 需要子線(xiàn)程中正確處理退出操作
 * 該監(jiān)聽(tīng)器最好不用直接做線(xiàn)程操作, 子類(lèi)的實(shí)現(xiàn)不干預(yù)
 */
@Slf4j
public class TaskApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
  /**
   * 任務(wù)啟動(dòng)監(jiān)聽(tīng)類(lèi)標(biāo)識(shí), 啟動(dòng)時(shí)注入
   * 即是 java -Dspring.task.class=com.github.zhgxun.learn.task.TestTask -jar learn.jar
   */
  private static final String SPRING_TASK_CLASS = "spring.task.class";

  /**
   * 支持該注解的方法個(gè)數(shù), 目前僅一個(gè)
   * 可以理解為控制臺(tái)一次執(zhí)行一個(gè)類(lèi), 依賴(lài)的任務(wù)應(yīng)該通過(guò)其它方式控制依賴(lài)
   */
  private static final int SUPPORT_METHOD_COUNT = 1;

  /**
   * 保存當(dāng)前容器運(yùn)行上下文
   */
  private ApplicationContext context;

  /**
   * 監(jiān)聽(tīng)容器刷新事件
   *
   * @param event 容器刷新事件
   */
  @Override
  @SuppressWarnings("unchecked")
  public void onApplicationEvent(ContextRefreshedEvent event) {
    context = event.getApplicationContext();
    // 不存在時(shí)可能為正常的容器啟動(dòng)運(yùn)行, 無(wú)需關(guān)心
    String taskClass = System.getProperty(SPRING_TASK_CLASS);
    log.info("ScheduleTask spring task Class: {}", taskClass);
    if (taskClass != null) {
      try {
        // 獲取類(lèi)字節(jié)碼文件
        Class clazz = findClass(taskClass);

        // 嘗試從內(nèi)容上下文中獲取已加載的目標(biāo)類(lèi)對(duì)象實(shí)例, 這個(gè)類(lèi)實(shí)例是已經(jīng)加載到容器內(nèi)的對(duì)象實(shí)例, 即可以獲取類(lèi)的信息
        Object object = context.getBean(clazz);

        Method method = findMethod(object);

        log.info("start to run task Class: {}, Method: {}", taskClass, method.getName());
        invoke(method, object);
      } catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) {
        e.printStackTrace();
      } finally {
        // 需要確保容器正常出發(fā)停止事件, 否則容器會(huì)僵尸卡死
        shutdown();
      }
    }
  }

  /**
   * 根據(jù)class路徑名稱(chēng)查找類(lèi)文件
   *
   * @param clazz 類(lèi)名稱(chēng)
   * @return 類(lèi)對(duì)象
   * @throws ClassNotFoundException ClassNotFoundException
   */
  private Class findClass(String clazz) throws ClassNotFoundException {
    return Class.forName(clazz);
  }

  /**
   * 獲取目標(biāo)對(duì)象中符合條件的方法
   *
   * @param object 目標(biāo)對(duì)象實(shí)例
   * @return 符合條件的方法
   */
  private Method findMethod(Object object) {
    Method[] methods = object.getClass().getDeclaredMethods();
    List<Method> schedules = Stream.of(methods)
        .filter(method -> method.isAnnotationPresent(ScheduleTask.class))
        .collect(Collectors.toList());
    if (schedules.size() != SUPPORT_METHOD_COUNT) {
      throw new IllegalStateException("only one method should be annotated with @ScheduleTask, but found "
          + schedules.size());
    }
    return schedules.get(0);
  }

  /**
   * 執(zhí)行目標(biāo)對(duì)象方法
   *
   * @param method 目標(biāo)方法
   * @param object 目標(biāo)對(duì)象實(shí)例
   * @throws IllegalAccessException  IllegalAccessException
   * @throws InvocationTargetException InvocationTargetException
   */
  private void invoke(Method method, Object object) throws IllegalAccessException, InvocationTargetException {
    method.invoke(object);
  }

  /**
   * 執(zhí)行完畢退出運(yùn)行容器, 并將返回值交給執(zhí)行環(huán)節(jié), 比如控制臺(tái)等
   */
  private void shutdown() {
    log.info("shutdown ...");
    System.exit(SpringApplication.exit(context));
  }
}

其實(shí)該處僅需要啟動(dòng)執(zhí)行即可,容器啟動(dòng)完畢事件也是可以的。

2. 標(biāo)識(shí)目標(biāo)方法

目標(biāo)方法的標(biāo)識(shí),最方便的是使用注解標(biāo)注。

package com.github.zhgxun.learn.common.task.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ScheduleTask {
}

3. 編寫(xiě)任務(wù)

package com.github.zhgxun.learn.task;

import com.github.zhgxun.learn.common.task.annotation.ScheduleTask;
import com.github.zhgxun.learn.service.first.LaunchInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class TestTask {

  @Autowired
  private LaunchInfoService launchInfoService;

  @ScheduleTask
  public void test() {
    log.info("Start task ...");
    log.info("LaunchInfoList: {}", launchInfoService.findAll());

    log.info("模擬啟動(dòng)線(xiàn)程操作");
    for (int i = 0; i < 5; i++) {
      new MyTask(i).start();
    }

    try {
      TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

class MyTask extends Thread {
  private int i;
  private int j;
  private String s;

  public MyTask(int i) {
    this.i = i;
  }

  @Override
  public void run() {
    super.run();
    System.out.println("第 " + i + " 個(gè)線(xiàn)程啟動(dòng)..." + Thread.currentThread().getName());
    if (i == 2) {
      throw new RuntimeException("模擬運(yùn)行時(shí)異常");
    }
    if (i == 3) {
      // 除數(shù)不為0
      int a = i / j;
    }
    // 未對(duì)字符串對(duì)象賦值, 獲取長(zhǎng)度報(bào)空指針錯(cuò)誤
    if (i == 4) {
      System.out.println(s.length());
    }
  }
}

4. 啟動(dòng)改造

啟動(dòng)時(shí)需要做一些調(diào)整,即跟普通的啟動(dòng)區(qū)分開(kāi)。這也是為什么不要把監(jiān)聽(tīng)目標(biāo)對(duì)象直接放入容器中的原因,在這里顯示添加到容器中,這樣就不影響項(xiàng)目中類(lèi)似 CommandLineRunner 的功能,畢竟這種功能是容器啟動(dòng)完畢就能運(yùn)行的。如果要改造,會(huì)涉及到很多硬編碼。

package com.github.zhgxun.learn;

import com.github.zhgxun.learn.common.task.TaskApplicationListener;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;

@SpringBootApplication
public class LearnApplication {

  public static void main(String[] args) {
    SpringApplicationBuilder builder = new SpringApplicationBuilder(LearnApplication.class);
    // 根據(jù)啟動(dòng)注入?yún)?shù)判斷是否為任務(wù)動(dòng)作即可, 否則不干預(yù)啟動(dòng)
    if (System.getProperty("spring.task.class") != null) {
      builder.listeners(new TaskApplicationListener()).run(args);
    } else {
      builder.run(args);
    }
  }
}

5. 啟動(dòng)注入

-Dspring.task.class 即是啟動(dòng)注入標(biāo)識(shí),當(dāng)然這個(gè)標(biāo)識(shí)不要跟默認(rèn)的參數(shù)混淆,需要區(qū)分開(kāi),否則可能始終獲取到系統(tǒng)參數(shù),而無(wú)法獲取用戶(hù)參數(shù)。

java -Dspring.task.class=com.github.zhgxun.learn.task.TestTask -jar target/learn.jar

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java深入講解異常處理try?catch的使用

    Java深入講解異常處理try?catch的使用

    這篇文章主要介紹了Java異常處理機(jī)制try?catch流程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-06-06
  • springboot項(xiàng)目啟動(dòng)的時(shí)候,運(yùn)行main方法報(bào)錯(cuò)NoClassDefFoundError問(wèn)題

    springboot項(xiàng)目啟動(dòng)的時(shí)候,運(yùn)行main方法報(bào)錯(cuò)NoClassDefFoundError問(wèn)題

    這篇文章主要介紹了springboot項(xiàng)目啟動(dòng)的時(shí)候,運(yùn)行main方法報(bào)錯(cuò)NoClassDefFoundError問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Mybatis如何動(dòng)態(tài)創(chuàng)建表

    Mybatis如何動(dòng)態(tài)創(chuàng)建表

    這篇文章主要介紹了Mybatis如何動(dòng)態(tài)創(chuàng)建表問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • Java 異常詳解

    Java 異常詳解

    本文主要介紹了異常與錯(cuò)誤的區(qū)別,異常的體現(xiàn)分類(lèi),異常的處理機(jī)制,如何自定義異常等,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02
  • JavaWeb中的常用的請(qǐng)求傳參注解說(shuō)明

    JavaWeb中的常用的請(qǐng)求傳參注解說(shuō)明

    這篇文章主要介紹了JavaWeb中的常用的請(qǐng)求傳參注解說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • 詳解SpringBoot和SpringBatch 使用

    詳解SpringBoot和SpringBatch 使用

    Spring Batch 是一個(gè)輕量級(jí)的、完善的批處理框架,旨在幫助企業(yè)建立健壯、高效的批處理應(yīng)用。這篇文章主要介紹了詳解SpringBoot和SpringBatch 使用,需要的朋友可以參考下
    2018-07-07
  • SpringBoot YAML語(yǔ)法基礎(chǔ)詳細(xì)整理

    SpringBoot YAML語(yǔ)法基礎(chǔ)詳細(xì)整理

    YAML 是 “YAML Ain’t Markup Language”(YAML 不是一種標(biāo)記語(yǔ)言)的遞歸縮寫(xiě)。在開(kāi)發(fā)的這種語(yǔ)言時(shí),YAML 的意思其實(shí)是:“Yet Another Markup Language”(仍是一種標(biāo)記語(yǔ)言),本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-10-10
  • Spring如何解決循環(huán)依賴(lài)的問(wèn)題

    Spring如何解決循環(huán)依賴(lài)的問(wèn)題

    這篇文章主要介紹了Spring是如何解決循環(huán)依賴(lài)的問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 在?Spring?Boot?中使用?Quartz?調(diào)度作業(yè)的示例詳解

    在?Spring?Boot?中使用?Quartz?調(diào)度作業(yè)的示例詳解

    這篇文章主要介紹了在?Spring?Boot?中使用?Quartz?調(diào)度作業(yè)的示例詳解,在本文中,我們將看看如何使用Quartz框架來(lái)調(diào)度任務(wù),Quartz支持在特定時(shí)間運(yùn)行作業(yè)、重復(fù)作業(yè)執(zhí)行、將作業(yè)存儲(chǔ)在數(shù)據(jù)庫(kù)中以及Spring集成,需要的朋友可以參考下
    2022-07-07
  • HttpUtils 發(fā)送http請(qǐng)求工具類(lèi)(實(shí)例講解)

    HttpUtils 發(fā)送http請(qǐng)求工具類(lèi)(實(shí)例講解)

    下面小編就為大家?guī)?lái)一篇HttpUtils 發(fā)送http請(qǐng)求工具類(lèi)(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07

最新評(píng)論