micronaut-projects / micronaut-core

Micronaut Application Framework
http://micronaut.io
Apache License 2.0
6.06k stars 1.06k forks source link

Presence of @Inject annotation causes prototype bean creation #4490

Open jpragma opened 3 years ago

jpragma commented 3 years ago

Looks like the presence of @Inject annotation on any field of the class causes micronaut annotation processor to create BeanDefinition (with @Prototype scope) even if no annotation is specified on the class.

Moreover, if this bean is created by @Factory then 2 separate bean definitions are generated and the app fails with "Multiple possible bean candidates found" error

Gitter discussion

https://gitter.im/micronautfw/questions?at=5fb1a2e72a60f731f758eafd

Steps to Reproduce

  1. Create class MyRepo and annotate it with @Singleton
  2. Create class MyService, do not annotate it with any scope annotations
  3. Inject MyRepo into MyService using field injection and @Inject
  4. Create class @Factory MyFactory, add method to create MyService and annotated this method with @Singleton
  5. Start the context and request MyService from BeanContext twice

Expected Behaviour

Should receive same instance of MySerivce in both calls, since MyService should be a singleton

Actual Behaviour

Fist call to beanContext.getBean(MyService.class) fails with error: "Multiple possible bean candidates found"

Environment Information

Example Application

https://github.com/jpragma/mntests/tree/master/inject-problem-demo Simply execute InjectProblemDemoTest unit test.

graemerocher commented 3 years ago

One thing to consider is future compliance with standards. In CDI beans are allowed to not have a bean defining annotation

jpragma commented 3 years ago

Right, if the bean without any class level annotation was automatically created as Prototype, it would be OK in my opinion as long as this behavior is mentioned in the documentation. However if the bean is created by a Factory, then the annotation on the factory method should probably be used.

At least with @ Signleton it throws "Multiple candidates" exception. I bumped into this issue while using a custom scope. Looks like in this case factory method is completely ignored and prototype scope bean is created.

Just try to replace @ Singleton in MyFactory with e.g. @ RequestScope - there will be no failures, and prototype bean will be created instead of request scoped bean.

jameskleeh commented 3 years ago

@jpragma The factory and the bean itself are not related. If you are in control of the class there should be no reason to use a factory. If you use a factory there should be no reason to apply bean annotations to the class itself.

jpragma commented 3 years ago

@jameskleeh That's my point, the bean itself doesn't have any class level bean annotations, only @Inject (either on a field OR on a constructor). In my projects, I don't have any real problems - I use only constructor injection without any annotations, limit usage of factories, understand (hopefully) their limitations, etc. But... I think other users of micronaut might make same assumptions/mistakes I made initially. I heard from multiple colleagues that they prefer factories over class level bean annotations. This is especially true about those that converted from Spring, where usage of @Configuration instead of classpath scan was quite popular. Maybe it's just a matter of updating micronaut documentation and describing all this behavior...

graemerocher commented 3 years ago

We need to make a decision on this for 3.0 ie should a bean become a bean without a bean defining annotation. In CDI lite annotated mode this is not allowed.