profile解决了不同环境配置切换的问题,但是对于同一个接口,当有多个实现的时候,Spring无法判断应该使用何种实现,这个可以通过@Primary和@Qualifier注解解决。
@Primary注解标示首选使用的bean,但是当存在多个有@Primary注解bean时,依然有无法判断的问题;
@Qualifier注解,可以通过自定义、组合的方式标示使用哪个bean。
1.接口
package main.java.soundsystem; public interface CompactDisc { void play(); }
2.声明2个bean
package main.java.soundsystem; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class SgtPepperCondition implements Condition { @Override public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) { return true; } }
package main.java.soundsystem; import org.springframework.stereotype.Component; @Component public class YellowSubmarine implements CompactDisc{ private String title ="Yellow Submarine"; private String artist="The Beatles"; @Override public void play() { System.out.println("Playing "+ title +" by "+artist); }{ } }
3.配置文件
package main.java.soundsystem; import org.springframework.context.annotation.*; @Configuration public class CDPlayerConfig { @Bean @Primary public CompactDisc sgtPeppers() { return new SgtPepper(); } @Bean public CompactDisc yellowSubmarine(){ return new YellowSubmarine(); } }
4.测试
package main.java.soundsystem; import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {CDPlayerConfig.class}) public class CDPlayerTest { @Autowired private CompactDisc cd; @Test public void cdShouldNotBeNull() { assertNotNull(cd); cd.play(); } }
在配置文件中,sgtPeppers上增加了@Primary注解,标示这是首选bean,若没有增加这个注解,编译时不会报错,但是运行是会有如下异常
org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type 'main.java.soundsystem.CompactDisc' available:
expected single matching bean but found 2: sgtPeppers,yellowSubmarine
若不使用@Primary注解,可以使用@Qualifier注解
将3.配置文件中的SgtPeppers上增加@Qualifier注解
package main.java.soundsystem; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.*; @Configuration public class CDPlayerConfig { @Bean @Qualifier("peppers") public CompactDisc sgtPeppers() { return new SgtPepper(); } @Bean public CompactDisc yellowSubmarine(){ return new YellowSubmarine(); } }
在4.测试中,CompactDisc上增加相同的@Qualifier注解
package main.java.soundsystem; import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {CDPlayerConfig.class}) public class CDPlayerTest { @Autowired @Qualifier("peppers") private CompactDisc cd; @Test public void cdShouldNotBeNull() { assertNotNull(cd); cd.play(); } }
在初始化cd的时候,会自动寻找带有相同@Qualifier注解的bean,不过如果相同有多个,还是会异常。
为了更好的找到bean,可以创建自定义的限定符注解,借助这样的注解来表达bean所希望限定的特性。
自定义限定符
package main.java.soundsystem; import org.springframework.beans.factory.annotation.Qualifier; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.METHOD,ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Qualifier public @interface ManualQualifier { }
配置文件使用自定限定符
package main.java.soundsystem; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.*; @Configuration public class CDPlayerConfig { @Bean @ManualQualifier public CompactDisc sgtPeppers() { return new SgtPepper(); } @Bean public CompactDisc yellowSubmarine(){ return new YellowSubmarine(); } }
使用自定义限定符
package main.java.soundsystem; import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {CDPlayerConfig.class}) public class CDPlayerTest { @Autowired @ManualQualifier private CompactDisc cd; @Test public void cdShouldNotBeNull() { assertNotNull(cd); cd.play(); } }
对于限定符,是可以使用多个的,通过不同的组合,从根本上解决了使用哪个bean的问题。