iT邦幫忙

0

spring boot @Autowired 疑問(JdbcTemplate+quartz)

  • 分享至 

  • xImage

test

JobController.java

@RequestMapping(path = "/testjob", method = RequestMethod.POST)

這個主要是呼叫 HelloJob.java 去執行每 5秒取 DB 資料印出來這樣的功能

[========]

HelloJob.java ↓↓↓

test

但是丟 POST/testjob 過去,都會出現 NullPointerException 的錯誤。找了一下發現是 UserImpl.java 內的 jdbcTemplate 沒有被自動注入 ↓↓↓

test

**想請問我這樣寫是正確的嗎,為什麼 JdbcTemplate jdbcTemplate 注入失敗

(如果把
@Autowired
private JdbcTemplate jdbcTemplate 放到 controller 內好像就找的到了)

**寫法上如何更好或者哪邊機制沒弄熟丟文章給我都可以,麻煩各位指導方向了!! 謝謝!

github_demo連結

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 個回答

1
喵凹咿唉思嗯
iT邦研究生 5 級 ‧ 2022-08-08 10:37:50

Hi,

我簡單試了一下 不過錯誤並不是顯示JDBCTemplate的注入失敗, 可能跟我有修改DB的部份有關? 但我看了一下JDBCTemplate應該是沒啥問題的 如果對注入有疑問可以試著加入actuator對注入的bean做檢查

我測試時報錯的是找不到UserRepository, 會這樣主因是因為排程是單獨被拉出來的 他看不到spring的相關設定, 如果在已經是spring的環境, 應該試著使用spring處理會比較簡單(包括對DB的操作, e.g. spring data)

如果使用spring scheduler的話, 基本上程式應該不太需要做太多調整

如果不想透過spring的元件來處理的話, 可以找看看比較古老的spring的版本是怎麼被quartz取得bean的, 或換句話說, 是怎麼取得applicationcontext

附上這邊測試的code

pom

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

JobController

package com.demo.controller;

import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class JobController  {
	@Autowired
	private Scheduler scheduler;

	public CronTrigger trigger;
	public JobDetail jobDetail;

	@RequestMapping(path = "/testjob", method = RequestMethod.GET)

	public String testjob() {
		try {
			jobDetail = JobBuilder.newJob(com.demo.controller.jobs.HelloJob.class).withIdentity("myJob", "group1").build();

			trigger = TriggerBuilder.newTrigger()
					.withIdentity("myTrigger", "group1")
					.withSchedule(CronScheduleBuilder.cronSchedule("0/5 0-59 0-23 * * ?"))
					.build();

			scheduler.scheduleJob(jobDetail, trigger);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return "testjob!";
	}
}

感謝分享,原來一直以為 @Autowired 不管在哪個 class 都可以取得 spring 底下的 bean。

後來又一直測試的結論是 JobBuilder.newJob(c) 給定class&後來呼叫runJob()時就不受 spring 管理了

jobDetail = JobBuilder.newJob(c)

解法是後來到 stackoverflow 詢問後的結果~ 順便分享一下。

SpringBeanUtils

@Component
public class SpringBeanUtils implements ApplicationContextAware{
	
	private static ApplicationContext context;

	public static Object getBean(String name) {
	    return getApplicationContext().getBean(name);
	}

	public static <T> T getBean(String name, Class<T> clazz) {
	    return getApplicationContext().getBean(name, clazz);
	}

	public static <T> T getBean(Class<T> clazz) {
	    return getApplicationContext().getBean(clazz);
	}

	public static ApplicationContext getApplicationContext() {
	    Assert.notNull(context, "applicationContext must be not null");
	    return context;
	}
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		SpringBeanUtils.context = applicationContext;
	}

}
public class HelloJob extends BaseJob {
    private UserRepository userRepository;
	
	public HelloJob() {
	    this.userRepository = SpringBeanUtils.getBean(UserRepository.class);
	}
...

如果是透過Spring的quartz就不需要做這些處理了, 他會幫你全部弄好. 這種寫法在舊的spring版本上才比較會看到

autowired的話 其實有個更簡單的判斷方法, 你可以試著改用setter或constructor的方式注入, 就會發現他拿不到了

我要發表回答

立即登入回答