Search code examples
javaspringcglib

How does spring create proxy for a final class?


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 ?


Solution

  • @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