springboot + quartz+ FeignClient内部调用注入空指针

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 Like