Spring Boot教程(23) – 容器中对象的命名和查找

Spring容器中的对象(Bean)都有自己的标识符(identifier),多数情况下一个Bean只对应一个标识符,你也可以给Bean指定多个标识符。另外在网上的教程里你可能看到id或者name的概念,其实他们和标识符指的是同一种东西。一个对象如果有多个标识符,还会有一个别名(alias)的概念,它是一种相对的叫法,你挑一个标识符出来,剩下的标识符都叫别名。我通常就把Bean的标识符称为Bean的名字。

Bean的命名

对于加了@Component注解(包括@Controller@Service等)的对象,也就是通过组件扫描而加入容器的对象,通常Bean的名字就是类名(首字母小写),如图:

如果你想自定义名字,可以给注解添加参数:

对于在@Configuration配置类中通过@Bean方法添加的对象来说,Bean的名字就是方法的名字,还可以通过@Bean的默认参数自定义名字。

同时@Bean还可以给对象定义多个名字,直接传递个String数组个@Bean作为默认参数就行。数组中第一个为主名字,其他的都算是它的别名。

给Bean命名是比较简单的一件事情,当容器准备好的时候,容器中的每个对象都有自己的类型,每个对象都有一个或多个名字,一个名字只对应一个对象。

Bean的查找

ApplicationContext提供了很多方法来查找Bean,总的来说就是通过名字查找通过类型查找。通过名字查找还是比较简单的,毕竟一个名字只对应了一个对象,要么找到,要么找不到(抛出异常)。

object和mc是同一个对象

通过类型来查找就比较复杂一点,因为一个对象,必定有着父类,父类也有父类,直到Object,用继承链上任何一个类型都可以找到对象。更不用说继承链上的类很可能实现了一个或者多个接口。

从上图中可以看出,在我写的这个演示程序中,Spring Boot已经自动帮我在容器中扔了300多个对象,其中有163个实现了Aware接口,真多。。

消除歧义

99%的情况下,你不会去直接使用ApplicationContext,而是通过@Autowired引入依赖,你通常会把这个注解加在成员变量、构造方法或者setter方法之上。@Autowired默认是按照类型在容器中查找对象的:

最常见的使用方式

但是呢,有的时候同类型可能会有多个对象,这个时候选哪个好呢?有很多种方法可以做到。

@Primary注解帮助你选择一个“主要”的对象。在使用@Bean创建Bean的时候,可以添加一个@Primary注解,以告诉框架,这个对象是首选的。这样上图代码在装配的时候,会选择这个首选的OrderService。

@Qualifier注解提供了更细致的控制方式,他的字面意思是“限定符”,我刚开始学习的就搞不懂限定符的意义是啥。后来逐渐明白,限定符就跟标签一样,你在写博客的时候经常会给文章添加标签,一篇文章可能有多个标签,一个标签也能对应多篇文章。限定符也是一样的,一个对象可能有多个限定符,一个限定符可能对应多个对象。你需要分别在对象创建的时候和装配的时候指定限定符:

添加限定符
根据限定符装配

事实上,容器中每个对象,都有个默认的限定符,那就是对象的名字。所以,当你直接指定名字的时候,就不会有歧义了,因为一个名字只对应一个对象。

如果你在项目里经常通过@Autowired + @Qualifier的方式用名字来查找对象,那么你的使用方式,其实和@Qualifier的语义有点相悖。更好的选择是使用JSR-250提供的@Resource注解,你可以直接通过名字来装配对象。

我更倾向于避免在项目中用名字来查找对象,因为如果你进行类的重构或者方法的重构之后,会导致对象的名字被更改,导致装配的时候可能找不到对象。

说点其他的

目前Spring Boot教程已经写了23篇,关于Spring核心的一些东西,比如IoC容器以及AOP都介绍了不少,目前写出来的知识,日常开发至少够用个三年吧。

你要是问我还有没有可以继续深入挖掘的东西,我说有。但是实在是不想写了,一是看的人少,二是我暂时用不到。继续深入的确可以使我对Spring的了解更细致,但是这非常容易踩入一个陷阱,就是耗费大量时间钻牛角尖,而不是做其他更有价值的事情上,比如用已有的技术去做点小产品。搁以前,我很可能就找一本源码分析相关的书,一头扎进去了,期待看完之后,能成为大牛,而现在的我知道,这么做并不能如愿,大牛都是通过业务熟悉框架熟悉源码的,而不是看书看文档。

接下来我规划写一点数据库相关的东西,JDBC,JPA(Hiberante)等内容,然后是一些运维相关的东西,再然后会根据场景写如何实现某某功能等等。Spring Cloud相关的计划就先放一放,因为如果项目里没有需求的话,学习起来无趣,且枯燥。

发表评论

电子邮件地址不会被公开。