本篇主要介绍Spring Boot 自动装配的相关内容。
目录
一、什么是自动装配
二、Bean的扫描方式
@ComponentScan
@Import
ImportSelector接口
三、Spring Boot自动装配原理
一、什么是自动装配
在我们在创建Spring Boot项目时往往会根据项目需求,引入很多第三方依赖,在这些依赖中,往往会提供一些类来供我们调用这些依赖,例如Jackson的ObjectMapper类,如果我们要使用这个类只需要引入依赖,然后直接通过注入的方式就能使用。在这个过程中,我们并没有去往容器中存这些Bean,那我们为什么还能够进行注入呢,其实是Spring Boot发挥了作用,它会自动去扫描到这些类,并将其装配到我们的Spring容器中,而这个装配的过程就被称为Spring Boot 的自动装配。
二、Bean的扫描方式
前面我们说了,Spring boot先要去扫描到Bean才能将其装配,那我们要如何才能扫描到Bean呢?下面我们就来介绍一下Bean的两种扫描方式。
@ComponentScan
通过@ComponentScan注解我们可以来指定Spring去哪个包下去扫描Bean,例如,我们想去com.example.config这个包中扫描,只需在会被装配到容器中的类(在配置的扫描的包中有五大类注解标识的类)或启动类上加上这个注解,并在注解中指定即可,具体如下:
我们还可以指定多个包进行扫描,具体如下:
但在一个项目中通常会引入很多依赖,这也就意味着如果我们使用@ComponentScan注解需要指定很多个包来进行扫描,这样不仅会使代码看起来非常繁多和冗余,还会大大加大扫描Bean所需要的时间,还有可能出现启动项目需要几分钟的情况。 下面,我们来介绍另一种扫描Bean的方式。
@Import
@Import注解可以指定要去扫描哪个类,例如我们需要去扫描TestConfig类,只需在启动类或者会被装配到容器中的类(扫描包中五大类注解标识的类或者@Import注解中指定的类)上加上此注解并指定即可,spring就会自动把指定的这个类及其内部的由通过@Bean注解标识的方法所返回的Bean一并加载到Spring容器中,具体代码如下:
但这种方式如果需要扫描多个类的话同样会显得十分繁琐,但与@ComponentScan注解不同的是,使用@Import注解有解决方案,那就是ImportSelector接口。
ImportSelector接口
如果我们传入到@Import注解中的类是ImportSelector接口的实现类,那么该实现类所重写的方法selectImports所返回的String数组将会被加载到@Import注解中,并去扫描数组中对应名称的类。下面我们来创建一个实现类,具体如下, 需要注意的是返回的String数组所包含的是需要扫描的类的全限定包名。
然后我们再将这个实现类写到@Import注解中:
这样当我们在启动项目时,实现类所返回的两个路径的类就会被spring扫描到并加载到容器中了。但要注意的是中这个实现类并不会装配到容器中。
我们将这两个类进行一下注入,代码如下,
启动项目后并未出现问题,这也就意味着前面的两个类都已被正常扫描到并装配了。
这种方式依然还是存在问题,使用这种方式时我们需要去知道所有需要装配的类的名称,这显然是不太可能的,那有没有一种更好的办法呢,其实是有的,我们只需要让第三方依赖给我们提供一个注解,在这个注解中对@Import注解进行封装,并由第三方依赖自行在@Import注解中指定要进行装配的Bean。这样我们就只需要使用第三方依赖提供的这个注解就能去扫描并装配第三方依赖需要装配的Bean了。Spring Boot的自动装配也确实是采取了类似的方式,下面我们来详细了解一下。
三、Spring Boot自动装配原理
Spring Boot自动装配主要与@SpringBootApplication注解有关,该注解的实现如下:
可以发现在这个注解中对@ComponentScan进行了封装 里面的内容大体是指定了当前类所在的包路径为扫描路径,这也就意味着如果我们不自己指定扫描的包的话,会默认以启动类所在包为扫描的包。
然后我们还可以看到一个@SpringBootConfiguration注解,我们进到其实现来看一下
可以看到这个注解的实现非常简单,大体就是将@Configuration注解进行了封装,标识当前启动类为一个配置类,并将其注入到容器中,并且还引入了@Indexed,这个注解主要是让标识的类被更快的扫描到。
然后我们在来看一下@EnableAutoConfiguration注解,这个注解是Spirng Boot自动装配的核心,让我们来具体看一下。
可以发现在这个注解中对@Import注解进行了封装,并且在注解中指定了一个类,根据名称可以退出这大概是一个ImportSelector接口的实现类。我们进到这个类中看一下,可以发现selectImports方法
在方法中可以看到调用了一个getAutoConfigurationEntry方法,这个方法主要用来获取自动装配的配置类的信息(也就是需要被Import指定扫描的类的信息),我们进到里面来看一下,
可以发现这个方法中通过getCandidateConfigurations方法获取了一个字符串List,这个List就是需要装配的类的配置类的信息,我们再进到getConfigurations方法来看一下,
在这个方法中,主要是去读取META-INF/spring. factories和META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.这两个文件中所包含的配置类的信息。
打开这两个文件可以看到依赖所需的所有配置类的全限定包名
然后将读取到的这两个文件的信息经过层层处理后再由前面的selectImports返回给@Imports注解,这样在项目启动时就会去扫描这些相关的配置类,并把其中所包含的由@Bean标注的方法所返回的Bean装配到spring容器中。但这些配置类并不会全部生效,我们进到其中一个配置类来看一下,可以发现在这个配置类上有一个@ConditionalOnClass注解
这个注解的作用是进行一些条件判定,只有符合条件,该配置类对Bean的配置才会生效
在@EnableAutoConfiguration中还有一个@AutoConfiurationPackage注解这个注解主要是导入AutoConfigurationPackges.register.class这个配置文件,并将启动类所在包中的第三方组件注册到Spring容器中(例如在使用Mybatis时所用到的@Mapper注解标识的类)。
最后总结一下以上述流程,大体如下:
这里介绍的只是Spring Boot自动装配的一个基本流程,完整的流程要远远比这里复杂,如果大家感兴趣的话,可以自己去阅读源码来深入研究。