最近在做一個(gè)“溫濕度控制”的項(xiàng)目,項(xiàng)目要求通過用戶設(shè)定的溫濕度數(shù)值和實(shí)時(shí)采集到的數(shù)值進(jìn)行比對(duì)分析,因?yàn)閿?shù)據(jù)的對(duì)比與分析是一個(gè)通過前端頁面控制的定時(shí)任務(wù),經(jīng)理要求在用戶開啟定時(shí)任務(wù)時(shí),單獨(dú)開啟一個(gè)線程進(jìn)行數(shù)據(jù)的對(duì)比分析,并將采集到的溫濕度數(shù)值存入數(shù)據(jù)庫中的歷史數(shù)據(jù)表,按照我們正常的邏輯應(yīng)該是用戶在請(qǐng)求開啟定時(shí)任務(wù)時(shí),前端頁面通過調(diào)用后端接口,創(chuàng)建一個(gè)新的線程來執(zhí)行定時(shí)任務(wù),然后在線程類中使用 @autowired
注解注入保存歷史數(shù)據(jù)的service層,在線程類中調(diào)用service層保存歷史數(shù)據(jù)的方法實(shí)現(xiàn)溫濕度數(shù)據(jù)的保存,這時(shí)就出現(xiàn)了一個(gè)很尷尬的問題,在新開啟的線程中使用 @autowired
注解無法注入需要的bean(即:保存歷史數(shù)據(jù)的service層),程序一直在報(bào) nullpointerexception
。
這是controller層,方法 startexperiment 和 stopexperiment 分別是開始定時(shí)任務(wù)和停止定時(shí)任務(wù)的方法,getdata方法不屬于本次討論范圍,不用管
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
|
package com.backstage.controller; import com.alibaba.fastjson.jsonobject; import com.backstage.entity.jsonresponse; import com.backstage.entity.threshold; import com.backstage.service.mainpageservice; import org.springframework.beans.factory.annotation.autowired; import org.springframework.web.bind.annotation.requestmapping; import org.springframework.web.bind.annotation.restcontroller; import javax.servlet.http.httpservletrequest; /** * @projectname: * @package: com.backstage.controller * @classname: mainpagecontroller * @description: 主頁面相關(guān)操作控制器 * @author: wangzhilong * @createdate: 2018/8/29 9:49 * @version: 1.0 */ @restcontroller @requestmapping ( "/main" ) public class mainpagecontroller { @autowired private mainpageservice mainpageservice; /** * 開始實(shí)驗(yàn) * * @param threshold */ @requestmapping ( "/startexperiment" ) public jsonresponse startexperiment(httpservletrequest request, threshold threshold) { return mainpageservice.startexperiment(request, threshold); } /** * 停止實(shí)驗(yàn) */ @requestmapping ( "/stopexperiment" ) public jsonresponse stopexperiment() { return mainpageservice.stopexperiment(); } /** * 獲取實(shí)時(shí)數(shù)據(jù) * * @return */ @requestmapping ( "/getdata" ) public jsonobject getdata() { return null ; } } |
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
|
package com.backstage.service; import com.alibaba.fastjson.jsonobject; import com.backstage.entity.jsonresponse; import com.backstage.entity.threshold; import javax.servlet.http.httpservletrequest; /** * @projectname: * @package: com.backstage.service * @classname: mainpageservice * @description: 主頁面相關(guān)操作業(yè)務(wù)層接口 * @author: wangzhilong * @createdate: 2018/8/29 9:51 * @version: 1.0 */ public interface mainpageservice { /** * 開始實(shí)驗(yàn) * * @param threshold */ jsonresponse startexperiment(httpservletrequest request, threshold threshold); /** * 停止實(shí)驗(yàn) */ jsonresponse stopexperiment(); /** * 獲取實(shí)時(shí)數(shù)據(jù) * * @return */ jsonobject getdata(); } |
service 層實(shí)現(xiàn)類代碼,關(guān)于springboot項(xiàng)目使用多線程進(jìn)行業(yè)務(wù)處理不屬于本章節(jié)的討論范圍,如有需要,請(qǐng)留言,我會(huì)在看到留言后第一時(shí)間更新相關(guān)技術(shù)文章,由于這里刪除了一些與本章節(jié)無關(guān)的代碼,如果復(fù)制到開發(fā)工具內(nèi)有報(bào)錯(cuò)問題,麻煩大家提醒我一下,以便修改,非常感謝
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
|
package com.backstage.service.impl; import com.alibaba.fastjson.jsonobject; import com.backstage.entity.*; import com.backstage.monitor.timingmonitoring; import com.backstage.service.*; import org.springframework.beans.factory.annotation.autowired; import org.springframework.context.annotation.bean; import org.springframework.scheduling.trigger; import org.springframework.scheduling.triggercontext; import org.springframework.scheduling.concurrent.threadpooltaskscheduler; import org.springframework.scheduling.support.crontrigger; import org.springframework.stereotype.service; import javax.servlet.http.httpservletrequest; import java.text.simpledateformat; import java.util.date; import java.util.list; import java.util.concurrent.scheduledfuture; /** * @projectname: * @package: com.backstage.service.impl * @classname: mainpageserviceimpl * @description: 主頁面相關(guān)操作業(yè)務(wù)層實(shí)現(xiàn)類 * @author: wangzhilong * @createdate: 2018/8/29 9:51 * @version: 1.0 */ @service public class mainpageserviceimpl implements mainpageservice { @autowired private threadpooltaskscheduler threadpooltaskscheduler; private scheduledfuture<?> future2; @bean public threadpooltaskscheduler threadpooltaskscheduler() { return new threadpooltaskscheduler(); } /** * 開始實(shí)驗(yàn) * * @param threshold */ @override public jsonresponse startexperiment(httpservletrequest request, threshold threshold) { timingmonitoring timingmonitoring = new timingmonitoring(); timingmonitoring.setthreshold(threshold, list, experiment.getid(), experimentdata.getid()); future2 = threadpooltaskscheduler.schedule( new timingmonitoring(), new trigger() { @override public date nextexecutiontime(triggercontext triggercontext) { //設(shè)置定時(shí)任務(wù)的執(zhí)行時(shí)間為3秒鐘執(zhí)行一次 return new crontrigger( "0/10 * * * * ?" ).nextexecutiontime(triggercontext); } }); return new jsonresponse( 0 , "開始實(shí)驗(yàn)!" ); } /** * 停止實(shí)驗(yàn) */ @override public jsonresponse stopexperiment() { if (future2 != null ) { experimentservice.upd(gettime()); future2.cancel( true ); } return new jsonresponse( 0 , "結(jié)束實(shí)驗(yàn)!" ); } /** * 獲取實(shí)時(shí)數(shù)據(jù) * * @return */ @override public jsonobject getdata() { return null ; } protected string gettime() { simpledateformat format = new simpledateformat( "yyyy-mm-dd hh:mm:ss" ); return format.format( new date()); } } |
重點(diǎn),線程類代碼,大家注意看,我在代碼最開始使用了spring的 @autowired 注解注入需要的service,可在調(diào)用service中的add方法時(shí),程序報(bào)空指針異常,一直認(rèn)為是add方法或者sql語句有問題,找了一上午,也沒發(fā)現(xiàn)任何問題,后來單獨(dú)調(diào)用這個(gè)add方法是可以正常插入數(shù)據(jù)的,唯獨(dú)在這個(gè)線程類中調(diào)用時(shí)報(bào)錯(cuò),感覺和線程有莫大的關(guān)系,百度一搜,還真找到了,原來,在線程中為了線程安全,是防注入的,沒辦法,要用到這個(gè)類啊。只能從bean工廠里拿個(gè)實(shí)例了,繼續(xù)往下看
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
|
package com.backstage.monitor; import com.backstage.entity.detaileddata; import com.backstage.entity.threshold; import com.backstage.entity.valvevalue; import com.backstage.service.detaileddataservice; import java.text.simpledateformat; import java.util.date; import java.util.list; /** * @projectname: * @package: com.backstage.monitor * @classname: timingmonitoring * @description: 定時(shí)監(jiān)測(cè)溫(濕)度 數(shù)據(jù) * @author: wangzhilong * @createdate: 2018/8/29 10:11 * @version: 1.0 */ public class timingmonitoring implements runnable{ //歷史數(shù)據(jù)業(yè)務(wù)層接口 @autowired public detaileddataservice detaileddataservice; private threshold threshold; //閾值實(shí)體類 private list<valvevalue> settingdata; //設(shè)定的溫濕度數(shù)據(jù) private integer id; //實(shí)驗(yàn)記錄id private integer dataid; //歷史數(shù)據(jù)主表id public void setthreshold(threshold threshold, list<valvevalue> settingdata, integer id, integer dataid) { this .threshold = threshold; this .settingdata = settingdata; this .id = id; this .dataid = dataid; } @override public void run() { //模擬從plc獲取到的數(shù)據(jù) string data = "001,50.5,002,37,003,45.6,004,40,005,55.2,006,58" ; if (data == null || data.trim() == "" ) { return ; //若獲取到的數(shù)據(jù)為空,則直接停止該方法的執(zhí)行 } double temperature = 0.0 ; //溫度 double humidity = 0.0 ; //濕度 integer type = null ; //數(shù)據(jù)類型,1是溫度,2是濕度 //解析數(shù)據(jù),并將數(shù)據(jù)保存到歷史數(shù)據(jù)數(shù)據(jù)庫 string[] str = data.split( "," ); simpledateformat format = new simpledateformat( "yyyy-mm-dd hh:mm:ss:ss" ); for ( int i = 0 ; i < str.length; i++) { if (i == 1 || i == 5 || i == 9 ) { //溫度 type = 1 ; temperature += double .parsedouble(str[i]); //system.out.println("溫度" + i + " -》 " + str[i-1] + ":" + str[i]); detaileddataservice.add( new detaileddata( null , type, double .parsedouble(str[i]), format.format( new date()), str[i - 1 ], dataid)); } if (i == 3 || i == 7 || i == 11 ) { //濕度 type = 2 ; humidity += double .parsedouble(str[i]); //system.out.println("濕度" + i + " -》 " + str[i-1] + ":" + str[i]); detaileddataservice.add( new detaileddata( null , type, double .parsedouble(str[i]), format.format( new date()), str[i - 1 ], dataid)); } } } /** * 獲取當(dāng)前時(shí)間,精確到毫秒 * @return */ protected string gettime() { simpledateformat format = new simpledateformat( "yyyy-mm-dd hh:mm:ss:ss" ); return format.format( new date()); } } |
獲取bean對(duì)象的工具類,既然程序無法通過注解拿到需要的bean,那就只好自己寫個(gè)工具類來獲取嘍,下面是工具類代碼
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
|
package com.backstage.config; import org.springframework.beans.beansexception; import org.springframework.context.applicationcontext; import org.springframework.context.applicationcontextaware; import org.springframework.stereotype.component; /** * @projectname: * @package: com.backstage.config * @classname: applicationcontextprovider * @description: 獲取bean對(duì)象的工具類 * @author: wangzhilong * @createdate: 2018/8/31 13:26 * @version: 1.0 */ /** * author:zhushangjin * date:2018/7/3 */ @component public class applicationcontextprovider implements applicationcontextaware { /** * 上下文對(duì)象實(shí)例 */ private static applicationcontext applicationcontext; @override public void setapplicationcontext(applicationcontext applicationcontext) throws beansexception { this .applicationcontext = applicationcontext; } /** * 獲取applicationcontext * * @return */ public static applicationcontext getapplicationcontext() { return applicationcontext; } /** * 通過name獲取 bean. * * @param name * @return */ public static object getbean(string name) { return getapplicationcontext().getbean(name); } /** * 通過class獲取bean. * * @param clazz * @param <t> * @return */ public static <t> t getbean( class <t> clazz) { return getapplicationcontext().getbean(clazz); } /** * 通過name,以及clazz返回指定的bean * * @param name * @param clazz * @param <t> * @return */ public static <t> t getbean(string name, class <t> clazz) { return getapplicationcontext().getbean(name, clazz); } } |
這樣呢,就可以在線程類中寫一個(gè)無參的構(gòu)造方法,在構(gòu)造方法中,通過調(diào)用工具類中的 getbean() 方法就可以拿到實(shí)例了,程序在調(diào)用這個(gè)線程類時(shí),會(huì)自動(dòng)調(diào)用其無參的構(gòu)造方法,在構(gòu)造方法中我們將需要的bean對(duì)象注入,然后就可以正常使用了,下邊是線程類修改后的代碼,由于別的地方?jīng)]有改動(dòng),所以這里只給大家改動(dòng)的代碼,省得大家看到一大堆代碼頭疼。
1
2
3
4
|
public timingmonitoring() { //new的時(shí)候注入需要的bean this .detaileddataservice = applicationcontextprovider.getbean(detaileddataservice. class ); } |
總結(jié)
以上所述是小編給大家介紹的springboot項(xiàng)目使用多線程處理任務(wù)時(shí)無法通過@autowired注入bean 問題,希望對(duì)大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的!
原文鏈接:https://www.cnblogs.com/xiaolong1996/archive/2018/09/01/9571645.html