J2EE Spring @Resource注解的扫描顺序

  |  
阅读次数
  |  
字数 836
  |  
时长 ≈ 3 分钟

让我们先来看看两段代码:

一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class FooService {}

@Service
public class BarService {}

@Controller
@RequestMapping(value = "/foo")
public class FooController {
@Resource
private FooService service;
}

@Controller
@RequestMapping(value = "/bar")
public class BarController {
@Resource
private BarService service;
}

二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class FooService {}

@Service
public class BarService {}

@Controller
@RequestMapping(value = "/foo")
public class FooController {
@Resource
private FooService barService;
}

@Controller
@RequestMapping(value = "/bar")
public class BarController {
@Resource
private BarService barService;
}

这两段代码里面分别都初始化了FooServiceBarService两个bean,然后分别都在其调用层被调用,而调用层注入的时候所使用的名字也都是相同的。
乍一看这样并没什么蹊跷,好像没什么毛病,但是实际上,第一段代码是可以成功运行的,而第二段却会注入失败。

我们注意到,两端代码几乎一模一样,而且调用层使用的变量命名虽然在两个调用层而言是一样的,但是在两端代码而言却是有所不同。
这里第二段代码会注入失败的原因,重点就在于FooController里注入的FooService变量命名barService

这又是为什么呢?

让我们先来看下Resource注解的扫描步骤。

1
2
3
4
5
6
7
8
9
如果@Resource的注解name属性为""
则把@Resource所在的属性的名称和Spring容器中的id作匹配
如果匹配成功,则赋值
如果匹配不成功,则会按照类型进行配置
如果匹配成功,则赋值;匹配不成功,报错
如果@Resource的注解的name的值不为""
则解析@Resource注解name属性的值,把值和spring容器中的ID进行呢匹配
如果匹配成功,则赋值
如果匹配不成功,则报错

在这里我们通过这个规则来分别分析一下两段代码。
第一段代码:
我们在FooController里面注入了BarService类型的变量service
由于我们的@Resource注解没有填写name属性,所以按照规则,@Resource会先使用属性的名称进行匹配,
这里因为属性名是service所以没找到nameservice的bean,所以根据类型继续匹配,最后找到了名为fooService的bean进行注入。

同理BarController的注入也是这样。
所以在这里我们的代码运行正确,并没有发现任何问题。

第二段代码:
我们在FooController里面注入了BarService类型的变量barService
由于我们的@Resource注解没有填写name属性,所以按照规则,@Resource会先使用属性的名称进行匹配,
这里因为属性名是barService,而我们的内存里面就已经存在着一个初始化好的namebarServiceBarService
所以这里@Resource拿到的是BarService,然后我们声明的变量类型为FooService,与拿到的不一致,所以导致注入失败。
而在BarController里,我们@Resource拿到的bean与声明的变量一致,所以注入成功。

这就是第二段代码为什么会出现错误的问题了,
所以这也意味,如果我们的实现只有一个的话使用@Autowired会更加方便。
在使用@Resource的情况下,如果属性名和nameid都没具备匹配的条件下,最后也会使用类型匹配,所以最后这步与@Autowired是同样的。