在运行时创建的Spring Bean中使用@Scheduled注解

3

我的使用情况如下:

我目前正在开发一个应用程序,其中有一个EventModule负责触发各种事件。其中之一是TimeEvent。这些时间事件应该每秒钟触发一次。当我的应用程序在运行时,还应该能够添加对新事件的支持。为了满足后一要求,我使用OSGI。

对于生成时间事件本身,我发现Spring已经为我提供了这样的功能,即通过在将周期性调用的方法上放置@Scheduled注解来实现(在我的情况下,每秒钟调用一次)。

因此,我有了这段代码:

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

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

}

这段代码可以正常运行,每秒都会触发事件。但是由于 TimeEventGenerator 类被注释为 @Component ,所以需要在启动时实例化该类。在我的应用场景中,即OSGI部分,我应该能够在需要支持 TimeEvents 时,在我的 BundleActivator start(BundleContext)方法中创建此类的实例,因此不考虑使用 @ Component 注释。
这就是我遇到问题的地方。我了解到可以通过使用 BeanFactoryPostProcessor 在Spring应用程序上下文中自己注入bean,这正是我所做的,结果如下所示:
/**
 * 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 );
  } 

}

在这里的问题是,每当我使用createBean方法创建一个TimeEventGenerator(参见第一个代码片段)时,testSchedule()方法从未被调用,而我原本期望它会被调用,因为该bean由Spring管理。
编辑:我忘记添加了,我也尝试过使用两个GenericBeanDefinition的子类,即ScannedGenericBeanDefinition和AnnotatedGenericBeanDefinition,但也都没有成功。
2个回答

1
看了Spring文档后,我认为你应该尝试使用AnnotatedGenericBeanDefinition而不是GenericBeanDefinition。请查看这里的文档。
编辑: 我查看了ScheduledAnnotationBeanPostProcessor的代码,并且我的注释被处理了。我遇到的问题是,即使后置处理器识别了计划任务,它也只会将任务与任务执行器一次性调度。当我注册我的计划类时,这已经发生了,因此它没有被调用。将刷新事件传递给后置处理器可以强制它进行调度,但它也会重新调度每个其他任务。查看代码(至少在Spring 3.2中),使用注释无法实现您想要的操作。您可以创建自定义注释处理器并使用任务执行器注册任务。

哦,是的,我也试过那个。我忘了在我的问题中添加它。它也没有起作用。我会把它添加到原始问题中。 - Kristof P.
你的修改实际上帮助我解决了这个问题!我的做法是让 RuntimeBeanFactory 扩展 ScheduledAnnotationBeanPostProcessor 并在从应用程序上下文中检索到新创建的 bean 之后调用 afterSingletonsInstantiated 方法: .... public T createBean( Class aBeanClass, String aBeanName ) throws IOException { // previous code from the original post untouched // .... T bean = applicationContext.getBean( aBeanClass ); afterSingletonInstantiated(); return bean; } - Kristof P.

0

如果您只需要打开或关闭TimeEventGenerator,则只需将其放在单独的bundle中,并启动和停止该bundle以打开或关闭它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接