分布式定时器 Quartz 作业中注入 Spring 依赖项
一、问题描述
SpringBoot 整合 分布式定时器 Quartz后,job中注入的自定义service,使用@Autowired 从spring容器中获取的对象为null,报空指针异常。
二、问题分析
sping容器可以管理Bean,但是Quartz的job是分布式定时器自己管理,所有通过@Autowired 从容器中获得对象无法别识别到。及时在自定义job上添加 @service/@Component注解依然无效。原因是job对象在spring容器加载的时候能够注入bean,但是调度时,job对象会重新创建,此时导致已经注入的对象丢失,因此报空指针异常。
三、以下通过三种办法进行解决
第一种:创建容器工具类 SpringUtil 实现 ApplicationContextAware, 此工具会在容器启动后自动加载,使用的时候通过getBean方法直接从容器中获得bean即可。代码和使用方法如下
1.1 SpringUtil 工具类代码
package com.northeasttycoon.shopping.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.stereotype.Component;
/**
 * @author :jack.zhao
 * @description :SpringUtil 容器工具类
 * @version: V1.0.0
 * @create :2022/06/18 10:16
 */
@Component
@Slf4j
public class SpringUtil implements ApplicationContextAware {
    private static ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        SpringUtil.context = context;
    }
    /**
     * 获取 Spring Bean
     * @param clazz 类
     * @param <T>   泛型
     * @return 对象
     */
    public static <T> T getBean(Class<T> clazz) {
        if (clazz == null) {
            return null;
        }
        return context.getBean(clazz);
    }
    /**
     * 获取 Spring Bean
     * @param bean 名称
     * @param <T>  泛型
     * @return 对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String bean) {
        if (bean == null) {
            return null;
        }
        return (T) context.getBean(bean);
    }
    /**
     * 获取 Spring Bean
     * @param beanName 名称
     * @param clazz    类
     * @param <T>      泛型
     * @return 对象
     */
    public static <T> T getBean(String beanName, Class<T> clazz) {
        if (null == beanName || "".equals(beanName.trim())) {
            return null;
        }
        if (clazz == null) {
            return null;
        }
        return (T) context.getBean(beanName, clazz);
    }
    /**
     * 获取上下文
     * @return 上下文
     */
    public static ApplicationContext getContext() {
        if (context == null) {
            throw new RuntimeException("There has no Spring ApplicationContext!");
        }
        return context;
    }
    /**
     * 发布事件
     * @param event 事件
     */
    public static void publishEvent(ApplicationEvent event) {
        if (context == null) {
            return;
        }
        try {
            context.publishEvent(event);
        } catch (Exception ex) {
           log.error(ex.getMessage());
        }
    }
}
1.2 job中使用
package com.northeasttycoon.shopping.quartz.job; import com..northeasttycoon.shopping.common.SpringUtil; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.PersistJobDataAfterExecution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Date; import java.util.concurrent.TimeUnit; /** * @author :jack.zhao * @description :自定义定时器 * @version: V1.0.0 * @create :2022/06/18 10:28 */ // 持久化 @PersistJobDataAfterExecution // 禁止并发执行 @DisallowConcurrentExecution @Slf4j public class CustomerQuartzJob extends QuartzJobBean { @SneakyThrows @Override protected void executeInternal(JobExecutionContext context) { // 业务服务类 CustomerServer customerServer = SpringUtil.getBean(CustomerServer .class); System.out.println("ceshi:"+str); String taskName = context.getJobDetail().getJobDataMap().getString("name"); log.info("数据 job, time:{" + new Date() + "} ,name:{" + taskName + "}<----"); } }
第二种方法:
查看QuartzJobFactory源码发现Quartz在初始化Bean未使用Spring的ApplicationContext,所以需要将Quartz的bean初始化注入到Spring中,代码如下
2.1 自定义定时工厂
package com.northeasttycoon.shopping.config; import org.quartz.spi.TriggerFiredBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.scheduling.quartz.AdaptableJobFactory; import org.springframework.stereotype.Component; /** * @author :jack.zhao * @description :解决Quartz的bean无法被spring管理 * @version: V1.0.0 * @create :2022/06/18 15:18 */ @Component public class QuartzJobFactory extends AdaptableJobFactory { @Autowired private AutowireCapableBeanFactory capableBeanFactory; @Override protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { //调用父类的方法 Object jobInstance = super.createJobInstance(bundle); //进行注入(这一步解决不能spring注入bean的问题) capableBeanFactory.autowireBean(jobInstance); return jobInstance; } }
2.2 应用
package com.northeasttycoon.shopping.quartz.job; import com..northeasttycoon.shopping.common.SpringUtil; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.quartz.DisallowConcurrentExecution; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.PersistJobDataAfterExecution; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Date; import java.util.concurrent.TimeUnit; /** * @author :jack.zhao * @description :自定义定时器 * @version: V1.0.0 * @create :2022/06/18 10:28 */ // 持久化 @PersistJobDataAfterExecution // 禁止并发执行 @DisallowConcurrentExecution @Slf4j public class CustomerQuartzJob extends QuartzJobBean { @Autowired CustomerServer customerServer; @SneakyThrows @Override protected void executeInternal(JobExecutionContext context) { // 业务服务类 customerServer = SpringUtil.getBean(CustomerServer .class); System.out.println("ceshi:"+str); String taskName = context.getJobDetail().getJobDataMap().getString("name"); log.info("数据 job, time:{" + new Date() + "} ,name:{" + taskName + "}<----"); } }
2.3 自定义配置QuartzJobConfig
@Configuration public class QuartzJobConfig { @Autowired private QuartzJobFactory customerJobFactory; //自定义的factory //获取调度器的工厂bean @Bean public SchedulerFactoryBean schedulerFactoryBean() { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); try { schedulerFactoryBean.setAutoStartup(false); //修改成自己的工厂 schedulerFactoryBean.setJobFactory(customerJobFactory); return schedulerFactoryBean; } catch (Exception e) { throw new RuntimeException(e); } } //创建schedule调度器 @Bean(name = "scheduler") public Scheduler scheduler() { return schedulerFactoryBean().getScheduler(); } }
第三种
private CustomerServer customerServer; @SneakyThrows @Override protected void executeInternal(JobExecutionContext context) { final String s = tet.UploadStaticInfo(); ApplicationContext applicationContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext"); customerServer= applicationContext.getBean(CustomerServer.class); System.out.println("ceshi:"+str); String taskName = context.getJobDetail().getJobDataMap().getString("name"); log.info("静态数据 job, time:{" + new Date() + "} ,name:{" + taskName + "}<----"); }
 
                    
                
 
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号