Spring 实战-第三章-处理自动装配的歧义性

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的问题。

原文地址:https://www.cnblogs.com/lvjianwei/p/7490547.html