Ссылки на компоненты не создаются при использовании кварца с планировщиком 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
Другие вопросы по тегам