水滴石穿-@Autowired和@Resouce的装配详解

本文最后更新于:May 13, 2023 pm

积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里,不积小流无以成江海。齐骥一跃,不能十步,驽马十驾,功不在舍。面对悬崖峭壁,一百年也看不出一条裂缝来,但用斧凿,能进一寸进一寸,能进一尺进一尺,不断积累,飞跃必来,突破随之。

目录

首先,我们知道的是@Autowired默认按byType自动装配,而@Resource默认byName自动装配。@Autowired只包含一个参数:required,表示是否开启自动准入,默认是true。而@Resource包含七个参数,其中最重要的两个参数是:name 和 type。@Autowired如果要使用byName,需要使用@Qualifier一起配合。而@Resource如果指定了name,则用byName自动装配,如果指定了type,则用byType自动装配。

这篇文章主要记录他们具体的一个装配顺序。

准备

这里有一个接口,后面就通过装配它来进行测试:

1
2
3
public interface Mapper {
void show();
}

@Autowired

首先看一下@Autowired的装配顺序:

默认装配

默认情况下是通过byType自动装配的。先写一个实现类,如下所示:

1
2
3
4
5
6
7
8
9
@Service
public class MapperImpl implements Mapper{

@Override
public void show() {
System.out.println("MapperImpl-实现类");
}
}

然后测试:

1
2
3
4
5
6
7
8
@Autowired
private Mapper mapper; // 属性名称为mapper或者mappertest等等均可以

@Test
void contextLoads() {
System.out.println(mapper);
mapper.show();
}

是可以成功的。不管属性的名称是否为mapper,都可以成功运行。

多实现

然后我们再写一个实现类:

1
2
3
4
5
6
7
8
@Service
public class MapperImpl2 implements Mapper{
@Override
public void show() {
System.out.println("MapperImpl2-实现类");
}
}

这时,我们再去跑刚才的测试代码,是会报错的。因为现在Mapper有多个实现类了,同一类型出现了多个的情况。而且我们也没有使用@Qualifier指定名称,所以这时它会去找Bean名称为mapper的,但是没有找到,所以报错了。但是,如果我们将属性的名称改为mapperImpl或者是mapperImpl2就可以正常运行,如下:

1
2
3
4
5
6
7
8
@Autowired
private Mapper mapperImpl; // 或者mapperImpl2,只能是这两个

@Test
void contextLoads() {
System.out.println(mapperImpl);
mapperImpl.show();
}

更换不同的属性名称,执行的方法不同,最后输出的结果不同。这就是在有多类型的情况下,自动通过Bean名称进行查找。

指定名称

使用指定名称,那么属性的名称就又可以任意了,如下:

1
2
3
4
5
6
7
8
9
@Qualifier("mapperImpl") // 指定某个Bean的名称
@Autowired
private Mapper mapper; // 变量命名任意

@Test
void contextLoads() {
System.out.println(mapper);
mapper.show();
}

执行的方法也就是指定的Bean的名称中的方法。

@Resource

在上面,我们已经有了两个实现类,在此基础上完成下面的测试。

同时指定name和type

装配顺序如图:

示例:

1
2
3
4
5
6
7
8
@Resource(name = "mapperImpl",type = Mapper.class)
private Mapper mapper;

@Test
void contextLoads() {
System.out.println(mapper);
mapper.show();
}

这里,我们指定了类型为Mapper,而且名称也必须是mapperImpl,当然了,名称也可以是mapperImpl2,根据情况而定。但是,如果指定的这两个不能唯一找到,则会报错。

指定name

装配顺序如图:

示例:

1
2
3
4
5
6
7
8
@Resource(name = "mapperImpl")
private Mapper mapper;

@Test
void contextLoads() {
System.out.println(mapper);
mapper.show();
}

可以正常执行,但是,如果有多个类型有相同的Bean名称为mapperImpl,则会报错。

指定type

装配顺序如图:

示例:

1
2
3
4
5
6
7
8
@Resource(type = Mapper.class)
private Mapper mapper;

@Test
void contextLoads() {
System.out.println(mapper);
mapper.show();
}

因为有多个类型,会找到多个,所以这里会报错。但是,如果属性的名称和某一个Bean的名称相同,则是可以正常运行的,如下:

1
2
3
4
5
6
7
8
@Resource(type = Mapper.class)
private Mapper mapperImpl; // 或者是mapperImpl2 两者都可以正常运行

@Test
void contextLoads() {
System.out.println(mapperImpl);
mapperImpl.show();
}

不指定

装配顺序如图:

示例:

1
2
3
4
5
6
7
8
@Resource
private Mapper mapper;

@Test
void contextLoads() {
System.out.println(mapper);
mapper.show();
}

因为没有名称为mapper的Bean,所以会报错。

如果有多个类型,有相同名称的Bean。如增加一个名称为mapperImpl的Bean:

1
2
3
4
5
6
7
@Service("mapperImpl")
public class DaoImpl implements Dao{
@Override
public void show() {
System.out.println("DaoImpl 实现类");
}
}

然后测试:

1
2
3
4
5
6
7
8
@Resource
private Mapper mapperImpl;

@Test
void contextLoads() {
System.out.println(mapperImpl);
mapperImpl.show();
}

一样的或报错,因为通过这个名称找到了多个类型。