Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider introducing @Eager or similar annotation for bean eager init #835

Open
manovotn opened this issue Jan 20, 2025 · 15 comments
Open
Labels
enhancement New feature or request
Milestone

Comments

@manovotn
Copy link
Contributor

manovotn commented Jan 20, 2025

This has been discussed and requested repeatedly (last I know of was here).
Note that this is not a new feature as the same can be achieved via declaration of Startup event observer in any given bean.
However, that is often perceived as a workaround instead of a solution and is admittedly longer than using an annotation.

The idea would be to introduce an annotation such as @Eager/@Startup (name is subject to change), declarable at bean class level, which would make CDI initialize the bean on container startup.

Some thoughts:

  • This makes most sense for @ApplicationScoped (or @Singleton) beans but can technically work for all scopes
    • Should we forbid it for some scopes? Doesn't seem to make much sense for dependent beans for instance.
    • What about contexts that aren't active at startup? We should probably align this with how a startup event observer would behave.
  • Nothing in CDI specification requires impl to behave lazily (not create a bean until its method is invoked) yet this features indirectly assumes it as it would otherwise have no effect. This doesn't prevent us from adding it; just saying :)

FTR, some discussion about implementation took place in the aforementioned issue (here). It boils down to:

  • Gathering information about eager-init beans during discovery
  • Resolving them after container starts (along the lines of Instance#get())
    • Beans without client proxies will be created as soon as they are resolved.
    • Bean with client proxies will need to be "forcefully" initialized.
      • Dumb version is invoking their toString() which would work in 99% cases.
      • Otherwise needs to manually invoke their creation cycle for which some impls already have code anyway (here's Weld bit)
@manovotn manovotn added the enhancement New feature or request label Jan 20, 2025
@manovotn manovotn added this to the CDI 5.0 milestone Jan 20, 2025
@Ladicek
Copy link
Contributor

Ladicek commented Jan 20, 2025

Agree this doesn't make much sense for other scopes than @Signleton and @ApplicationScoped. For @Dependent, the solution is simple, just create an instance and immediately destroy it; for other scopes, such as @RequestScoped, it's more problematic. I'd say it's probably best to make it a deployment problem if non-singleton beans are @Eager.

Another question: should this be limited to class-based beans? I guess producers and synthetic beans could benefit from this as well.

@Azquelt
Copy link
Member

Azquelt commented Jan 20, 2025

It would seem odd if we used "eager" to mean "created at startup", rather than "created eagerly". I would expect an "eager" @RequestScoped bean, to be created at the start of every request.

IIRC though, there's no requirement for scopes to have a defined beginning?

@Azquelt
Copy link
Member

Azquelt commented Jan 20, 2025

My other thought is that, if eager initialization is only going to be used to run something at startup, then observing Startup to run something at startup is far more logical.

The only problem is when this startup logic is also some kind of initialization for your bean. Having it in @PostConstruct guarantees (almost?) that it runs before any other methods on your bean are called, whereas having it in a Startup observer method doesn't. Then you end up putting your initialization logic in a @PostConstruct method and having an empty Startup observer method, which is ugly.

@manovotn
Copy link
Contributor Author

For @dependent, the solution is simple, just create an instance and immediately destroy it

Sure, but if we are talking an annotation, it doesn't make much sense as you won't execute anything and it will get destroyed immediately?
If you want some logic executed and it is a dependent bean, the observer seem like much better fit.

Another question: should this be limited to class-based beans? I guess producers and synthetic beans could benefit from this as well.

Hm, I assume the idea there is to pre-create beans that are time consuming to create on demand?
However, in such case we are no longer just making it just more convenient than the OM you can currently use as this adds functionality that you cannot otherwise have.

It would seem odd if we used "eager" to mean "created at startup", rather than "created eagerly". I would expect an "eager" @RequestScoped bean, to be created at the start of every request.

Yes, I thought about that as well but personally I find creating beans on scope/context start very awkward.

IIRC though, there's no requirement for scopes to have a defined beginning?

They are basically defined by firing an initialized event which you can observe. Can't think of anything else.

@manovotn
Copy link
Contributor Author

My other thought is that, if eager initialization is only going to be used to run something at startup, then observing Startup to run something at startup is far more logical.

The only problem is when this startup logic is also some kind of initialization for your bean. Having it in @PostConstruct guarantees (almost?) that it runs before any other methods on your bean are called, whereas having it in a Startup observer method doesn't. Then you end up putting your initialization logic in a @PostConstruct method and having an empty Startup observer method, which is ugly.

I basically agree. Yet I keep hearing about @Startup from ejb and omnifaces.
I suppose the other use case is not having application logic executed but just initializing your bean that's time consuming to create right from the get go? Although I am not sure what would have to be inside that bean to make it that costly...

Either way, I am not sold on this annotation either, I just want to create a tracking issue and see if there is any interest in it 🤷

@m-reza-rahman
Copy link

Very much in support of this. I come across this repeatedly. People always miss the event observer. A simple annotation would greatly reduce ongoing confusion.

@tandraschko
Copy link

+1 for @Eager as shortcut to @Overserves @Initialized(SomeScope) Object event

@kiswanij
Copy link

I didn't follow up with the spec since while, but if its not there ( specially in the App scope and Singleton, as mentioned in one of the comments) it should be; in many designs we desire to fail early in case of mis-configuration or initialization problems than in th middle of a process that has customers engagement. There is alot to learn from the Servlets specs.

@manovotn
Copy link
Contributor Author

I didn't follow up with the spec since while, but if its not there ( specially in the App scope and Singleton, as mentioned in one of the comments) it should be; in many designs we desire to fail early in case of mis-configuration or initialization problems than in th middle of a process that has customers engagement. There is alot to learn from the Servlets specs.

@kiswanij I am not sure I understand the case you want to cover with this - detection of misconfiguration can be performed without initializing beans. Or, if you need/want to do it this way, it probably means you will have to place it in post construct in which case you might as well just have observer for Startup event?

+1 for @eager as shortcut to @Overserves @initialized(SomeScope) Object event

@tandraschko Ok, that's slightly different. The initial comment describes after startup initialization - a shorthand for @Observes Startup essentially.
What use case do you have in mind for annotation in combination with context initialization? I mean, in which case is the annotation better than observing that particular context event?

@BalusC
Copy link
Member

BalusC commented Jan 21, 2025

OmniFaces has one since a decade: https://showcase.omnifaces.org/cdi/Eager. It could be used as an example.

@tandraschko
Copy link

tandraschko commented Jan 21, 2025

i have some in my mind:

  • using @Initialized(RequestScoped.class) for tracing requests or similar stuff you would do with servlet listeners
  • using @Initialized(SessionScoped.class) for initializing user-data e.g. after authentication

i know, both are possible with plain e.g. jakarta security or servlet artifacts / events but also via CDI

therefore @Eager would be just more reuseable as @Startup

@Azquelt
Copy link
Member

Azquelt commented Jan 21, 2025

+1 for @Eager as shortcut to @Overserves @Initialized(SomeScope) Object event

I quite like this because it's general and can be easily generalised to any normal scope which fires an @Initialized event.

However, one of the reasons for introducing the Startup event in #496 was because some runtimes may initialize the application scope at build time. If we're making an "easy" alternative to observing events, we probably wouldn't want an @Eager @ApplicationScoped bean to initialize at build time, so we'd at least need an exception for application scoped beans.

@benjamin-confino
Copy link
Contributor

Dumb version is invoking their toString() which would work in 99% cases.

I just want to chime in and say I've seen support tickets where the problem turned out come from calling toString() on CDI beans when writing out trace. So I'd recommend against this. calling equals(null) would be a better dumb solution since the contract for equals() says it returns false if you pass in null; and that's usually implemented by putting a null check with a return statement at the top of the equals method.

Apart from that I'll just note I like @Eager.

@Ladicek
Copy link
Contributor

Ladicek commented Jan 21, 2025

I've seen support tickets where the problem turned out come from calling toString() on CDI beans when writing out trace. So I'd recommend against this. calling equals(null) would be a better dumb solution

Unfortunately the only method declared on Object that is guaranteed to be forwarded to the contextual instance is toString(), see https://jakarta.ee/specifications/cdi/4.1/jakarta-cdi-spec-4.1.html#client_proxy_invocation

@manovotn
Copy link
Contributor Author

Ladislav is right on the money - there is no other method which can be used according to the specification.
That being said, impls should have other means to initialize beans so we needn't do this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants