SpringBoot2.x基础篇:带你了解扫描Package自动注册Bean
我们一直在使用SpringBoot
来开发应用程序,但是为什么在项目启动时就会自动注册使用注解@Component
、@Service
、@RestController
…标注的Bean
呢?
推荐阅读
默认扫描目录
SpringBoot
把入口类所在的Package
作为了默认的扫描目录,这也是一个约束,如果我们把需要被注册到IOC
的类创建在扫描目录下就可以实现自动注册,否则则不会被注册。
如果你入口类叫做ExampleApplication
,它位于org.minbox.chapter
目录下,当我们启动应用程序时就会自动扫描org.minbox.chapter
同级目录、子级目录下全部注解的类,如下所示:
1 | . src/main/java |
HelloController.java
、HelloExample.java
与入口类ExampleApplication.java
在同一级目录下,所以在项目启动时可以被扫描到。
IndexController.java
则是位于入口类的下级目录org.minbox.chapter.index
内,因为支持下级目录扫描,所以它也可以被扫描到。
TestController.java
位于com.hengboy
目录下,默认无法扫描到。
自定义扫描目录
在上面目录结构中位于com.hengboy
目录下的TestController.java
类,默认情况下是无法被扫描并注册到IOC
容器内的,如果想要扫描该目录下的类,下面有两种方法。
方法一:使用@ComponentScan注解
1 |
方法二:使用scanBasePackages属性
1 |
注意事项:配置自定义扫描目录后,会覆盖掉默认的扫描目录,如果你还需要扫描默认目录,那么你要进行配置扫描目录,在上面自定义配置中,如果仅配置扫描
com.hengboy
目录,则org.minbox.chapter
目录就不会被扫描。
追踪源码
下面我们来看下SpringBoot
源码是怎么实现自动化扫描目录下的Bean
,并将Bean
注册到容器内的过程。
由于注册的流程比较复杂,挑选出具有代表性的流程步骤来进行讲解。
获取BasePackages
在org.springframework.context.annotation.ComponentScanAnnotationParser#parse
方法内有着获取basePackages
的业务逻辑,源码如下所示:
1 | Set<String> basePackages = new LinkedHashSet<>(); |
获取basePackages
分为了那么三个步骤,分别是:
- 获取
@ComponentScan
注解basePackages
属性值 - 获取
@ComponentScan
注解basePackageClasses
属性值 - 将
Application
入口类所在的package
作为默认的basePackages
注意事项:根据源码也就证实了,为什么我们配置了
basePackages
、basePackageClasses
后会把默认值覆盖掉,这里其实也不算是覆盖,是根本不会去获取Application
入口类的package
。
扫描Packages下的Bean
获取到全部的Packages
后,通过org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
方法来扫描每一个Package
下使用注册注解(@Component
、@Service
、@RestController
…)标注的类,源码如下所示:
1 | protected Set<BeanDefinitionHolder> doScan(String... basePackages) { |
在上面源码中会扫描每一个basePackage
下通过注解定义的Bean
,获取Bean
注册定义对象后并设置一些基本属性。
注册Bean
扫描到basePackage
下的Bean
后会直接通过org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition
方法进行注册,源码如下所示:
1 | public static void registerBeanDefinition( |
通过org.springframework.beans.factory.support.BeanDefinitionRegistry#registerBeanDefinition
注册器内的方法可以直接将Bean
注册到IOC
容器内,而BeanName
则是它生命周期内的唯一名称。
总结
通过本文的讲解我想你应该已经了解了SpringBoot
应用程序启动时为什么会自动扫描package
并将Bean
注册到IOC
容器内,虽然项目启动时间很短暂,不过这是一个非常复杂的过程,在学习过程中大家可以使用Debug
模式来查看每一个步骤的逻辑处理。
SpringBoot2.x基础篇:带你了解扫描Package自动注册Bean
https://blog.minbox.org/spring-boot-basic-default-scan-package.html