springboot + quartz+ FeignClient内部调用注入空指针
在定时任务中使用内部调用feign的注入为空怎么解决?
环境
- springboot 2.6.6
- spring-cloud-starter-openfeign: 3.1.5
- quartz用的starter
说明
- 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- feign接口
package com.example.springbootsatoken.api;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
@FeignClient(name = "test-service", url = "https://api.vvhan.com/api")
public interface TestService {
@GetMapping("/covid?city=南京")
Object test();
}
- 启动类
@SpringBootApplication
@EnableFeignClients
public class SpringBootSaTokenApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSaTokenApplication.class, args);
}
}
- job任务类
package com.example.springbootsatoken.quartz;
import com.example.springbootsatoken.api.TestService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import javax.annotation.Resource;
import java.time.LocalDateTime;
@Slf4j
public class TestJob extends QuartzJobBean {
@Resource
private TestService testService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("执行TestJob:{}", LocalDateTime.now());
Object result = testService.test();
log.info("openfeign return: {}", result);
}
}
- quartz配置类
package com.example.springbootsatoken.quartz;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfig {
//构建 JobDetail
@Bean
public JobDetail downloadDetail() {
return JobBuilder.newJob(TestJob.class) //具体任务类
//给 JobDetail 起一个 id, 不写也会自动生成唯一的 TriggerKey
.withIdentity("testJobDetail")
//JobDetail 内部的一个 map, 可以存储有关 Job 的数据, 这里的数据
// 可通过 Job 类中executeInternal方法的参数进行获取
.usingJobData("test_job", "test_job")
.storeDurably() //即使没有Trigger关联时也不删除该Jobdetail
.build();
}
//构建 Trigger 及 Scheduler
@Bean
public Trigger downloadTrigger() {
return TriggerBuilder.newTrigger()
.forJob(downloadDetail()) //关联上面的 jobDetail
.withIdentity("testTrigger")
.usingJobData("test_trigger", "running")
//cron 表达式设置每隔 5 秒执行一次
.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ? *"))
.build();
}
}
- 启动运行日志
2023-02-20 13:58:44.668 INFO 14496 --- [eduler_Worker-1] c.e.springbootsatoken.quartz.TestJob : 执行TestJob:2023-02-20T13:58:44.668
2023-02-20 13:58:45.014 INFO 14496 --- [eduler_Worker-2] c.e.springbootsatoken.quartz.TestJob : 执行TestJob:2023-02-20T13:58:45.014
2023-02-20 13:58:46.958 INFO 14496 --- [eduler_Worker-2] c.e.springbootsatoken.quartz.TestJob : openfeign return: {success=true, data={updatetime=2022-12-15 09:28:09, country=中国, province=江苏省, city=南京, now={sure_new_loc=未公布, sure_new_hid=未公布, sure_present=465}, history={sure_cnt=860, cure_cnt=395, die_cnt=-}, danger={high_risk=0, medium_risk=0}}}
2023-02-20 13:58:46.958 INFO 14496 --- [eduler_Worker-1] c.e.springbootsatoken.quartz.TestJob : openfeign return: {success=true, data={updatetime=2022-12-15 09:28:09, country=中国, province=江苏省, city=南京, now={sure_new_loc=未公布, sure_new_hid=未公布, sure_present=465}, history={sure_cnt=860, cure_cnt=395, die_cnt=-}, danger={high_risk=0, medium_risk=0}}}
2023-02-20 13:58:50.014 INFO 14496 --- [eduler_Worker-3] c.e.springbootsatoken.quartz.TestJob : 执行TestJob:2023-02-20T13:58:50.014
2023-02-20 13:58:50.176 INFO 14496 --- [eduler_Worker-3] c.e.springbootsatoken.quartz.TestJob : openfeign return: {success=true, data={updatetime=2022-12-15 09:28:09, country=中国, province=江苏省, city=南京, now={sure_new_loc=未公布, sure_new_hid=未公布, sure_present=465}, history={sure_cnt=860, cure_cnt=395, die_cnt=-}, danger={high_risk=0, medium_risk=0}}}
2023-02-20 13:58:55.016 INFO 14496 --- [eduler_Worker-4] c.e.springbootsatoken.quartz.TestJob : 执行TestJob:2023-02-20T13:58:55.016
2023-02-20 13:58:55.187 INFO 14496 --- [eduler_Worker-4] c.e.springbootsatoken.quartz.TestJob : openfeign return: {success=true, data={updatetime=2022-12-15 09:28:09, country=中国, province=江苏省, city=南京, now={sure_new_loc=未公布, sure_new_hid=未公布, sure_present=465}, history={sure_cnt=860, cure_cnt=395, die_cnt=-}, danger={high_risk=0, medium_risk=0}}}
2023-02-20 13:59:00.005 INFO 14496 --- [eduler_Worker-5] c.e.springbootsatoken.quartz.TestJob : 执行TestJob:2023-02-20T13:59:00.005
2023-02-20 13:59:00.174 INFO 14496 --- [eduler_Worker-5] c.e.springbootsatoken.quartz.TestJob : openfeign return: {success=true, data={updatetime=2022-12-15 09:28:09, country=中国, province=江苏省, city=南京, now={sure_new_loc=未公布, sure_new_hid=未公布, sure_present=465}, history={sure_cnt=860, cure_cnt=395, die_cnt=-}, danger={high_risk=0, medium_risk=0}}}
问题分析
若使用
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
这个依赖,可能导致bean未托管给spring,或者OpenFeign未加@EnableFeignClients
,导致没扫描到,需要手动在普通调用类手动获取bean
package com.example.springbootsatoken.quartz;
import com.example.springbootsatoken.api.TestService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.QuartzJobBean;
import javax.annotation.Resource;
import java.time.LocalDateTime;
@Slf4j
public class Test1Job extends QuartzJobBean implements ApplicationContextAware {
private TestService testService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("执行Test1Job:{}", LocalDateTime.now());
Object result = testService.test();
log.info("openfeign return: {}", result);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.testService = applicationContext.getBean(TestService.class);
}
}
或者通过工具类
package com.example.springbootsatoken.config;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
}
- 获取bean
package com.example.springbootsatoken.quartz;
import com.example.springbootsatoken.api.TestService;
import com.example.springbootsatoken.config.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.QuartzJobBean;
import javax.annotation.Resource;
import java.time.LocalDateTime;
@Slf4j
public class Test1Job extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//通过工具类获取bean
TestService testService = SpringUtils.getBean(TestService.class);
log.info("执行Test1Job:{}", LocalDateTime.now());
Object result = testService.test();
log.info("openfeign return: {}", result);
}
}
1 个赞