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

@Named annotation implies @Bean (Prototype) #11667

Open
musketyr opened this issue Mar 19, 2025 · 10 comments · May be fixed by micronaut-projects/micronaut-data#3362
Open

@Named annotation implies @Bean (Prototype) #11667

musketyr opened this issue Mar 19, 2025 · 10 comments · May be fixed by micronaut-projects/micronaut-data#3362

Comments

@musketyr
Copy link
Contributor

Expected Behavior

When a class is annotated only with @Named annotation and asked for injection then the bean locator throws NoSuchBeanException because of missing scope annotation such as @Singleton

Actual Behaviour

@Named annotation works as a synonym for @Bean and implies prototype scope. Every time the bean locator is asked for the bean, the new one is created.

Steps To Reproduce

  1. create a bean with only @Named annotation such as @Named("foo") public class NamedBean { }
  2. create injection point or ask bean locator for the bean beanLocator.getBean(NamedBean.class)
  3. new instance of the bean is created every time you ask for the bean

Environment Information

No response

Example Application

https://github.com/musketyr/named-annotation-bean-issue for reproducer

Version

4.7.6

@graemerocher
Copy link
Contributor

this is not a bug and by design. Adding any qualifier will make it a bean with the default scope (prototype)

@musketyr
Copy link
Contributor Author

ok, just related to this I wonder - why was prototype chosen as a default scope instead of singleton as it is in Spring? falling back to prototype when any scope is selected has significant impact on the application performance. @wololock discovered this behavior when he was debugging huge memory leak (thousands of repository instances in the memory).

@wololock
Copy link

Just to give you more context, there's one nasty corner case when injecting a class with only @Named annotation when it also injects the Micronaut Data repository - such a repository will be instantiated for each prototype. When looking at what is injected in such a case, we will see SomeRepository$Intercepted instead of $SomeRepository$Intercepted$Definition. And since each repository has always alive reference from interceptors from DataInterceptorResolver, those one-time-use repositories stay in the memory forever, GC cannot clean them up.

This was our use case - one of our microservices that was using a few @Named service classes without @Singleton annotation started having huge memory issues. Mem consumption was growing at a pretty fast pace, and when we looked at the heap dump, we saw millions of entries inside DataInterceptorResolver.interceptors and hundreds of thousands of duplicated repositories in memory. Basically, each request that used this unlucky @Named service ended up adding a new repository instance to the memory. I don't know if this behavior applies only to repositories - I see in the debugger of our codebase that other injected beans (like ObjectMapper) are not duplicated, but every repository injected generates a new instance each time the @Named only bean is injected (or requested from BeanLocator).

I guess if non-duplicated Microaut Data repositories were injected in such a case (like it happens with other beans), the default behavior wouldn't produce any weird side-effects.

@dstepanov
Copy link
Contributor

@wololock Can you please create a spearate issue with a test?

@musketyr
Copy link
Contributor Author

@dstepanov I think I have enough info to add it to my test repo. let me check.

@dstepanov
Copy link
Contributor

Nevermind, found the problem

@wololock
Copy link

Thank you, @dstepanov !

@musketyr
Copy link
Contributor Author

you're fast. here's the test anyway musketyr/named-annotation-bean-issue#1

@graemerocher
Copy link
Contributor

so this look like a separate bug.

As for Micronaut default vs Spring default one could inverse the question. In CDI and other dependency injectors singleton is not the default.

@musketyr
Copy link
Contributor Author

thanks for explanation @graemerocher. I will keep this in mind. I got the question from my colleague as almost everyone comes from Spring background.

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

Successfully merging a pull request may close this issue.

4 participants