Spring in action读书笔记(四) Spring 处理自动装配的歧义性

1、 有歧义的bean

        自动装配中仅有一个bean匹配所需要的结果时,自动装配才是有效的。如果有多个bean能够匹配结果的话,

这种奇异性会阻碍Spring自动装配属性,构造器参数或方法参数。

        假设定义了一个接口。

package test;

public interface Interface {
}

有两个实现类:

package test;

import org.springframework.stereotype.Component;

@Component
public class Impl1 implements Interface {
}
package test;

import org.springframework.stereotype.Component;

@Component
public class Impl2 implements Interface {
}

创建配置类InterfaceConfig

package test;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackageClasses = Interface.class)
public class InterfaceConfig {


}

这个时候如果注入Interface属性,程序将报错。

package test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {InterfaceConfig.class})
public class TestAutowired {

    @Autowired
    private Interface imp;

    @Test
    public void test() {
        System.out.println(imp);
    }

}

错误信息:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'test.TestAutowired': Unsatisfied dependency expressed through field 'imp'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'test.Interface' available: expected single matching bean but found 2: impl1,impl2

大致是说产生了歧义性,无法确定装配哪一个bean

2、 使用@Primary注解,标记首选的bean

package test;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Primary
public class Impl1 implements Interface {
}

 缺点: 如果标记了两个或更多的首选bean,仍然会产生歧义性。

3、在需要注入参数的地方,使用限定符@Qualifier注解,指定装配bean 的ID

package test;

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.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {InterfaceConfig.class})
public class TestAutowired {

    @Autowired
    @Qualifier("impl2")
    private Interface imp;

    @Test
    public void test() {
        System.out.println(imp);
    }

}

缺点: 有类型强耦合,对类名的改动会导致限定符失效

4、在bean上使用@Qualifier设定限定符,在注入的地方使用相同的限定符。

package test;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Qualifier("1")
public class Impl1 implements Interface {
}

注入:

package test;

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.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {InterfaceConfig.class})
public class TestAutowired {

    @Autowired
    @Qualifier("1")
    private Interface imp;

    @Test
    public void test() {
        System.out.println(imp);
    }

}

 不足: 如果多个bean都具备相同特性,会再次遇到歧义性的问题。

例如:对Impl2增加同样的注解,这时会产生和步骤1中同样的问题;

package test;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
@Qualifier("1")
public class Impl2 implements Interface {
}

由于@Qualifier注解不可重复, 这时没有直接的方法将自动装配的bean缩小范围到仅有一个可选的bean

5、自定义限定符注解

package test;

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 Interface1 {
}

在bean类及需要注入的地方添加该注解:

package test;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

@Component
@Interface1
public class Impl1 implements Interface {
}

 

package test;

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.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {InterfaceConfig.class})
public class TestAutowired {

    @Autowired
    @Interface1
    private Interface imp;

    @Test
    public void test() {
        System.out.println(imp);
    }

}

可以通过多个自定义注解,缩减满足bean装配的范围。

posted @ 2019-11-26 07:40  笪笠  阅读(135)  评论(0编辑  收藏  举报