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做注册的
我们先来看这个new PackageImports(metadata).getPackageNames().toArray(new String[0])
他是一个什么值、
进入这个PackageImports
类这个类的构造方法
可以看出获取到主启动类的类名、包括一些其他信息
可以将这个方法拆成三块
第一块:就是获取@AutoConfigurationPackage
注解中的属性有没有值
- 什么是属性有没有值
- 如 :
@AutoConfigurationPackage(basePackages={"com.wei"})
第二块:就是通过第一块地方获取出来的值进行遍历添加到packageNames中
第三块:就是判断packageNames是否null、由于在@AutoConfigurationPackage
注解中没有添加属性值所以走的就是第三块
第三块通过debug来看
metadata.getClassName() ---- 的到的值com.wei.springBootHelloWorld
ClassUtils.getPackageName(metadata.getClassName()) -- 得到的值com.wei
红色标记的框是元数据的class的全类名、黄色框住的就是一个工具类通过元数据的class的全类名最后一个.来截取字符串、因此来获取包名、
最后将包名存入packageNames的集合中
执行完PackageImports类
的构造方法后我们回到Registrar
类
执行完构造方法、我们获取的数据就在红的框中、接下来就是执行绿色框中的方法
Registrar
类中的register
方法
register
方法—>bean的注册方法
第一块:他会判断一个常量是否存在、尤于第一次运行肯定是不存在的所以走的就是第二块
第二块:就是将所获取包名的同级包以及子包注入将被扫描(特定注解)的类创建bean加入IOC容器中。
-
特定注解 : 是什么?
@RestController -- 标注了这种会被spring扫描到的注解 public class hello {}
自动扫描包结果类似于:
<context:component-scan base-package="com.wei"/>
小结图片:
自动扫描包的最后做的就是获取启动类的文件位置、将其同包以及子包内的(类似被@Controller标注的)类注入到springioc容器当中