The problem arose when I used shedlock-spring's @SchedulerLock annotation in a bean that autowires a reference to itself. The code (roughly) looked like this:
@Service
public class Service {
@Autowired
private Service self;
@Scheduled // Shedlock is not applied when executed by the scheduler
@SchedulerLock
public void process() {}
public void action() {
this.process(); // Shedlock is not applied, as expected
self.process(); // Shedlock is properly applied, as expected
}
}
What I discovered was that when Spring's scheduler runs this method, locking is not applied at all. Upon closed inspection it seems that the @Scheduled
annotation processor, which is defined as a BeanPostProcessor, applies itself to the unproxied instance of the service. This is not the case when I remove the self reference, as the BeanPostProcessor then properly operates on a proxy and locking works as expected. Similar behavior can be seen with the @Transactional
annotation.
I understand that circular references should be resolved by other means than delaying dependency injection, but I cannot quite grasp the reason behind "falling back" to applying BeanPostProcessors on the raw bean in case of an early proxy creation. Am I missing something?
Comment From: snicoll
action
is calling a method of the same class. That doesn't take the proxy into account, I am afraid. There are several sections in the reference guide that covers this. For instance, https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#scheduling-task-executor
I can see that @Scheduled
could be explicitly stated in that note there so we'll reuse this issue for that.
Comment From: simopahk
You can ignore the action method, it's there only to explain the use of self-injection.
When an early reference is requested to a bean containing @Scheduled
methods, and it also requires proxying (e.g. due to @SchedulerLock
and @Transactional
annotations), the @Scheduled
annotation processor receives a reference to the raw bean, not the proxy. It's more of a problem with how AbstractAutoProxyCreator handles early references, and therefore what object subsequent BeanPostProcessors work with.
Comment From: snicoll
All in all, self injection should not be done using field injection. Rather you should use @Lazy
or ObjectProvider
to get a lazy-resolved reference of that bean. This will make sure proxying is applied properly.
I cannot quite grasp the reason behind "falling back" to applying BeanPostProcessors on the raw bean in case of an early proxy creation.
There's not much we can do about that. It's doing best effort considering what you're asking.