spring-projects/spring-boot

ApplicationListener is kind of inefficient, and maybe its usage could be optimized

dsyer opened this issue · 5 comments

dsyer commented

Spring is pretty slow at calculating the generic signature of ApplicationListeners. See https://jira.spring.io/browse/SPR-16970 for more background.

Spring Boot exacerbates these issues by having 2
ApplicationEventMulticasters (one in EventPublishingRunListener
and one in the actual ApplicationContext).

Some of the Boot listeners could be implemented as
SmartApplicationListener to avoid the cost of looking up the generic
type information.

It might also help to use a different callback
(e.g. SpringApplicationRunListener or
ApplicationContextInitializer) instead of
ApplicationListener. E.g. LiquibaseServiceLocatorApplicationListener
which is queried multiple times in a vanilla Boot app, only to do nothing
because Liquibase is not present. It only needs to be called once, so ApplicationContextInitializer
would probably suffice.

The cache in GenericApplicationListenerAdapter is static so two ApplicationEventMulticasters shouldn't make a big difference.

dsyer commented

The cache in AbstractApplicationEventMulticaster is not static though. So there is a cost in having multiple instances. (The other points are more important anyway.)

During start up of SampleWebFluxApplication, a total of 4-5ms is spent getting the application listeners for the 8 events that are multicast. Switching LiquibaseServiceLocatorApplicationListener to be a SpringApplicationRunListener makes no discernible difference to the time spent retrieving the application listeners.

I'm going to close this one for now as there doesn't appear to be much, if anything, to be gained.

dsyer commented

Oh well. So be it. I'm pretty disappointed in this though, I have to say. It's a small cost for a small reward. What's so wrong about that? For the record, I think that a more direct approach is probably intrinsically better.

It's a small cost for a small reward

I haven't been able to identify any reward. Upon converting the Liquibase listener to be a run listener, I couldn't see any difference in the time taken getting and calling the application listeners (it was comfortably within the error bars), and that was without considering the possible increase in time taken to call the run listeners.

The cost isn't that small either. In addition to the time taken to make the changes, there's also the fact that some of the conversions would be breaking API changes. LiquibaseServiceLocatorApplicationListener, for example, is public. If someone can present evidence that there's a gain to be had here that's reproducible and more precise than "kind of inefficient" we can reconsider, but right now I don't think any change can be justified as, from what I've seen, it won't make any discernible difference to performance.