7

Maybe I have some outdated knowledge but it is the same as described here https://stackoverflow.com/a/2657465/2674303

But now I noticed that this example works without any exceptions:

@Service
@EnableScheduling
public final class MyService {
    @PostConstruct
    public void init(){
        System.out.println("MyService started");
    }
    @Scheduled(fixedDelay= 1000)
    public void scheduleCall() {
        System.out.println("scheduleCall");    
 
  }
}

Could you pease provide how does it work ?

gstackoverflow
  • 36,709
  • 117
  • 359
  • 710
  • 1
    It uses Magic! And Lies! As far as I know, modern frameworks are able to use byte code rewriting to change the `final` class as it's loaded (i.e., during class load). Thus you get a "different" class without having to extend it. But this technology moves pretty fast and there's always more than one way to do it, so it could be any technique really. – markspace Jun 06 '22 at 16:18
  • @markspace We should find a MythBuster for that. – gstackoverflow Jun 06 '22 at 20:48

1 Answers1

13

@Scheduled annotation does not require proxy creation. The mechanism is different. After bean initialization Spring called post-processor ScheduledAnnotationBeanPostProcessor. Post processor searches for all methods annotated with @Scheduled and registers them to TaskScheduller for execution. Method execution will be performed via reflection.
See ScheduledAnnotationBeanPostProcessor source code.

@Scheduled

Processing of @Scheduled annotations is performed by registering a ScheduledAnnotationBeanPostProcessor. This can be done manually or, more conveniently, through the task:annotation-driven/ XML element or @EnableScheduling annotation.

ScheduledAnnotationBeanPostProcessor

Bean post-processor that registers methods annotated with @Scheduled to be invoked by a TaskScheduler according to the "fixedRate", "fixedDelay", or "cron" expression provided via the annotation. This post-processor is automatically registered by Spring's task:annotation-driven XML element, and also by the @EnableScheduling annotation.

Autodetects any SchedulingConfigurer instances in the container, allowing for customization of the scheduler to be used or for fine-grained control over task registration (e.g. registration of Trigger tasks). See the @EnableScheduling javadocs for complete usage details.

@PostConstruct also implemented via post-processor InitDestroyAnnotationBeanPostProcessor when dependency injection performed for bean, method which marked @PostConstruct will be executed thru reflection without proxy.
See InitDestroyAnnotationBeanPostProcessor source code

Summary:
In your example, Spring will create bean without proxy.

In case you will add a proxy-specific annotation, for example, @Transactional you will get an exception that proxy can not be created due to final class java.lang.IllegalArgumentException: Cannot subclass final class com.test.services.MyService

@Service
@EnableScheduling
public final class MyService {
    @PostConstruct
    public void init(){
        System.out.println("MyService started");
    }
    @Scheduled(fixedDelay= 1000)
    @Transactional
    public void scheduleCall() {
        System.out.println("scheduleCall");

    }
}

But the current problem you also can solve to force use JDK dynamic proxy. We need to create an interface for class and set property spring.aop.proxy-target-class = false according to Proxying mechanisms

Eugene
  • 5,269
  • 2
  • 14
  • 22