一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring Boot支持Crontab任務改造的方法

Spring Boot支持Crontab任務改造的方法

2021-07-10 14:51廣訓 Java教程

這篇文章主要介紹了Spring Boot支持Crontab任務改造的方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

在以往的 tomcat 項目中,一直習慣用 ant 打包,使用 build.xml 配置,通過 ant -buildfile 的方式在機器上執行定時任務。雖然 spring 本身支持定時任務,但都是服務一直運行時支持。其實在項目中,大多數定時任務,還是借助 linux crontab 來支持,需要時運行即可,不需要一直占用機器資源。但 spring boot 項目或者普通的 jar 項目,就沒這么方便了。

spring boot 提供了類似 commandlinerunner 的方式,很好的執行常駐任務;也可以借助 applicationlistener 和 contextrefreshedevent 等事件來做很多事情。借助該容器事件,一樣可以做到類似 ant 運行的方式來運行定時任務,當然需要做一些項目改動。

1. 監聽目標對象

借助容器刷新事件來監聽目標對象即可,可以認為,定時任務其實每次只是執行一種操作而已。

比如這是一個寫好的例子,注意不要直接用 @service 將其放入容器中,除非容器本身沒有其它自動運行的事件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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;
 
/**
 * 不自動加入容器, 用于區分是否屬于任務啟動, 否則放入容器中, spring 無法選擇性執行
 * 需要根據特殊參數在啟動時注入
 * 該監聽器本身不能訪問容器變量, 如果需要訪問, 需要從上下文中獲取對象實例后方可繼續訪問實例信息
 * 如果其它類中啟動了多線程, 是無法接管異常拋出的, 需要子線程中正確處理退出操作
 * 該監聽器最好不用直接做線程操作, 子類的實現不干預
 */
@slf4j
public class taskapplicationlistener implements applicationlistener<contextrefreshedevent> {
  /**
   * 任務啟動監聽類標識, 啟動時注入
   * 即是 java -dspring.task.class=com.github.zhgxun.learn.task.testtask -jar learn.jar
   */
  private static final string spring_task_class = "spring.task.class";
 
  /**
   * 支持該注解的方法個數, 目前僅一個
   * 可以理解為控制臺一次執行一個類, 依賴的任務應該通過其它方式控制依賴
   */
  private static final int support_method_count = 1;
 
  /**
   * 保存當前容器運行上下文
   */
  private applicationcontext context;
 
  /**
   * 監聽容器刷新事件
   *
   * @param event 容器刷新事件
   */
  @override
  @suppresswarnings("unchecked")
  public void onapplicationevent(contextrefreshedevent event) {
    context = event.getapplicationcontext();
    // 不存在時可能為正常的容器啟動運行, 無需關心
    string taskclass = system.getproperty(spring_task_class);
    log.info("scheduletask spring task class: {}", taskclass);
    if (taskclass != null) {
      try {
        // 獲取類字節碼文件
        class clazz = findclass(taskclass);
 
        // 嘗試從內容上下文中獲取已加載的目標類對象實例, 這個類實例是已經加載到容器內的對象實例, 即可以獲取類的信息
        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 {
        // 需要確保容器正常出發停止事件, 否則容器會僵尸卡死
        shutdown();
      }
    }
  }
 
  /**
   * 根據class路徑名稱查找類文件
   *
   * @param clazz 類名稱
   * @return 類對象
   * @throws classnotfoundexception classnotfoundexception
   */
  private class findclass(string clazz) throws classnotfoundexception {
    return class.forname(clazz);
  }
 
  /**
   * 獲取目標對象中符合條件的方法
   *
   * @param object 目標對象實例
   * @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);
  }
 
  /**
   * 執行目標對象方法
   *
   * @param method 目標方法
   * @param object 目標對象實例
   * @throws illegalaccessexception  illegalaccessexception
   * @throws invocationtargetexception invocationtargetexception
   */
  private void invoke(method method, object object) throws illegalaccessexception, invocationtargetexception {
    method.invoke(object);
  }
 
  /**
   * 執行完畢退出運行容器, 并將返回值交給執行環節, 比如控制臺等
   */
  private void shutdown() {
    log.info("shutdown ...");
    system.exit(springapplication.exit(context));
  }
}

其實該處僅需要啟動執行即可,容器啟動完畢事件也是可以的。

2. 標識目標方法

目標方法的標識,最方便的是使用注解標注。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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. 編寫任務

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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("模擬啟動線程操作");
    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 + " 個線程啟動..." + thread.currentthread().getname());
    if (i == 2) {
      throw new runtimeexception("模擬運行時異常");
    }
    if (i == 3) {
      // 除數不為0
      int a = i / j;
    }
    // 未對字符串對象賦值, 獲取長度報空指針錯誤
    if (i == 4) {
      system.out.println(s.length());
    }
  }
}

4. 啟動改造

啟動時需要做一些調整,即跟普通的啟動區分開。這也是為什么不要把監聽目標對象直接放入容器中的原因,在這里顯示添加到容器中,這樣就不影響項目中類似 commandlinerunner 的功能,畢竟這種功能是容器啟動完畢就能運行的。如果要改造,會涉及到很多硬編碼。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
    // 根據啟動注入參數判斷是否為任務動作即可, 否則不干預啟動
    if (system.getproperty("spring.task.class") != null) {
      builder.listeners(new taskapplicationlistener()).run(args);
    } else {
      builder.run(args);
    }
  }
}

5. 啟動注入

-dspring.task.class 即是啟動注入標識,當然這個標識不要跟默認的參數混淆,需要區分開,否則可能始終獲取到系統參數,而無法獲取用戶參數。

?
1
java -dspring.task.class=com.github.zhgxun.learn.task.testtask -jar target/learn.jar

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://segmentfault.com/a/1190000017946999

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 青苹果乐园影院免费观看完整版 | 国产精品探花一区在线观看 | 国内精品久久久久影院男同志 | 国产精品亚洲精品日韩已方 | 午夜福利理论片高清在线 | 饭冈加奈子在线播放观看 | 91亚洲视频在线观看 | 男人摸女人下面 | 日韩欧美亚洲每日更新网 | 亚洲图片综合区 | 俄罗斯一级成人毛片 | 午夜久久久久久亚洲国产精品 | 女人张开腿让男人桶视频免费大全 | 国产拍拍拍免费专区在线观看 | 亚洲精品高清中文字幕完整版 | 欧美特级特黄a大片免费 | 欧美一区二区福利视频 | 星球大战成人h无删减版 | 毛片大全免费看 | 天若有情1992国语版完整版 | 无限在线看免费视频大全 | 久久99精国产一区二区三区四区 | 99热这里有精品 | 成人区精品一区二区毛片不卡 | 免费高清视频免费观看 | 国产精品视频久久 | chinese国产人妖videos | 果冻传媒mv在线观看入口免费 | 亚洲成A人片在线观看中文L | 欧美日韩精品乱国产 | 国产精品亚洲片夜色在线 | 国产成人亚洲精品91专区手机 | 秋葵污视频 | 国产亚洲sss在线播放 | 91外围| 性xxxx直播放免费 | 亚洲天堂视频在线免费观看 | 国产亚洲精品自在线亚洲情侣 | 久久精品男人影院 | 手机看片自拍自自拍日韩免费 | 2020最新韩国理论三级0k |