A question about qualifier in Spring DI
Normally, a qualified component will be injected to annotated fields with the same qualifier:
@Component class Apple1 implements IApple {}
@Component @Black class Apple2 implements IApple {}
class User {
@Inject IApple apple; // -> Apple1
@Inject @Black IApple blackApple; // -> Apple2
@Inject @Red IApple redApple; // -> Error: not defined
}
What I want is, if a component with specific qualifier isn't de开发者_JAVA技巧fined, I'd like to give a default one, so the redApple
in above example will be injected with an instance of Apple1
.
Is it possible? Or can I implement a specific qualifier matching strategy for Spring DI?
** EDIT **
I know subclass will work, but this is an example to describe the question, so subclass isn't applicable here.
If you try to inject something with a qualify where no bean exists for: like
@Inject @Red IApple redApple;
then you will get an: NoSuchBeanDefinitionException
.
This exception occures no matter if you use:
- @Inject
- @Resource
- @Autowire
The reason is simple: Spring DI first search determine all autowire candidates.
- if there is exactly one, the it uses this candidate
- if there is no candidate, it raise a NoSuchBeanDefinitionException
- if there is more then one, it tryes to determine the primery candidate out of the candiates.
@See org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
line 785..809 (3.0.4.RELEASE)
So what you need to do is putting the fall back (Apple) one in the set of candiates, but make sure that it is only used if there is no other candidate. Because there is no way to mark a bean as fall back or less importend, you need to mark the normal bean as more important: @primary
.
So the (proved) solution would be annotating
- the Black and Red Apple with @Black and @Red and with @Primary.
- the default fallback Apple (Apple1) with with @Red and @Black, but without @Primary.
Example:
@Component @Red @Black public class Apple1 implements IApple {} //fall back
@Component @Black @Primary public class Apple2 implements IApple {}
@Component public class AppleEater {
@Inject @Black IApple blackApple; // -> Apple2
@Inject @Red IApple redApple; // -> Apple1
}
Possible improvement: If you don't like to add all annotations (@Black, @Red, @AllOtherStangeColors) to your fall back bean, you could try to implment your your own AutowireCandiateResolver
so that it adds the fall back bean to all candiate lists of the required type (Apple)
@see Reference Documentation: 3.9.4 CustomAutowireConfigurer
精彩评论