Spring中xml标签primary使用


1. 问题

在项目源码目录/root/liwl_dev/java/web/spring/SpringDI/src/main/java/com/liwl/dev

创建新的School实现类SchoolImpl02.java

比SchoolImpl多了一个属性id,以示区别

package com.liwl.dev;

public class SchoolImpl02 extends SchoolImpl{

    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "[SchoolImpl02 bean]" + ",id" + id;
    }

    @Override
    public void destroy() throws Exception {
       System.out.println("SchoolImpl02 bean 销毁"); 
    }
    
}

修改LiwanLiangImpl.java

package com.liwl.dev;

public class LiwanLiangImpl implements LiwanLiang{
    
    private String name;
    private int age;
    private School school;
    private School school02;
    private Company company;

    public LiwanLiangImpl(){
        System.out.println("调用LiwanLiangImpl bean 无参构造方法");
    }

    public LiwanLiangImpl(String name,int age,School school){
        System.out.println("调用LiwanLiangImpl bean 有参构造方法");
        this.name = name;
        this.age = age;
        this.school = school;
    }

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }

    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }

    public School getSchool02() {
        return school02;
    }

    public void setSchool02(School school02) {
        this.school02 = school02;
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }


    @Override
    public String toString() {
        return "[LiwanLiangImpl bean]" + ",name:" + name + ",age:" + age + ",school:" + school + ",company:" + company + ",school02:" + school02; 
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("LiwanLiangImpl bean 销毁");
    }
}

修改spingdi.xml



    
        
        
    

    
        
        
    

    
        
        
    

    
        
        
    


运行报错:

WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'liwl001' defined in class path resource [springdi.xml]: Unsatisfied dependency expressed through bean property 'school'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.liwl.dev.School' available: expected single matching bean but found 2: school,school02

大概意思就是:

在LiwanLiangImpl.java里面,我们依赖了同一个School类型的属性:school和school02。但是通过xml里面的标签byType时,加载到Spring容器里面两个School类型的bean:SchoolImpl和SchoolImpl01,所以Spring无从知道该如何自动装载bean=school还是bean=school2,因为它们都是同一个类型的。

2. 解决

上述解决就需要通过xml的标签primary来处理

修改springdi.xml



    
        
        
    

    
        
        
    

    
        
        
    

    
        
        
    


运行结果

调用SchoolImpl bean 无参构造方法
调用SchoolImpl bean 无参构造方法
调用CompanyImpl bean 无参构造方法
调用LiwanLiangImpl bean 无参构造方法
[LiwanLiangImpl bean],name:liwl,age:30,school:[SchoolImpl bean],name:jiangnan,address:江苏省无锡市滨湖区蠡湖大道1800号,company:[CompanyImpl bean],name:nsccwx,address:江苏省无锡市滨湖区吟白路1号,school02:[SchoolImpl bean],name:jiangnan,address:江苏省无锡市滨湖区蠡湖大道1800号
LiwanLiangImpl bean 销毁
CompanyImpl bean 销毁
SchoolImpl02 bean 销毁
SchoolImpl bean 销毁

修改springdi.xml



    
        
        
    

    
        
        
        
    

    
        
        
    

    
        
        
    


运行结果

调用SchoolImpl bean 无参构造方法
调用SchoolImpl bean 无参构造方法
调用CompanyImpl bean 无参构造方法
调用LiwanLiangImpl bean 无参构造方法
[LiwanLiangImpl bean],name:liwl,age:30,school:[SchoolImpl02 bean],id:2,company:[CompanyImpl bean],name:nsccwx,address:江苏省无锡市滨湖区吟白路1号,school02:[SchoolImpl02 bean],id:2
LiwanLiangImpl bean 销毁
CompanyImpl bean 销毁
SchoolImpl02 bean 销毁
SchoolImpl bean 销毁

3. 结论

对比上面的结果可以发现
LiwanLiangImpl里面注入了两个School的实现类的bean,xml文件也对应的配置了两个bean。

当通过byType标签,为bean:liwanliang自动注入时,因为Spring容器里面存在着2个SchoolImpl的bean,因此Spring无法正确通过类型为liwanliang注入。

这个时候,通过primary标签的值[true/false]来指定 有限注入哪个bean,就可以解决这个问题

时,注入的是SchoolImpl的bean,所以结果是:

[LiwanLiangImpl bean],name:liwl,age:30,school:[SchoolImpl bean],name:jiangnan,address:江苏省无锡市滨湖区蠡湖大道1800号,company:[CompanyImpl bean],name:nsccwx,address:江苏省无锡市滨湖区吟白路1号,school02:[SchoolImpl bean],name:jiangnan,address:江苏省无锡市滨湖区蠡湖大道1800号

时,注入的是SchoolImpl02的bean,所以结果是:

[LiwanLiangImpl bean],name:liwl,age:30,school:[SchoolImpl02 bean],id:2,company:[CompanyImpl bean],name:nsccwx,address:江苏省无锡市滨湖区吟白路1号,school02:[SchoolImpl02 bean],id:2

4. 拓展

xml配置标签primary能够指定配置在xml里面的bean,哪些是作为优先注入的bean。primary用在autowire=byType时,作用很明显。

另外一个标签autowire-candidate,这个跟autowire=byType时有所异同。

bean的autowire-candidata默认值为true,所以想要优先选择某个bean,需要把另外一个bean的该标签设置为false即可

比如下面的配置,就会往LiwanLiangImpl注入school,而非shool002