-
Notifications
You must be signed in to change notification settings - Fork 38.3k
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
Allow creation of Beans that cannot be autowired by type unless qualified #26528
Comments
@hjohn I requested this long time ago, and I was responded with a very enlightning answer by (was it Andy Wilkinson?). You can then expose a @bean like
You can even use Lombok's @DeleGate if you want a 1-to-1 functionally equivalent decorator This is exactly the same as defining a theoretical @bean(strictQualifying = true) @qualifier("strict-qualif-implementation") + with a @Autowired @qualifier("strict-qualif-implementation") injection |
Sorry, but I don't consider wrapping every bean that I don't want exposed a very good answer to this problem. A much better way to approach this is how CDI does this. Every bean without a qualifier silently gets a default qualifier added to it, and every injection point defined without a qualifier also gets this default qualifier. Now when you define a bean with your own qualifier, it will not match with an injection point that does not explicitly have that qualifier:
|
Yes, its my same need. In a way, I can understand what I was told, i.e, that if you need to strict qualify Red A everywhere, it is not an A anymore, it is a RedA and must be injected as such. Kinda Liskov. |
I've been bitten by this problem again in a multi module project. When upgrading a library, a bean which was clearly not intended to be used for normal use was injected into an injection point that just expected a default version of that bean. I've taken the time to construct a solution which is somewhat similar to what CDI offers, but in order to remain backwards compatible with how Spring selects candidates by default (given the many projects out there) I've flipped the logic around. With the
Will only get injected into an injection point that specifies the If anyone would like to toy with this, define a configuration to use an alternative
And then use this implementation:
And these two annotations:
|
I would be willing to contribute a PR for this as I have a working solution already, but due to how Please advise if you'd are interested in such a feature at all, and what the chances are for it to get accepted. A description of the feature follows: ProblemSpring currently has no trivial mechanism to prevent autowiring of beans that happen to match the criteria of an unqualified injection point. This means that if some internal bean happens to be part of the context that happens to be of the same type as an injection point expects, it will be autowired even though the bean may be very specialized for a specific purpose. This is a particular problem for large projects where multiple artifacts are combined that may be contributing (intended or not) beans to the overall context. Some of these artifacts may just want to use Spring injection for their own wiring purposes, but have no interest in having their beans also be used by other artifacts. SolutionIn CDI this is prevented by giving all injection points and beans a magic qualifier Solving this in the same manner as CDI does is not possible for Spring as it would break a lot of injections when they suddenly have a ExampleConsider these two beans:
Given the injection points:
... only Falling back to bean namesI'm aware that Spring can take the field or parameter name into account to differentiate between beans of the same type; however, I think this practice should be discouraged as it is all too easy to rename a field (for improved readability or some other reason) only to discover that this has an impact on which bean is selected by autowiring. This can all too easily lead to situations where everything seems to be in order (with a bean that works very similar) but may break when presented with edge cases or extra load (consider a synchronized vs unsynchronized bean, or an Alternative Syntax
ImplementationThe implementation basically only needs to override the
The above code would be the extent of the changes, if it weren't for one small issue; This is because currently in Spring, the two Introducing an
|
I am not a member of the Spring Framework team so it is not for me to say. |
I'm considering a variation of this in the form of declaring specific qualifier types as mandatory, e.g. through In 6.2, we got the opportunity to introduce this as part of a deep revisit of the autowiring algorithm: #28122 |
I eventually went with an approach closer to the original proposal, aligned with the existing As for an |
Thanks a lot, very happy with the new feature, in whatever form fits best with Spring's model, and I'm looking forward to playing with it. The |
I'm seeing more and more problems with complex Spring / Spring Boot applications where beans created in some dependency interfere with the operation of either another dependency or our own code.
Dependencies like to declare Beans because it is convenient to inject them where needed in the dependency's own code. However, some of these beans really shouldn't be public and available for general use. By declaring a Bean, it becomes publicly available, even if given its own name or if it is declared with a Qualifier:
This bean can still be injected anywhere (when it is the only option) because Spring will match it by type:
This is highly undesirable for types that are used commonly by multiple dependencies. Some of the biggest offenders are
ObjectMapper
,ThreadPoolTaskScheduler
, etc. Beans like this often get configured in a specific way for a specific dependency and making them public (by simple declaring them) means that these beans can end up in completely unrelated parts of an application.Spring's injection capabilities are very useful, even in libraries, but just like a library should be able to limit what API is publicly available, it should also be able to decide which of its beans are safe to use in external code (published) and which are not (kept private).
Qualifiers and Bean names are however insufficient due to Spring's matching by type -- a Bean intended for internal use only can still leak out and be used in external code. I therefore suggest allowing Beans to be declared with restrictions on how it can be matched:
Or:
The first declaration would require the wiring site to specify the bean name:
The second would require:
This would NOT match:
In this fashion, library authors can keep certain beans private to the library by keeping the Qualifier private or by using an obscure bean name (use at your own risk in external code).
The suggested names and even the entire mechanism are just for illustration. I could imagine there being different annotations for this purpose:
@NamedBean
-- bean that must match by name to be considered for injection, and@QualifiedBean
-- bean that must match all qualifiers to be considered.The text was updated successfully, but these errors were encountered: