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

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="school" class="com.liwl.dev.SchoolImpl">
        <property name="name" value="jiangnan"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号"/>
    </bean>

    <bean id="school02" class="com.liwl.dev.SchoolImpl02">
        <property name="name" value="jiangnan02"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号-02"/>
    </bean>

    <bean id="company" class="com.liwl.dev.CompanyImpl">
        <property name="name" value="nsccwx"/>
        <property name="address" value="江苏省无锡市滨湖区吟白路1号"/>
    </bean>

    <bean id="liwl001" class="com.liwl.dev.LiwanLiangImpl" depends-on="school,company" autowire="byType">
        <property name="name" value="liwl" />
        <property name="age" value="30"/>
    </bean>

</beans>

运行报错:

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

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="school" class="com.liwl.dev.SchoolImpl" primary="true">
        <property name="name" value="jiangnan"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号"/>
    </bean>

    <bean id="school02" class="com.liwl.dev.SchoolImpl02">
        <property name="name" value="jiangnan02"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号-02"/>
    </bean>

    <bean id="company" class="com.liwl.dev.CompanyImpl">
        <property name="name" value="nsccwx"/>
        <property name="address" value="江苏省无锡市滨湖区吟白路1号"/>
    </bean>

    <bean id="liwl001" class="com.liwl.dev.LiwanLiangImpl" depends-on="school,company" autowire="byType">
        <property name="name" value="liwl" />
        <property name="age" value="30"/>
    </bean>

</beans>

运行结果

调用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

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="school" class="com.liwl.dev.SchoolImpl">
        <property name="name" value="jiangnan"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号"/>
    </bean>

    <bean id="school02" class="com.liwl.dev.SchoolImpl02" primary="true">
        <property name="name" value="jiangnan02"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号-02"/>
        <property name="id" value="02"/>
    </bean>

    <bean id="company" class="com.liwl.dev.CompanyImpl">
        <property name="name" value="nsccwx"/>
        <property name="address" value="江苏省无锡市滨湖区吟白路1号"/>
    </bean>

    <bean id="liwl001" class="com.liwl.dev.LiwanLiangImpl" depends-on="school,company" autowire="byType">
        <property name="name" value="liwl" />
        <property name="age" value="30"/>
    </bean>

</beans>

运行结果

调用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,就可以解决这个问题

<bean id="school" class="com.liwl.dev.SchoolImpl" primary="true">时,注入的是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号

<bean id="school02" class="com.liwl.dev.SchoolImpl02" primary="true">时,注入的是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

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd 
        http://www.springframework.org/schema/context 
        https://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="school" class="com.liwl.dev.SchoolImpl">
        <property name="name" value="jiangnan"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号"/>
    </bean>

    <bean id="school02" class="com.liwl.dev.SchoolImpl02" autowire-candidata="false">
        <property name="name" value="jiangnan02"/>
        <property name="address" value="江苏省无锡市滨湖区蠡湖大道1800号-02"/>
    </bean>

    <bean id="company" class="com.liwl.dev.CompanyImpl">
        <property name="name" value="nsccwx"/>
        <property name="address" value="江苏省无锡市滨湖区吟白路1号"/>
    </bean>

    <bean id="liwl001" class="com.liwl.dev.LiwanLiangImpl" depends-on="school,company" autowire="byType">
        <property name="name" value="liwl" />
        <property name="age" value="30"/>
    </bean>

</beans>
posted @ 2022-02-23 16:11  liwldev  阅读(650)  评论(0编辑  收藏  举报