分布式定时器 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号