Ссылки на компоненты не создаются при использовании кварца с планировщиком Spring - не удалось создать экземпляр задания
Я обновил приложение с весенней загрузкой с 1.x до 2.x после того, как планировщик обновлений не работает должным образом. Получается следующее исключение.
org.quartz.SchedulerException: Job instantiation failed
at org.springframework.scheduling.quartz.AdaptableJobFactory.newJob(AdaptableJobFactory.java:47)
at org.quartz.core.JobRunShell.initialize(JobRunShell.java:127)
at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:392) Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.schedulers.simplejobs.SimpleJobExecutions': Unsatisfied dependency expressed through field 'sampleService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.service.SampleService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:351)
at org.springframework.scheduling.quartz.SpringBeanJobFactory.createJobInstance(SpringBeanJobFactory.java:90)
at com.scheduler.factory.JobFactory.createJobInstance(JobFactory.java:35)
at org.springframework.scheduling.quartz.AdaptableJobFactory.newJob(AdaptableJobFactory.java:43)
... 2 common frames omitted Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.service.SampleService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1655)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1214)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1168)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
... 11 common frames omitted
Я пробовал много ссылок от SO, и предлагаемые конфигурации не помогли.
Ниже приведены детали стека.
Пружина загрузки 2.1.5
Весна 5.1.7
Кварц 2.3.1
Вот моя кодовая база.
public final class JobFactory extends SpringBeanJobFactory {
private transient AutowireCapableBeanFactory beanFactory;
private static final Logger LOGGER = LoggerFactory.getLogger(JobFactory.class);
public void setApplicationContext(final ApplicationContext context) {
Stream.of(context.getBeanDefinitionNames())
.filter(s -> s.startsWith("sample"))
.forEach(beanName -> LOGGER.debug("applicationContext beans {}",beanName));
beanFactory = context.getAutowireCapableBeanFactory();
}
@Override
protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
final Object job = super.createJobInstance(bundle);
LOGGER.debug("createJobInstance job {}",job);
beanFactory.autowireBean(job);
LOGGER.debug("createJobInstance autowireBean job {}",job);
return job;
}
@Component
public class SimpleJobExecutions extends QuartzJobBean {
@Autowired
public SampleService sampleService;
@Override
public void executeInternal(JobExecutionContext context) throws JobExecutionException {
try {
SampleDTO sample = new SampleDTO();
sample.setName("Name");
sample.setAge(25);
SampleDTO sampleObj = sampleService.save(sample);
} catch (Exception ex) {
throw new JobExecutionException(ex);
}
}
@Service("sampleService")
public class SampleServiceImpl implements SampleService {
@Autowired
SampleRepository sampleRepository;
@Autowired
SampleMapper sampleMapper;
@Override
public SampleDTO save(SampleDTO sampleDTO) {
log.info("Request to save Sample : {}", sampleDTO);
Sample sample = sampleMapper.toEntity(sampleDTO);
sample = sampleRepository.save(sample);
return sampleMapper.toDto(sample);
}
}
Я могу распечатать все bean-компоненты из контекста приложения, но они не внедряются во время выполнения задания.
3 ответа
Наконец, я нашел первопричину этой проблемы.
Автоматическое подключение других ссылок на bean-компоненты начинает работать после удаления следующих зависимостей.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
Зависимости инструментов разработки создают конфликты с зависимостями приложений, и это не позволяет создавать ссылки на bean-компоненты только при выполнении QuartzJobBean.
Привет постараюсь ответить на вопрос
По умолчанию ваш SchedulerFactoryBean использует AdaptableJobFactory( https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html.), который не имеет возможности автоматического подключения, поэтому вам нужно указать здесь экземпляр SpringBeanJobFactory, в основном вам нужно указать его планировщику. пример:
@Autowired
private ApplicationContext applicationContext;
@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
JobFactory jobFactory = new JobFactory();
jobFactory.setApplicationContext(applicationContext);
schedulerFactoryBean.setJobFactory(jobFactory);
return schedulerFactoryBean;
}
Альтернативный подход:
если приведенный выше код не работает, вы можете просто попытаться получить bean-компонент из контекста приложения внутри вашего метода executeInternal()
@Component
public class SimpleJobExecutions extends QuartzJobBean {
@Override
public void executeInternal(JobExecutionContext context) throws JobExecutionException {
try {
ApplicationContext applicationContext = (ApplicationContext)
context.getScheduler().getContext().get("applicationContext");
SampleService sampleService = (SampleService) applicationContext.getBean(SampleService.class);
SampleDTO sample = new SampleDTO();
sample.setName("Name");
sample.setAge(25);
SampleDTO sampleObj = sampleService.save(sample);
} catch (Exception ex) {
throw new JobExecutionException(ex);
}
}
Во-первых, по линии @Autowired public SampleServiceImpl sampleService;
в SimpleJobExecutions
class, было бы хорошей практикой кодировать интерфейсы.
Я чувствую, что в контейнере есть прокси-компонент, который реализует SimpleService
и так как вы пытаетесь ввести SimpleServiceImpl
он не вводится.(обратите внимание, что прокси-объект не является SimpleServiceImp).
Чтобы проверить, так ли это в вашем случае, просто попробуйте ввести SimpleServiceImpl
по интерфейсу.
Просто замените:
@Autowired public SampleServiceImpl sampleService;
с
@Autowired public SampleService sampleService;
Надеюсь, это поможет.
Обновление: (просто внесите некоторые пояснения)
Когда ваш bean-компонент обернут прокси-сервером, прокси-объект реализует интерфейс вашего bean-компонента, и поскольку в предыдущей версии вашего вопроса (до редактирования) вы вводили bean-компонент по классу (вместо интерфейса), bean-компонент такого класса не был (но был один с типом интерфейса), поэтому контейнеру не удалось найти такой компонент и он выбросил:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.service.SampleService' available