Spring Framework
  1. Spring Framework
  2. SPR-7216

@Scheduled processed twice when accidentally using @Configurable on a managed bean

    Details

    • Type: Task Task
    • Status: Closed
    • Priority: Trivial Trivial
    • Resolution: Complete
    • Affects Version/s: 3.0.2
    • Fix Version/s: 3.0.3
    • Component/s: Core
    • Labels:
      None
    • Last commented by a User:
      false

      Description

      Here is a stack trace of a @Scheduled annotation being added to cronTasks:

      Thread [main] (Suspended (breakpoint at line 117 in ScheduledAnnotationBeanPostProcessor$1))
      ScheduledAnnotationBeanPostProcessor$1.doWith(Method) line: 117
      ReflectionUtils.doWithMethods(Class<?>, MethodCallback, MethodFilter) line: 455
      ReflectionUtils.doWithMethods(Class<?>, MethodCallback) line: 431
      ScheduledAnnotationBeanPostProcessor.postProcessAfterInitialization(Object, String) line: 90
      DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyBeanPostProcessorsAfterInitialization(Object, String) line: 407
      DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(String, Object, RootBeanDefinition) line: 1418
      DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(Object, String) line: 386
      BeanConfigurerSupport.configureBean(Object) line: 141
      AnnotationBeanConfigurerAspect.configureBean(Object) line: 59
      AnnotationBeanConfigurerAspect(AbstractDependencyInjectionAspect).ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$2$1ea6722c(Object) line: 89
      <unknown receiving type>(XxxJob).<init>() line: 69
      NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method]
      NativeConstructorAccessorImpl.newInstance(Object[]) line: 39
      DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 27
      Constructor<T>.newInstance(Object...) line: 513
      BeanUtils.instantiateClass(Constructor<T>, Object...) line: 126
      CglibSubclassingInstantiationStrategy(SimpleInstantiationStrategy).instantiate(RootBeanDefinition, String, BeanFactory) line: 72
      DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).instantiateBean(String, RootBeanDefinition) line: 948
      DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBeanInstance(String, RootBeanDefinition, Object[]) line: 901
      DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 485
      DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 456
      AbstractBeanFactory$1.getObject() line: 291
      DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 222
      DefaultListableBeanFactory(AbstractBeanFactory).doGetBean(String, Class<T>, Object[], boolean) line: 288
      DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 190
      DefaultListableBeanFactory.preInstantiateSingletons() line: 563
      XmlWebApplicationContext(AbstractApplicationContext).finishBeanFactoryInitialization(ConfigurableListableBeanFactory) line: 872
      XmlWebApplicationContext(AbstractApplicationContext).refresh() line: 423
      ContextLoaderListener(ContextLoader).createWebApplicationContext(ServletContext, ApplicationContext) line: 276
      ContextLoaderListener(ContextLoader).initWebApplicationContext(ServletContext) line: 197
      ContextLoaderListener.contextInitialized(ServletContextEvent) line: 47
      StandardContext.listenerStart() line: 3972
      StandardContext.start() line: 4467
      StandardHost(ContainerBase).start() line: 1045
      StandardHost.start() line: 785
      StandardEngine(ContainerBase).start() line: 1045
      StandardEngine.start() line: 443
      StandardService.start() line: 519
      StandardServer.start() line: 710
      Catalina.start() line: 581
      NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
      NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
      DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
      Method.invoke(Object, Object...) line: 597
      Bootstrap.start() line: 289
      Bootstrap.main(String[]) line: 414

      Shortly thereafter, the same annotation on the same bean instance is processed a second time from doCreateBean, the same method. Here is the diff of the first and second stack traces:

      — stack1 2010-05-19 01:56:49.000000000 -0500
      +++ stack2 2010-05-19 01:56:46.000000000 -0500
      @@ -5,20 +5,7 @@
      ScheduledAnnotationBeanPostProcessor.postProcessAfterInitialization(Object, String) line: 90
      DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyBeanPostProcessorsAfterInitialization(Object, String) line: 407
      DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(String, Object, RootBeanDefinition) line: 1418

      • DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).initializeBean(Object, String) line: 386
      • BeanConfigurerSupport.configureBean(Object) line: 141
      • AnnotationBeanConfigurerAspect.configureBean(Object) line: 59
      • AnnotationBeanConfigurerAspect(AbstractDependencyInjectionAspect).ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$2$1ea6722c(Object) line: 89
      • <unknown receiving type>(ClientSavingsPlanJob).<init>() line: 69
      • NativeConstructorAccessorImpl.newInstance0(Constructor, Object[]) line: not available [native method]
      • NativeConstructorAccessorImpl.newInstance(Object[]) line: 39
      • DelegatingConstructorAccessorImpl.newInstance(Object[]) line: 27
      • Constructor<T>.newInstance(Object...) line: 513
      • BeanUtils.instantiateClass(Constructor<T>, Object...) line: 126
      • CglibSubclassingInstantiationStrategy(SimpleInstantiationStrategy).instantiate(RootBeanDefinition, String, BeanFactory) line: 72
      • DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).instantiateBean(String, RootBeanDefinition) line: 948
      • DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBeanInstance(String, RootBeanDefinition, Object[]) line: 901
      • DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 485
        + DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).doCreateBean(String, RootBeanDefinition, Object[]) line: 519
        DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 456
        AbstractBeanFactory$1.getObject() line: 291
        DefaultListableBeanFactory(DefaultSingletonBeanRegistry).getSingleton(String, ObjectFactory) line: 222

      This only seems to affect two beans that I use with Spring Batch, although that may not actually matter. Both job beans are annotated at the class level with @Service and @Configurable(dependencyCheck = true), both extend the same abstract subclass with @Autowired annotations on fields, and both @Scheduled annotations are on methods with @Transactional.

        Activity

        Hide
        Juergen Hoeller added a comment - - edited

        It looks like those XxxJob instances are being defined as Spring beans and hence created as fully managed Spring bean instances, while at the same time being annotated with @Configurable? That would lead to each of those instances getting initialized twice, once by the container as part of its standard management, and once through @Configurable's aspect. Each field would get injected twice (which you wouldn't notice), and each @Scheduled would get processed twice as well.

        Note that @Configurable is meant to be used for instances which are not managed by the container - that's the whole point there. Otherwise you wouldn't need to use an aspect to begin with because plain @Autowired use on fields (etc) will get processed by the container itself during the createBean phase, before it returns the instance from a getBean call.

        Juergen

        Show
        Juergen Hoeller added a comment - - edited It looks like those XxxJob instances are being defined as Spring beans and hence created as fully managed Spring bean instances, while at the same time being annotated with @Configurable? That would lead to each of those instances getting initialized twice , once by the container as part of its standard management, and once through @Configurable's aspect. Each field would get injected twice (which you wouldn't notice), and each @Scheduled would get processed twice as well. Note that @Configurable is meant to be used for instances which are not managed by the container - that's the whole point there. Otherwise you wouldn't need to use an aspect to begin with because plain @Autowired use on fields (etc) will get processed by the container itself during the createBean phase, before it returns the instance from a getBean call. Juergen
        Hide
        Christopher G. Stach II added a comment -

        You hit the nail right on the head. I actually knew that and my brain has just been retarded, I guess. I think the @Configurable got merged in alongside the @Service during some refactoring a while back and it just didn't click. Things are running fine now. Thanks!

        Show
        Christopher G. Stach II added a comment - You hit the nail right on the head. I actually knew that and my brain has just been retarded, I guess. I think the @Configurable got merged in alongside the @Service during some refactoring a while back and it just didn't click. Things are running fine now. Thanks!
        Hide
        Christopher G. Stach II added a comment -

        Maybe this issue should be modified to reflect the need for some fail-fastness in the post processors for the stereotypes and @Configurable? Are there instances where someone would want both?

        Show
        Christopher G. Stach II added a comment - Maybe this issue should be modified to reflect the need for some fail-fastness in the post processors for the stereotypes and @Configurable? Are there instances where someone would want both?
        Hide
        Juergen Hoeller added a comment -

        Not sure why somebody would deliberately activate the @Configurable aspect for instances that are managed by the container anyway. Unfortunately it isn't completely trivial to detect invalid cases there: @Configurable itself doesn't necessarily hurt; it's rather an activated BeanConfigurerAspect that happens to react to @Configurable... In order to reliably detect that at runtime, we'd have to track and compare bean instances (since the BeanConfigurerAspect isn't necessarily using the same bean name as the corresponding bean definition in the container, the instances are all we got).

        Juergen

        Show
        Juergen Hoeller added a comment - Not sure why somebody would deliberately activate the @Configurable aspect for instances that are managed by the container anyway. Unfortunately it isn't completely trivial to detect invalid cases there: @Configurable itself doesn't necessarily hurt; it's rather an activated BeanConfigurerAspect that happens to react to @Configurable... In order to reliably detect that at runtime, we'd have to track and compare bean instances (since the BeanConfigurerAspect isn't necessarily using the same bean name as the corresponding bean definition in the container, the instances are all we got). Juergen
        Hide
        Juergen Hoeller added a comment -

        I've added corresponding warnings to the reference docs: a general one in the AOP chapter, and a more specific one in the scheduling chapter.

        Juergen

        Show
        Juergen Hoeller added a comment - I've added corresponding warnings to the reference docs: a general one in the AOP chapter, and a more specific one in the scheduling chapter. Juergen

          People

          • Assignee:
            Juergen Hoeller
            Reporter:
            Christopher G. Stach II
            Last updater:
            Trevor Marshall
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:
              Days since last comment:
              3 years, 44 weeks, 6 days ago