0

I am trying to use Spring AOP with Quartz job spring beans. The jobs are autowired into spring container using the following approach: a. Create AutowiringSpringBeanJobFactory extending SpriongBeanJobFactory implementing ApplicationContextAware b. override createJobInstance and use the AutowiringCapableBeanFactory to autowire the job beans.

See solution

Spring boot Application Configuration:

@Configuration
public class MyApplicationConfig {
  @Bean
  @Primary
  public SchedulerFactoryBean schedulerFactoryBean() throws Exception {
    AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
    jobFactory.setApplicationContext(ApplicationContextProvider.getContext());

    SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
    factoryBean.setDataSource(datasource());
    factoryBean.setConfigLocation(new ClassPathResource("quartz.properties"));
    factoryBean.setFactory(jobFactory);
  }
}

Autowiring of Jobs done as:

public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

  private AutowireCapableBeanFactory beanFactory;

    @Override
    public void setApplicationContext() {
      beanFactory = context.getAutowireCapableBeanFactory();
    }
    
    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
      final Object job = super.createJobInstance(bundle);
      beanFactory.autowireBean(job);
      return job;

    }
}

Using this setup, I have created an Aspect:

@Aspect
@Configuration
public class MyAspect {
    @Around("execution(public void com.myapp.jobs.*.execute(..))")
    public Object process(ProceedingJoinPoint pjp) throws Throwable {
       // do something with pjp
    }
}

This is my sample job:

@Service
public class Myjob implements Job {
  @Autowired
  IAuditService auditService;

  public void execute(JobExecutionContext jctx) throws JobExecutionException {
    //do something
  }
}

However, when the above job executes, spring aop does not invoke MyAspect.process().

I am already autowring my job beans and I am able to autowire other beans into my jobs but only the aspect does not get invoked. Whats missing here?

Mohd Asim
  • 45
  • 1
  • 10
  • Please confirm if the class `MyAspect ` falls in the component scan and recognized as a bean. Also how is the method `Myjob.execute()` called ? An internal method call cannot be adviced by Spring AOP. – R.G Jul 28 '20 at 06:41
  • Yes, when i replace the pointcut expression to be invoked for controllers it works fine. It however does not work for jobs. MyJob.execute() is called by Quartz.Scheduler. – Mohd Asim Jul 28 '20 at 12:43
  • How is the instance of `MyJob` made available to the scheduler ? Is that a spring bean or the instance is created using `new` operator ? – R.G Jul 28 '20 at 12:45
  • Its a spring bean. I have used this solution [link](https://stackoverflow.com/questions/48577810/how-to-autowired-in-quartz) to enable autowiring for the job bean. – Mohd Asim Jul 28 '20 at 12:58
  • Could you please update the question with a minimal reproducible example code ? – R.G Jul 28 '20 at 16:03
  • Added code for application configuration and job factory – Mohd Asim Jul 28 '20 at 16:58
  • 1
    `job` instance is not a spring managed bean and Spring AOP will not work on those instance. Please go through these links to understand the [AutowireCapableBeanFactory.autowireBean()](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/config/AutowireCapableBeanFactory.html#autowireBean-java.lang.Object-) and https://stackoverflow.com/a/3813725/4214241 – R.G Jul 29 '20 at 04:13

2 Answers2

0

First of all you can use @Component instead of @Configuration as annotation of your aspect, thus it will look like this:

@Aspect
@Component
public class MyAspect {
    @Around("execution(public void com.myapp.jobs.*.execute(..))")
    public Object process(ProceedingJoinPoint pjp) throws Throwable {
       // do something with pjp
    }
}

Secondly make sure that the pointcut expression is correct, whether you are referring to the right package/classe/method.

Lastly, if you are not using spring boot you should add @EnableAspectJAutoProxy to your configuration class otherwise no need for that with spring boot as it's auto configured !

Abdelghani Roussi
  • 2,707
  • 2
  • 21
  • 39
  • Unfortunately, no luck with using @Component either. I have verified the pointcut expression is good by creating similar bean and calling it from within another bean. – Mohd Asim Jul 27 '20 at 23:36
0

To enable AOP for my jobs, i had to enhance "AutowiringSpringBeanJobFactory " to manually weave aspects in for the jobs as follows:

public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware {

  private AutowireCapableBeanFactory beanFactory;

    private Object jobAspect;

    public void setJobAspect(Object aspect) {
      this.jobAspect = aspect;
    }

    @Override
    public void setApplicationContext() {
      beanFactory = context.getAutowireCapableBeanFactory();
    }
    
    @Override
    protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
      final Object job = super.createJobInstance(bundle);
      beanFactory.autowireBean(job);

      AspectJProxyFactory pFactory = new AspectJProxyFactory(job);
      pFactory.addAspect(this.jobAspect);

      return pFactory.getProxy();
    }
}

However, there are additional issues with this if your job uses annotations on its method(s). That's for another time though.

Mohd Asim
  • 45
  • 1
  • 10