2、SpringBoot的默认扫描包机制

个人理解、仅供参考。

2.1、@SpringBootApplication

先从主启动类来说

@SpringBootApplication
public class springBootHelloWorld {
    public static void main(String[] args) {
        SpringApplication.run(springBootHelloWorld.class,args);
    }
}

@SpringBootApplication:在主启动类上标注了一个这样的注解、这个是一个什么注解呢?

  • 标注这个类是springboot的主配置类、

我们点开这个类

@Target(ElementType.TYPE) //表名该注解可以作用的范围,TYPE表示可以作用在类,enum,接口
@Retention(RetentionPolicy.RUNTIME)//表示注解的声明周期,保留在class文件中(三个声明周期
@Documented //表名该注解应该被javadoc记录
@Inherited //表名该注解可以被继承
@SpringBootConfiguration // 继承至spring中的注解、标明此类为配置类
@EnableAutoConfiguration//开启自动配置的功能,这个是核心注解
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
//扫描路径设置,可以通过basePackages属性来指定扫描范围,默认扫描当前类所在包以及子包
public @interface SpringBootApplication {}

在这个注解中其中两个注解我们要注意的

  • @SpringBootConfiguration :从字面意思可以理解为springboot配置类
  • @EnableAutoConfiguration:从字面意思可以理解为启用自动配置类

2.2、@SpringBootConfiguration

打开这个注解可以发现、他有spring中的@Configuration注解、在学习spring中应该学过这个注解、这个 @Configuration 注解的意思就是将一个类标注为配置类、在ssm阶段时、全是使用xml来配置bean的、也可以使用配置类的形式来配置bean、来将其交给spring来管理、在springboot中会出来大量 @Configuration 这个注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {}

由此可以说明 @SpringBootApplication-> @SpringBootConfiguration -> @Configuration

通过这个注解可以得出一个结论:@SpringBootApplication注解标注的主启动类就是一个配置类

2.3、@EnableAutoConfiguration

这个注解就是自动扫描包、和自动装配的核心注解

先说自动扫描包原理

我们先打开这个注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

@EnableAutoConfiguration注解中可以看出有一个@AutoConfigurationPackage这个注解

2.3.1、@AutoConfigurationPackage

@AutoConfigurationPackage:从字面意思可以得出一个、自动装配包的一个意思

@AutoConfigurationPackage注解中可以得出

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {}

他引用了一个内部类@Import(AutoConfigurationPackages.Registrar.class)

现在点进他的内部类看看做了什么

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImports(metadata));
		}

	}

可以看出他实现了两个接口并从写了接口的方法、主要来看这个方法

@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}

registerBeanDefinitions方法: 理解为注册bean的定义、

AnnotationMetadata metadata : 获取注解的元数据、简单来说就是可以通过这个来获取启动类的信息

BeanDefinitionRegistry registry:负责bean做注册的

spring boot 包扫描多个 包 springboot包扫描原理_spring boot 包扫描多个 包

我们先来看这个new PackageImports(metadata).getPackageNames().toArray(new String[0])他是一个什么值、

进入这个PackageImports类这个类的构造方法

spring boot 包扫描多个 包 springboot包扫描原理_spring boot_02

可以看出获取到主启动类的类名、包括一些其他信息

可以将这个方法拆成三块

spring boot 包扫描多个 包 springboot包扫描原理_spring boot_03

第一块:就是获取@AutoConfigurationPackage注解中的属性有没有值

  • 什么是属性有没有值
  • 如 : @AutoConfigurationPackage(basePackages={"com.wei"})

第二块:就是通过第一块地方获取出来的值进行遍历添加到packageNames中

第三块:就是判断packageNames是否null、由于在@AutoConfigurationPackage注解中没有添加属性值所以走的就是第三块

第三块通过debug来看

spring boot 包扫描多个 包 springboot包扫描原理_表名_04

metadata.getClassName() ----  的到的值com.wei.springBootHelloWorld

ClassUtils.getPackageName(metadata.getClassName()) -- 得到的值com.wei

红色标记的框是元数据的class的全类名、黄色框住的就是一个工具类通过元数据的class的全类名最后一个.来截取字符串、因此来获取包名、

最后将包名存入packageNames的集合中

执行完PackageImports类的构造方法后我们回到Registrar

spring boot 包扫描多个 包 springboot包扫描原理_spring boot_05

执行完构造方法、我们获取的数据就在红的框中、接下来就是执行绿色框中的方法

Registrar类中的register方法

register方法—>bean的注册方法

spring boot 包扫描多个 包 springboot包扫描原理_spring boot_06

第一块:他会判断一个常量是否存在、尤于第一次运行肯定是不存在的所以走的就是第二块

第二块:就是将所获取包名的同级包以及子包注入将被扫描(特定注解)的类创建bean加入IOC容器中。

  • 特定注解 : 是什么?
@RestController  -- 标注了这种会被spring扫描到的注解
public class hello {}

自动扫描包结果类似于:

<context:component-scan base-package="com.wei"/>

小结图片:

spring boot 包扫描多个 包 springboot包扫描原理_spring boot 包扫描多个 包_07

自动扫描包的最后做的就是获取启动类的文件位置、将其同包以及子包内的(类似被@Controller标注的)类注入到springioc容器当中