自动装配

以上演示的这些例子全都是手动来实现一个bean的装配的,需要手动一个个去写标签,手动去指定需要注入的属性,功能不复杂实现起来还是很简单的,但是在日后的开发中日积月累,bean的数量肯定会越来越多,如果通过这样的方式再去一个个手动的装配bean,那工作量还是巨大的,于是乎,Spring提供了更方便的自动装配方式,下面我们一起来学习Bean的自动装配。

Spring中提供了三种装配的方式:

  1. 在xml中显式的配置
  2. 在java中显式的配置
  3. 隐式的自动装配

1. byName的装配方式

示例场景:

Spring容器中包含两个bean

一个son,一个father

其中father中包含一个son的属性,这里演示使用的autowire="byName"属性来实现将son装配到father的bean中

这里先列出示例代码:

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="son" class="cc.leeleo.entity.Son">
        <property name="name" value="erzi"/>
    </bean>
    <bean id="father" class="cc.leeleo.entity.Father">
        <property name="name" value="baba" />
        <property name="son" ref="son" />
    </bean>
</beans>

Father.java 由于我们需要给装配一个son的变量,所以在Father增加了setSon()方法用于注入

public class Father {
    private String name;
    private Son son;

    public void saySon(){
        System.out.println(this.name+"说:erzi");
    }

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

    public void setSon(Son son) {
        this.son = son;
    }

		public Son getSon() {
        return son;
    }
}

Son.java

public class Son {
    private String name;

    public void sayDaddy(){
        System.out.println(this.name+"说:baba");
    }

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

}

好了 重点来了,

原先在beans.xml中我们可以看到在注入son这个对象的时候,我们需要通过ref属性来指定要注入的bean。

现在我们可以通过Spring提供的auto wired属性来省去这一步。

image-1659954433014

我们看到bean标签的autowire的属性中包含五个值,

  • byName顾名思义,Spring容器可以通过名称(bean的id、别名)去上下文中查找到对应id的bean将其装配
  • byType顾名思义,Spring容器可以通过类型(bean的class)去上下文中查找到对应类型的bean将其装配。
  • byConstructor顾名思义,可以通过(bean的constructor)查找相似构造器的方式来实现自动装配。

这里方便演示我们暂且只将byName和byType

首先我们先删除bean中如以下属性

~~<**property name~="son" ref="son"** />~~

并bean中加入该属性**autowire="byName"**

完整代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
   <bean id="son" class="cc.leeleo.entity.Son">
        <property name="name" value="erzi"/>
    </bean>
    <bean id="father" class="cc.leeleo.entity.Father" autowire="byName">
        <property name="name" value="baba" />
    </bean>
</beans>

接下来我们测试一下

image-1659954444931

可以看到由于我们定义的Father类中的Son的set方法名称setSon和beans中定义的bean id = “son”一致,所以装配成功,我们再试下更改bean的id

image-1659954454027

此时我们发现,由于spring找不到装配时所需的set方法,此时为了验证其到底是不是根据set方法装配,我们将setSon方法更改为setSon1再来看下

image-1659954461075

此时便验证了我们之前所说,使用byName方式Spring会

根据需要装配的bean的set方法后的id值,去查找上下文中包含该id的bean进行装配

需要注意的是 使用byName的方式需要保证装配bean的id是唯一的,并且这个bean需要和注入属性的set方法名的值一致

2. byType的装配方式

这里我们更改**autowire="byType" ,**再试一下,使用此种方式可以将bean中的id属性干掉不用

image-1659954471829

由此得知,使用byType方式

Spring会在上下文中,根据需要装配的对象类型,在上下文中查找对应类型的bean进行装配

通过这两种方式都能完成对bean中所包含的对象进行装配,但是有利有弊

  • 利:能省去xml中的配置代码,无需手动再去配置注入对象

    <property name~=“son” ref=“son” />

  • 弊:在开发过程中,我们并不能直观的从的配置中看到注入的类是谁,需要到单独到bean所对应的类中去查看

需要注意的是 使用byType的方式需要保证装配的bean的类上下文中是唯一的

3. 使用注解的装配方式

在使用了上面两种方式进行装配以后,虽然是便利了些,但是还是差点意思,Spring 还提供了一种更爽的配置方式,注解!

jdk1.5支持的注解,Spring2.5就支持注解了使用注解须知:

  1. 导入约束:context约束
  2. 配置注解的支持:context:annotation-config/

image-1659954483318

完整示例:

beans.xml 这里我们添加上面的三个步骤,并且添加两个bean,这里细心的小伙伴们可能就发现了,咱们bean里面只有一个class属性,id和autowire都干掉了,代码又干净了不少。

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <!--开启注解支持-->
    <context:annotation-config/>

		<bean class="cc.leeleo.entity.Son">
        <property name="name" value="erzi"/>
    </bean>
    <bean class="cc.leeleo.entity.Father">
        <property name="name" value="baba"/>
    </bean>
</beans>

Father.java 这里我们把setSon的方法干掉!并且在son属性上加上@Autowired注解

public class Father {
    private String name;

    @Autowired
    private Son son;

    public void saySon(){
        System.out.println(this.name+"说:erzi");
    }

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

    public Son getSon() {
        return son;
    }
}

上面的代码是不是又简洁了不少?接下来我们再测试下

image-1659954498639

成功执行~

@Autowired 可以在属性上使用 也可以在set方法上使用,使用该方式可以省略set方法

4. Qualifier 注解

跟@Autowired一起使用的注解还有@Qualifier 注解,它的使用场景是,当一个spring的容器中存在多个相同类型的bean,这里就可以指定你使用哪个bean作为注入的对象

还是上面的代码,这里我们更改一下Fater类,增加@Qualifier注解,指定注入的bean为son2

public class Father {
    private String name;

    @Autowired
    @Qualifier(value = "son2")
    private Son son;

    public void saySon(){
        System.out.println(this.name+"说:erzi");
    }

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

    public Son getSon() {
        return son;
    }
}

还有beans.xml,新增一个bean

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <!--开启注解支持-->
    <context:annotation-config/>

    <bean class="cc.leeleo.entity.Son">
        <property name="name" value="erzi"/>
    </bean>
    <bean id="son2" class="cc.leeleo.entity.Son">
        <property name="name" value="xiaoerzi"/>
    </bean>
    <bean class="cc.leeleo.entity.Father">
        <property name="name" value="baba"/>
    </bean>
</beans>

测试:

image-1659954512613

需要注意的是,@Qualifier 的value值指定的是Spring上下文中存在的bean的id

5. @Resource注解

除了Spring提供的@Autowired注解以外,java也提供了一个@Resource注解能实现相同的功能,这里我们废话不多说 上代码:

beans.xml 这里我们不做修改

<?xml version="1.0" encoding="UTF-8"?>
<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">

    <!--开启注解支持-->
    <context:annotation-config/>

    <bean class="cc.leeleo.entity.Son">
        <property name="name" value="erzi"/>
    </bean>
    <bean class="cc.leeleo.entity.Father">
        <property name="name" value="baba"/>
    </bean>
</beans>

Father.java 这里我们使用@Resource注解

public class Father {
    private String name;

    @Resource
    private Son son;

    public void saySon(){
        System.out.println(this.name+"说:erzi");
    }

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

    public Son getSon() {
        return son;
    }
}

测试:

image-1659954524770

如果有多个相同类型的bean,我们也可以通过@Resource(name = “xxx”)来指定所需的bean,

上代码:

Father.java 修改注解,指定name值为xml中bean的id

@Resource(name = "son2")
private Son son;

beans.xml 新增一个bean

<bean id="son2" class="cc.leeleo.entity.Son">
    <property name="name" value="xiaoerzi"/>
</bean>

测试:

image-1659954534538

依旧没有问题~

  • @Autowired@Resource区别:
  • 都是用来自动装配的,都可以放在属性字段上
  • @Autowired通过byType实现,必须要求这个对象存在
  • @Resoutce默认通过byName方式实现,如果找不到名字,则通过byType实现
  • 执行顺序不同:@Autowired通过byType实现,@Resoutce通过byName实现