Use @Scheduled annotations in a, at runtime generated by spring bean

My use case:

I am currently developing an application that has an EventModule that is responsible for firing all kinds of events. One of these events is TimeEvent. These temporary events must fire every second. It should also be possible to add support for new events when my application is up and running. For the latter requirement, I am using OSGI.

For generating the timing events themselves, I found that spring already provides such functionality for me, namely by placing annotations @Scheduled

on a method that will be called periodically (in my case every second).

So, I had this code:

/**
 * Generator class that periodically generates {@link TimeEvent}s.
 */
@Component
@EnableScheduling
public class TimeEventGenerator {

  @Scheduled( cron = "*/1 * * * * ?" )
  public void testSchedule() {
    // fire a time event
  }

}

      

This works great and the events fire every second. However, this requires the class to TimeEventGenerator

be instantiated on startup (since it is annotated with @Component

). In my use case, namely the OSGI part, I would have to instantiate this class in start(BundleContext)

my method BundleActivator

whenever I decided to plug in support TimeEvents

, so @Component

annotation is not an option.

This is the part where I have problems. I found out that I am injecting beans myself into the context of a spring application using BeanFactoryPostProcessor

which I did, and this resulted in the following code:

/**
 * Class that allows to add spring beans to a running application context.
 */
@Component
public class RuntimeBeanFactory implements ApplicationContextAware,
                                    BeanFactoryPostProcessor {

  private ApplicationContext applicationContext;
  private ConfigurableListableBeanFactory beanFactory;

  @Override
  @Autowired
  public void setApplicationContext( ApplicationContext aApplicationContext )    throws     BeansException {
    applicationContext = aApplicationContext;
  }

  @Override
  @Autowired
  public void postProcessBeanFactory( ConfigurableListableBeanFactory aBeanFactory )     throws BeansException {
    beanFactory = aBeanFactory;
  }

  public <T> T createBean( Class<T> aBeanClass, String aBeanName ) throws IOException {
    BeanDefinitionRegistry registry = ( ( BeanDefinitionRegistry ) beanFactory );

    GenericBeanDefinition beanDefinition = new GenericBeanDefinition();       
    beanDefinition.setBeanClass( aBeanClass );
    beanDefinition.setLazyInit( false );
    beanDefinition.setAbstract( false );
    beanDefinition.setAutowireCandidate( true );

    if ( !registry.isBeanNameInUse( aBeanName ) ) {
      registry.registerBeanDefinition( aBeanName, beanDefinition );
    }

    return applicationContext.getBean( aBeanClass );
  } 

}

      

The problem is that whenever I create TimeEventGenerator

(see first code snippet) with a method createBean

, the method is testSchedule()

never called, while I expected it to be, since the bean is managed by spring.

EDIT: I forgot to add that I also tried the above code with two GenericBeanDefinition subclasses, namely ScannedGenericBeanDefinition and AnnotatedGenericBeanDefinition, with no success.

+3


source to share


2 answers


Looking at the Spring documentation, I think you should try AnnotatedGenericBeanDefinition instead of GenericBeanDefinition. See docs here .



EDIT I was looking at the code ScheduledAnnotationBeanPostProcessor

and my annotations were getting processed. The problem I ran into was that although the post processor recognized the scheduled task, it only runs the tasks with the task executor once. By the time I registered my scheduled class, this had already happened as it was not being called. Sending the update event to the post processor made it schedule, but also moved every other task. Looking at the code (at least in Spring 3.2), what you want to do is not possible with annotations. You can create a custom annotation processor and register tasks with task executors.

+1


source


If you just need to enable and disable the TimeEventGenerator, just add it to a separate bundle and start and stop it to enable and disable it.



0


source







All Articles