7.5.6 使用自动装配注入合作者Bean

7.5.6 使用自动装配注入合作者Bean

Spring能自动装配BeanBean之间的依赖关系,即无须使用ref显式指定依赖Bean,而是由Spring容器检査XML配置文件内容,根据某种规则,为调用者Bean注入被依赖的Bean

Spring的自动装配

Spring的自动装配
可通过<beans>元素的default-autowire属性指定,该属性对配置文件中所有的Bean起作用;
也可通过<bean.>元素的autowire属性指定,该属性只对该Bean起作用。

从上面介绍不难发现:在同一个Spring容器中完全可以让某些Bean使用自动装配,而另一些Bean不使用自动装配。
自动装配可以减少配置文件的工作量,但降低了依赖关系的透明性和清晰性。

autowire属性值

autowiredefault-autowire属性可以接受如下值。

属性值 描述
no 不使用自动装配。Bean依赖必须通过ref元素定义。这是默认的配置,在较大的部署环境中不鼓励改变这个配置,显式配置合作者能够得到更清晰的依赖关系。
byName 根据setter方法名进行自动装配。 Spring容器查找容器中的全部Bean,找出其idsetter方法名去掉set前缀,并小写首字母后的同名的Bean来完成注入。如果没有找到匹配的Bean实例,则Spring不会进行任何注入。
byType 根据setter方法的形参类型来自动装配。 Spring容器查找容器中的全部Bean,如果正好有一个Bean的类型与setter方法的形参类型匹配,就自动注入这个Bean;如果找到多个这样的Bean,就抛出一个异常;如果没找到这样的Bean,则什么都不会发生, setter方法不会被调用。
constructor byType类似,区别是用于自动匹配构造器的参数。如果容器不能恰好找到一个与构造器参数类型匹配的Bean,则会抛出一个异常。
autodetect Spring容器根据Bean内部结构,自行决定使用constructor还是使用byType策略。如果找到一个默认的构造函数,那么就会应用byType策略。

由此可见, byName策略是根据setter方法的方法名与Beanid进行匹配;byType策略是根据setter方法的参数类型Bean的类型进行匹配。

7.5.6.1 byName规则

byName规则是指setter方法的方法名与Beanid进行匹配,假如Bean A的实现类包含setB()方法,而Spring的配置文件恰好包含idbBean,则Spring容器会将b实例注入Bean A中。如果容器中没有名字匹配的Bean,则Spring不会做任何事情。

程序示例

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\byName
└─src\
├─beans.xml
├─lee\
│ └─SpringTest.java
└─org\
└─crazyit\
└─app\
└─service\
├─Dog.java
├─impl\
│ ├─Chinese.java
│ └─GunDog.java
└─Person.java

beans.xml

看如下配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 指定使用byName自动装配策略,Spring会根据setter方法的方法名与Bean的id进行匹配 -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
autowire="byName" />
<bean id="gunDog" class="org.crazyit.app.service.impl.GunDog">
<property name="name" value="wangwang" />
</bean>
</beans>

Chinese.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.crazyit.app.service.impl;

import org.crazyit.app.service.*;

public class Chinese implements Person
{
private Dog dog;
// dog的setter方法
public void setGunDog(Dog dog)
{
this.dog = dog;
}
public Dog getDog()
{
return this.dog;
}
public void test()
{
System.out.println("我是一个普通人,养了一条狗:"
+ getDog().run());
}
}

上面的配置文件指定了byName自动装配策略,而且Chinese类恰好有如下setter方法:

1
2
3
4
5
// dog的setter方法
public void setGunDog(Dog dog)
{
this.dog = dog;
}

上面setter方法的方法名为setGunDog, Spring容器就会寻找容器中idgunDogBean,

  • 如果能找到这样的Bean,该Bean就会作为调用setGunDog()方法的参数;
  • 如果找不到这样的Bean, Spring就不调用setGunDog()方法

7.5.6.2 byType规则

byType规则是根据setter法的参数类型与Bean的类型进行匹配:

  • 假如A实例有setB(B b)方法,而Spring的配置文件中恰好有一个类型为BBean实例,则容器为A注入类型匹配的Bean实例;
  • 如果容器中没有类型为B的实例, 则Spring不会调用setB()方法;
  • 但如果容器中包含多于一个的B实例,则程序将会抛出异常。

程序示例

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\byType\src
├─beans.xml
├─lee\
│ └─SpringTest.java
└─org\
└─crazyit\
└─app\
└─service\
├─Dog.java
├─impl\
│ ├─Chinese.java
│ ├─GunDog.java
│ └─PetDog.java
└─Person.java

beans.xml

看如下配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 指定使用byType自动装配策略,Spring会根据setter方法的参数类型与Bean的类型进行匹配 -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
autowire="byType" />
<bean id="gunDog" class="org.crazyit.app.service.impl.GunDog">
<property name="name" value="wangwang" />
</bean>
</beans>

上面的配置文件指定了byType自动装配策略,而且Chinese类恰好有如下setter方法。

1
2
3
4
5
// dog的setter方法
public void setDog(Dog dog)
{
this.dog = dog;
}

只有一个类型的Bean对象的情况

上面setter法的形参类型是Dog,而Spring容器中的org.crazyit.app.service.impl.GunDog类实现了Dog接口,因此Spring会以该Bean为参数来调用chinesesetDog方法。

有两个同类型的Bean对象的情况

但如果在配置文件beans.xml中再配置一个Bean,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 指定使用byType自动装配策略,Spring会根据setter方法的参数类型与Bean的类型进行匹配 -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
autowire="byType" />
<bean id="gunDog" class="org.crazyit.app.service.impl.GunDog">
<property name="name" value="wangwang" />
</bean>
<bean id="petDog" class="org.crazyit.app.service.impl.PetDog">
<property name="name" value="ohoh" />
</bean>
</beans>

此时, Spring将无法按byType策略进行自动装配,因为容器中有两个类型为DogBean,Spring无法确定应为chinese bean注入哪个Bean,所以程序将抛出异常.

同时指定自动装配和使用ref显示指定依赖时

当一个Bean既使用自动装配依赖,又使用ref显式指定依赖时,则使用ref显式指定的依赖覆盖自动装配依赖。在如下配置文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 指定使用byType自动装配策略,Spring会根据setter方法的参数类型与Bean的类型进行匹配 -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
autowire="byType">
<property name="dog" ref="petDog" />
</bean>
<bean id="gunDog" class="org.crazyit.app.service.impl.GunDog">
<property name="name" value="wangwang" />
</bean>
<bean id="petDog" class="org.crazyit.app.service.impl.PetDog">
<property name="name" value="ohoh" />
</bean>
</beans>

因为使用ref显式指定的依赖关系将覆盖自动装配的依赖关系,所以自动转配失效,程序不会抛出异常.

对于大型的应用,不鼓励使用自动装配。虽然使用自动装配可减少配置文件的工作量,但大大降低了依赖关系的清晰性和透眀性。依赖关系的装配依赖于源文件的属性名或属性类型,导致BeanBean之间的耦合降低到代码层次,不利于高层次解耦

将某些Bean排除在自动装配之外

在某些情况下,程序希望将某些Bean排除在自动装配之外,不作为Spring自动装配策略的候选者此时可设置autowire-candidate属性,通过为<bean>元素设置autowire-candidate="false",即可将该Bean排除在自动装配之外,容器在查找自动装配Bean时将不考虑该Bean:
除此之外,还可通过在<beans>元素中指定defaul-autowire-candidates属性将一批Bean排除在自动装配之外。 default-autowire-candidates属性的值允许使用模式字符串,例如指定default-autowire-candidates="*abc",则所有以"abc"结尾的Bean都将被排除在自动装配之外。不仅如此,该属性甚至可以指定多个模式字符串,这样所有匹配任一模式字符串的Bean都将被排除在自动装配之外。
例如,把上述配置文件改成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 指定使用byType自动装配策略,Spring会根据setter方法的参数类型与Bean的类型进行匹配 -->
<bean id="chinese" class="org.crazyit.app.service.impl.Chinese"
autowire="byType">
</bean>
<bean id="gunDog" class="org.crazyit.app.service.impl.GunDog">
<property name="name" value="wangwang" />
</bean>
<!-- 配置petDog Bean,其实现类也实现了Dog接口 -->
<bean id="petDog" class="org.crazyit.app.service.impl.PetDog"
autowire-candidate="false">
<property name="name" value="ohoh" />
</bean>
</beans>

这样petDog这个被排除在自动转配之外,则此时只有gunDog符合byType自动装配要求,程序依然可以正常运行.

剩下的两种自动装配策略与byNamebyType大同小异,此处不再赘述。