Spring容器中的对象(Bean)都有自己的标识符(identifier),多数情况下一个Bean只对应一个标识符,你也可以给Bean指定多个标识符。另外在网上的教程里你可能看到id或者name的概念,其实他们和标识符指的是同一种东西。一个对象如果有多个标识符,还会有一个别名(alias)的概念,它是一种相对的叫法,你挑一个标识符出来,剩下的标识符都叫别名。我通常就把Bean的标识符称为Bean的名字。
对于加了 @Component
注解(包括 @Controller
, @Service
等)的对象,也就是通过组件扫描而加入容器的对象,通常Bean的名字就是类名(首字母小写),如图:
如果你想自定义名字,可以给注解添加参数:
对于在 @Configuration
配置类中通过 @Bean
方法添加的对象来说,Bean的名字就是方法的名字,还可以通过 @Bean
的默认参数自定义名字。
同时 @Bean
还可以给对象定义多个名字,直接传递个String数组个 @Bean
作为默认参数就行。数组中第一个为主名字,其他的都算是它的别名。
给Bean命名貌似是比较简单的一件事情,当容器准备好的时候,容器中的每个对象都有自己的类型,每个对象都有一个或多个名字,一个名字只对应一个对象。
ApplicationContext
提供了很多方法来查找Bean,总的来说就是 通过名字查找 或 通过类型查找 。通过名字查找还是比较简单的,毕竟一个名字只对应了一个对象,要么找到,要么找不到(抛出异常)。
通过类型来查找就比较复杂一点,因为一个对象,必定有着父类,父类也有父类,直到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相关的东西就先放一放,因为如果项目里没有需求的话,学习起来其实挺枯燥的。