1

I have updated my spring-boot application from 1.x to 2.x after upgrade scheduler is not working as expected. Getting the following exception.

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

I have tried many references from SO and suggested configurations didn't help.

Below are the stack details.

Spring boot 2.1.5

Spring 5.1.7

Quartz 2.3.1

Here is my codebase.


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);
  }

}

I can able to print all the beans from application context but it's not injecting at the time of job execution.

Mohankumar Rathinam
  • 623
  • 1
  • 11
  • 25

3 Answers3

1

Finally, I found the root cause of this problem.

Autowiring other bean references are started working after removing the following dependencies.

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <version>2.1.5.RELEASE</version>
  </dependency>

dev tools dependencies are creating conflicts with application dependencies and it's not allowing to create the bean references only in QuartzJobBean executions.

Mohankumar Rathinam
  • 623
  • 1
  • 11
  • 25
0

Hi I will try to answer the question

By default your SchedulerFactoryBean uses AdaptableJobFactory( https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html#setJobFactory-org.quartz.spi.JobFactory) which does not have the autowiring capabalities so you need to Specify an instance of Spring's SpringBeanJobFactory here basically you need to specify it to the scheduler. ex:

@Autowired
private ApplicationContext applicationContext;

@Bean
public SchedulerFactoryBean schedulerFactoryBean() {
    SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
    JobFactory jobFactory = new JobFactory();
    jobFactory.setApplicationContext(applicationContext);
    schedulerFactoryBean.setJobFactory(jobFactory);
    return schedulerFactoryBean;
}

Alternative approach :

if the above code is not working you then you can simply try to get the bean from the application context inside your executeInternal() method

 @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);
        }
    }
Richard Elite
  • 720
  • 5
  • 15
-1

Firstly, regarding the line @Autowired public SampleServiceImpl sampleService; in SimpleJobExecutions class, it would be a good practice to code to interfaces.

I feel that there is a proxy bean in container which implements SimpleService and since your are trying to inject SimpleServiceImpl it is not injected.(note that the proxy Object is not 'SimpleServiceImp').

To test if this is your case just try to inject SimpleServiceImpl by interface.

Simply replace:

@Autowired public SampleServiceImpl sampleService;

with

@Autowired public SampleService sampleService;

hope it helps.

Update: (just make some clarification)

When your bean is wrapped by a proxy, the proxy object implements the interface of your bean and since in previous version of your question (before edit) you were injecting a bean by class (instead of interface), there was no bean of such class (but there was one with the interface type), therefore container was unable to find such a bean and it threw:

org.springframework.beans.factory.NoSuchBeanDefinitionException:  No qualifying bean of type 'com.service.SampleService' available
ali4j
  • 522
  • 3
  • 15