问题思考

  • 自动装配是什么?
  • 自动装配解决了什么问题?

没有自动装配的时候是怎么注入依赖的?

假设用xml方式进行元数据配置,如下:

未使用自动装配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Person {

private Superpower superpower;

public Superpower getSuperpower() {
return superpower;
}

public void setSuperpower(Superpower superpower) {
this.superpower = superpower;
}
}

public class Superpower {

private String name;

private String description;

public String getName() {
return name;
}

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

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}
}

// xml配置
<!--配置Bean-->
<bean id="person" class="com.hph.model.Person">
<!--配置JavaBean的属性-->
<property name="superpower" ref="superpower"/>
</bean>

<bean id="superpower" class="com.hph.model.Superpower">
<property name="name" value="闪电侠"/>
<property name="description" value="超能力"/>
</bean>

Person通过Setter方式使用ref属性注入Superpower类型的依赖项bean

执行结果:

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {

//创建Spring上下文(加载spring-config.xml文件)
ClassPathXmlApplicationContext classPathXmlApplicationContext =
new ClassPathXmlApplicationContext("test-config.xml");

Person person = (Person)classPathXmlApplicationContext.getBean("person");

System.out.println(person.getSuperpower().getName()+"------"+person.getSuperpower().getDescription());
}

打印输出:

image-20251025193722184

使用自动装配

改xml配置即可(以byName为例)

1
2
3
4
5
6
7
<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byName" />

<bean id="superpower" class="com.hph.model.Superpower">
<property name="name" value="闪电侠"/>
<property name="description" value="超能力"/>
</bean>

打印输出:

image-20251025194014051

可以发现,当使用自动装配的方式后,只需要加autowire属性即可将依赖项注入进来,不需要写property属性进行显示配置需要注入哪些依赖项;

那么,可以知道,自动装配是为了解决依赖配置复杂的问题

autowire不同类型之间的区别

官方给出四种自动装配模式,分别为no、byName、byType和constructor,下面看看byName、byType和constructor这三种模式的区别

byName

当bean查找并且注入依赖项时,通过依赖项变量名进行匹配,并且是基于Setter注入

代码如下:

当bean person注入名称为superpowerA的依赖项时,通过与id或name进行查找匹配的bean进行注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Person {

// 名称为superpowerA
private Superpower superpowerA;

public Superpower getSuperpowerA() {
return superpowerA;
}

public void setSuperpowerA(Superpower superpowerA) {

this.superpowerA = superpowerA;
}
}

public interface Superpower {

void release();
}

public class SuperpowerA implements Superpower {

public void release() {

System.out.println("释放超能力SuperpowerA:");
}
}

public class SuperpowerB implements Superpower {

public void release() {

System.out.println("释放超能力SuperpowerB");
}
}

// xml配置
<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byName"/>

// 匹配到这个bean进行注入
<bean id="superpowerA" class="com.hph.model.SuperpowerA"/>

<bean id="superpowerB" class="com.hph.model.SuperpowerB"/>

打印输入:

image-20251026001306515

为了验证是否是通过id或name进行匹配的,改下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Person {

// 名称改为superpowerB
private Superpower superpowerB;


public Superpower getSuperpowerB() {
return superpowerB;
}

public void setSuperpowerB(Superpower superpowerB) {
this.superpowerB = superpowerB;
}
}

部分代码省略…………

// xml配置
<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byName"/>

<bean id="superpowerA" class="com.hph.model.SuperpowerA"/>

// 匹配这个bean进行注入
<bean id="superpowerB" class="com.hph.model.SuperpowerB"/>

打印输出:

image-20251026001633232

证明通过id进行匹配

验证是否通过name进行匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Person {

// 名称为superpowerB
private Superpower superpowerB;


public Superpower getSuperpowerB() {
return superpowerB;
}

public void setSuperpowerB(Superpower superpowerB) {
this.superpowerB = superpowerB;
}
}

部分代码省略…………

// xml配置
<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byName"/>

// 添加name属性值为superpowerB
<bean id="superpowerA" name="superpowerB" class="com.hph.model.SuperpowerA"/>

<bean id="superpowerBB" name="a" class="com.hph.model.SuperpowerB"/>

打印输出:

image-20251026001908465

确实匹配了name

疑问:如果id和name存在相同会怎样?

改代码:

1
2
3
4
5
6
7
8
9
部分代码省略…………

<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byName"/>

// name值和下面那个bean的id相同
<bean id="superpowerA" name="superpowerB" class="com.hph.model.SuperpowerA"/>

<bean id="superpowerB" name="a" class="com.hph.model.SuperpowerB"/>

打印输出:直接抛异常,提示superpowerB已被使用

image-20251026002351090

验证:是否是基于Setter进行注入

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Person {

// 名称为superpowerA
private Superpower superpowerA;

public Superpower getSuperpowerA() {
return superpowerA;
}

public void setSuperpowerA(Superpower superpowerA) {

// 标准输出打印
System.out.println("通过Setter方式进行注入");
this.superpowerA = superpowerA;
}
}

public interface Superpower {

void release();
}

部分代码省略…………

// xml配置
<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byName"/>

// 匹配到这个bean进行注入
<bean id="superpowerA" class="com.hph.model.SuperpowerA"/>

<bean id="superpowerB" class="com.hph.model.SuperpowerB"/>

打印输出:

image-20251026003110668

再加一个构造函数,验证是否会通过构造函数进行注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Person {

// 名称为superpowerA
private Superpower superpowerA;

// 新增带参构造函数
public Person(Superpower superpowerA) {
this.superpowerA = superpowerA;
}

public Superpower getSuperpowerA() {
return superpowerA;
}

public void setSuperpowerA(Superpower superpowerA) {

// 标准输出打印
System.out.println("通过Setter方式进行注入");
this.superpowerA = superpowerA;
}
}

public interface Superpower {

void release();
}

部分代码省略…………

// xml配置
<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byName"/>

// 匹配到这个bean进行注入
<bean id="superpowerA" class="com.hph.model.SuperpowerA"/>

<bean id="superpowerB" class="com.hph.model.SuperpowerB"/>

打印输出:抛异常,提示在创建bean person的时候没有找到默认构造函数

image-20251026003400078

byType

当bean查找并且注入依赖项时,通过依赖项类型进行匹配,并且是基于Setter注入

代码如下

bean person注入类型为Superpower的bean,但是该类型有两个实现类SuperpowerA和SuperpowerB,通过类型查找时不知道注入哪一个,所以会抛异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Person {

private Superpower superpower;

public Superpower getSuperpower() {
return superpower;
}

public void setSuperpower(Superpower superpower) {

this.superpower = superpower;
}
}

public interface Superpower {

void release();
}

public class SuperpowerA implements Superpower {

public void release() {

System.out.println("释放超能力SuperpowerA:");
}
}

public class SuperpowerB implements Superpower {

public void release() {

System.out.println("释放超能力SuperpowerB");
}
}

// xml配置
<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byType"/>

// 实现类SuperpowerA
<bean id="superpowerA" class="com.hph.model.SuperpowerA"/>

// 实现类SuperpowerB
<bean id="superpowerB" class="com.hph.model.SuperpowerB"/>

打印输出:提示找到两个匹配的bean

image-20251026105319809

当减少为一个时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Person {

private Superpower superpower;

public Superpower getSuperpower() {
return superpower;
}

public void setSuperpower(Superpower superpower) {

this.superpower = superpower;
}
}

部分代码省略…………

// xml配置
<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byType"/>
// 只保留一个
<bean id="superpowerA" class="com.hph.model.SuperpowerA"/>

打印输出:正常

image-20251026105532510

那如果有多个实现类,并且能正常注入呢

可以通过primary属性指定首选的注入bean

1
2
3
4
5
6
7
8
9
部分代码省略…………

<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="byType"/>

<bean id="superpowerA" class="com.hph.model.SuperpowerA"/>

// 指定该bean为首选注入
<bean id="superpowerB" class="com.hph.model.SuperpowerB" primary="true"/>

打印输出:

image-20251026110049254

constructor

这种方式和byType类似,不同的是constructor是基于构造函数注入的,也就是说依赖项注入的时机不一样,byType是实例化之后通过setter方法注入,而constructor是在实例化的时候就开始注入

代码如下:

Person中没有定义带参数的构造函数,默认无参构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Person {

private Superpower superpower;

public Superpower getSuperpower() {
return superpower;
}

public void setSuperpower(Superpower superpower) {

this.superpower = superpower;
}
}

public interface Superpower {

void release();
}

public class SuperpowerA implements Superpower {

public void release() {

System.out.println("释放超能力SuperpowerA:");
}
}

public class SuperpowerB implements Superpower {

public void release() {

System.out.println("释放超能力SuperpowerB");
}
}

// xml配置
<!--配置Bean-->
// constructor类型
<bean id="person" class="com.hph.model.Person" autowire="constructor"/>

// 实现类SuperpowerA
<bean id="superpowerA" class="com.hph.model.SuperpowerA"/>

// 实现类SuperpowerB
<bean id="superpowerB" class="com.hph.model.SuperpowerB"/>

打印输出:提示空指针,依赖没有注入进来,因为没有定义带Superpower类型参数的构造函数,通过默认无参构造函数实例化,没有注入Superpower依赖项

Person中有定义setter方法,也恰好说明了constructor类型不是通过setter进行注入的

image-20251026111037587

定义带Superpower类型参数的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Person {

private Superpower superpower;

// 新增带Superpower类型参数的构造函数
public Person(Superpower superpower) {
this.superpower = superpower;
}

public Superpower getSuperpower() {
return superpower;
}

public void setSuperpower(Superpower superpower) {

this.superpower = superpower;
}
}

部分代码省略…………

打印输出:空指针没有了,提示存在相同类型的多个bean

image-20251026111601740

和byType一样,通过primary属性指定首选依赖项

1
2
3
4
5
6
7
8
部分代码省略…………  

<!--配置Bean-->
<bean id="person" class="com.hph.model.Person" autowire="constructor"/>

<bean id="superpowerA" class="com.hph.model.SuperpowerA" primary="true"/>

<bean id="superpowerB" class="com.hph.model.SuperpowerB"/>

打印输出:

image-20251026111758431